From d31084e9d1118b25fd16580d9d8c2924b5740dff Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Tue, 9 Jul 1996 06:22:35 +0000 Subject: [PATCH] Postgres95 1.01 Distribution - Virgin Sources --- src/Makefile | 48 + src/Makefile.global | 306 + src/backend/Makefile | 289 + src/backend/access/Makefile.inc | 35 + src/backend/access/attnum.h | 61 + src/backend/access/common/Makefile.inc | 16 + src/backend/access/common/heaptuple.c | 1011 ++ src/backend/access/common/heapvalid.c | 134 + src/backend/access/common/indextuple.c | 427 + src/backend/access/common/indexvalid.c | 84 + src/backend/access/common/printtup.c | 306 + src/backend/access/common/scankey.c | 68 + src/backend/access/common/tupdesc.c | 398 + src/backend/access/funcindex.h | 43 + src/backend/access/genam.h | 60 + src/backend/access/hash.h | 336 + src/backend/access/hash/Makefile.inc | 18 + src/backend/access/hash/hash.c | 467 + src/backend/access/hash/hashfunc.c | 276 + src/backend/access/hash/hashinsert.c | 239 + src/backend/access/hash/hashovfl.c | 614 + src/backend/access/hash/hashpage.c | 669 ++ src/backend/access/hash/hashscan.c | 172 + src/backend/access/hash/hashsearch.c | 425 + src/backend/access/hash/hashstrat.c | 104 + src/backend/access/hash/hashutil.c | 147 + src/backend/access/heap/Makefile.inc | 14 + src/backend/access/heap/heapam.c | 1507 +++ src/backend/access/heap/hio.c | 195 + src/backend/access/heap/stats.c | 329 + src/backend/access/heapam.h | 149 + src/backend/access/hio.h | 26 + src/backend/access/htup.h | 115 + src/backend/access/ibit.h | 34 + src/backend/access/index/Makefile.inc | 14 + src/backend/access/index/genam.c | 275 + src/backend/access/index/indexam.c | 411 + src/backend/access/index/istrat.c | 679 ++ src/backend/access/iqual.h | 32 + src/backend/access/istrat.h | 80 + src/backend/access/itup.h | 104 + src/backend/access/nbtree.h | 264 + src/backend/access/nbtree/Makefile.inc | 15 + src/backend/access/nbtree/README | 68 + src/backend/access/nbtree/nbtcompare.c | 173 + src/backend/access/nbtree/nbtinsert.c | 831 ++ src/backend/access/nbtree/nbtpage.c | 523 + src/backend/access/nbtree/nbtree.c | 516 + src/backend/access/nbtree/nbtscan.c | 164 + src/backend/access/nbtree/nbtsearch.c | 1133 ++ src/backend/access/nbtree/nbtsort.c | 1196 ++ src/backend/access/nbtree/nbtstrat.c | 134 + src/backend/access/nbtree/nbtutils.c | 239 + src/backend/access/printtup.h | 26 + src/backend/access/relscan.h | 87 + src/backend/access/rtree.h | 98 + src/backend/access/rtree/Makefile.inc | 14 + src/backend/access/rtree/rtget.c | 320 + src/backend/access/rtree/rtproc.c | 150 + src/backend/access/rtree/rtree.c | 955 ++ src/backend/access/rtree/rtscan.c | 392 + src/backend/access/rtree/rtstrat.c | 239 + src/backend/access/rtscan.h | 17 + src/backend/access/rtstrat.h | 18 + src/backend/access/sdir.h | 57 + src/backend/access/skey.h | 52 + src/backend/access/strat.h | 86 + src/backend/access/transam.h | 213 + src/backend/access/transam/Makefile.inc | 14 + src/backend/access/transam/transam.c | 675 ++ src/backend/access/transam/transsup.c | 663 + src/backend/access/transam/varsup.c | 606 + src/backend/access/transam/xact.c | 1314 ++ src/backend/access/transam/xid.c | 156 + src/backend/access/tupdesc.h | 53 + src/backend/access/tupmacs.h | 43 + src/backend/access/valid.h | 37 + src/backend/access/xact.h | 115 + src/backend/bootstrap/Makefile.inc | 63 + src/backend/bootstrap/boot.sed | 9 + src/backend/bootstrap/bootparse.y | 293 + src/backend/bootstrap/bootscanner.l | 108 + src/backend/bootstrap/bootstrap.c | 1049 ++ src/backend/bootstrap/bootstrap.h | 78 + src/backend/catalog/Makefile.inc | 69 + src/backend/catalog/README | 66 + src/backend/catalog/catalog.c | 205 + src/backend/catalog/catalog.h | 24 + src/backend/catalog/catname.h | 52 + src/backend/catalog/genbki.sh | 218 + src/backend/catalog/heap.c | 1428 +++ src/backend/catalog/heap.h | 42 + src/backend/catalog/index.c | 1655 +++ src/backend/catalog/index.h | 59 + src/backend/catalog/indexing.c | 561 + src/backend/catalog/indexing.h | 103 + src/backend/catalog/pg_aggregate.c | 325 + src/backend/catalog/pg_aggregate.h | 132 + src/backend/catalog/pg_am.h | 115 + src/backend/catalog/pg_amop.h | 546 + src/backend/catalog/pg_amproc.h | 134 + src/backend/catalog/pg_attribute.h | 512 + src/backend/catalog/pg_class.h | 162 + src/backend/catalog/pg_database.h | 57 + src/backend/catalog/pg_defaults.h | 55 + src/backend/catalog/pg_demon.h | 58 + src/backend/catalog/pg_group.h | 42 + src/backend/catalog/pg_hosts.h | 44 + src/backend/catalog/pg_index.h | 71 + src/backend/catalog/pg_inheritproc.h | 59 + src/backend/catalog/pg_inherits.h | 57 + src/backend/catalog/pg_ipl.h | 57 + src/backend/catalog/pg_language.h | 75 + src/backend/catalog/pg_listener.h | 56 + src/backend/catalog/pg_log.h | 40 + src/backend/catalog/pg_magic.h | 54 + src/backend/catalog/pg_opclass.h | 85 + src/backend/catalog/pg_operator.c | 1077 ++ src/backend/catalog/pg_operator.h | 480 + src/backend/catalog/pg_parg.h | 116 + src/backend/catalog/pg_proc.c | 265 + src/backend/catalog/pg_proc.h | 769 ++ src/backend/catalog/pg_rewrite.h | 64 + src/backend/catalog/pg_server.h | 56 + src/backend/catalog/pg_statistic.h | 60 + src/backend/catalog/pg_time.h | 41 + src/backend/catalog/pg_type.c | 595 + src/backend/catalog/pg_type.h | 267 + src/backend/catalog/pg_user.h | 99 + src/backend/catalog/pg_variable.h | 40 + src/backend/catalog/pg_version.h | 58 + src/backend/catalog/unused_oids | 41 + src/backend/commands/Makefile.inc | 25 + src/backend/commands/_deadcode/version.c | 336 + src/backend/commands/async.c | 605 + src/backend/commands/async.h | 33 + src/backend/commands/cluster.c | 370 + src/backend/commands/cluster.h | 30 + src/backend/commands/command.c | 511 + src/backend/commands/command.h | 56 + src/backend/commands/copy.c | 782 ++ src/backend/commands/copy.h | 21 + src/backend/commands/creatinh.c | 564 + src/backend/commands/creatinh.h | 20 + src/backend/commands/defind.c | 505 + src/backend/commands/define.c | 564 + src/backend/commands/defrem.h | 53 + src/backend/commands/explain.c | 219 + src/backend/commands/explain.h | 17 + src/backend/commands/purge.c | 168 + src/backend/commands/purge.h | 20 + src/backend/commands/recipe.c | 1181 ++ src/backend/commands/recipe.h | 17 + src/backend/commands/remove.c | 435 + src/backend/commands/rename.c | 275 + src/backend/commands/rename.h | 24 + src/backend/commands/vacuum.c | 853 ++ src/backend/commands/vacuum.h | 48 + src/backend/commands/version.h | 26 + src/backend/commands/view.c | 325 + src/backend/commands/view.h | 20 + src/backend/executor/Makefile.inc | 29 + src/backend/executor/execAmi.c | 439 + src/backend/executor/execFlatten.c | 236 + src/backend/executor/execFlatten.h | 26 + src/backend/executor/execJunk.c | 389 + src/backend/executor/execMain.c | 1023 ++ src/backend/executor/execProcnode.c | 477 + src/backend/executor/execQual.c | 1504 +++ src/backend/executor/execScan.c | 136 + src/backend/executor/execTuples.c | 1013 ++ src/backend/executor/execUtils.c | 1092 ++ src/backend/executor/execdebug.h | 377 + src/backend/executor/execdefs.h | 54 + src/backend/executor/execdesc.h | 38 + src/backend/executor/executor.h | 229 + src/backend/executor/functions.c | 388 + src/backend/executor/functions.h | 22 + src/backend/executor/hashjoin.h | 82 + src/backend/executor/nodeAgg.c | 558 + src/backend/executor/nodeAgg.h | 21 + src/backend/executor/nodeAppend.c | 483 + src/backend/executor/nodeAppend.h | 22 + src/backend/executor/nodeGroup.c | 407 + src/backend/executor/nodeGroup.h | 21 + src/backend/executor/nodeHash.c | 828 ++ src/backend/executor/nodeHash.h | 35 + src/backend/executor/nodeHashjoin.c | 792 ++ src/backend/executor/nodeHashjoin.h | 33 + src/backend/executor/nodeIndexscan.c | 902 ++ src/backend/executor/nodeIndexscan.h | 32 + src/backend/executor/nodeMaterial.c | 392 + src/backend/executor/nodeMaterial.h | 23 + src/backend/executor/nodeMergejoin.c | 1194 ++ src/backend/executor/nodeMergejoin.h | 40 + src/backend/executor/nodeNestloop.c | 370 + src/backend/executor/nodeNestloop.h | 21 + src/backend/executor/nodeResult.c | 288 + src/backend/executor/nodeResult.h | 21 + src/backend/executor/nodeSeqscan.c | 449 + src/backend/executor/nodeSeqscan.h | 27 + src/backend/executor/nodeSort.c | 523 + src/backend/executor/nodeSort.h | 23 + src/backend/executor/nodeTee.c | 503 + src/backend/executor/nodeTee.h | 22 + src/backend/executor/nodeUnique.c | 316 + src/backend/executor/nodeUnique.h | 21 + src/backend/executor/tuptable.h | 72 + src/backend/include/Makefile.inc | 16 + src/backend/include/c.h | 768 ++ src/backend/include/miscadmin.h | 193 + src/backend/include/postgres.h | 224 + src/backend/lib/Makefile.inc | 20 + src/backend/lib/bit.c | 45 + src/backend/lib/dllist.c | 204 + src/backend/lib/dllist.h | 72 + src/backend/lib/fstack.c | 153 + src/backend/lib/fstack.h | 113 + src/backend/lib/hasht.c | 47 + src/backend/lib/hasht.h | 23 + src/backend/lib/lispsort.c | 56 + src/backend/lib/lispsort.h | 18 + src/backend/lib/qsort.c | 281 + src/backend/lib/qsort.h | 24 + src/backend/lib/stringinfo.c | 116 + src/backend/lib/stringinfo.h | 47 + src/backend/libpq/Makefile.inc | 26 + src/backend/libpq/auth.c | 668 ++ src/backend/libpq/auth.h | 49 + src/backend/libpq/be-dumpdata.c | 323 + src/backend/libpq/be-fsstubs.c | 351 + src/backend/libpq/be-fsstubs.h | 32 + src/backend/libpq/be-pqexec.c | 382 + src/backend/libpq/libpq-be.h | 51 + src/backend/libpq/libpq-fs.h | 119 + src/backend/libpq/libpq.h | 261 + src/backend/libpq/portal.c | 783 ++ src/backend/libpq/portalbuf.c | 511 + src/backend/libpq/pqcomm.c | 724 ++ src/backend/libpq/pqcomm.h | 124 + src/backend/libpq/pqpacket.c | 283 + src/backend/libpq/pqsignal.c | 40 + src/backend/libpq/pqsignal.h | 32 + src/backend/main/Makefile.inc | 16 + src/backend/main/main.c | 45 + src/backend/makeID | 17 + src/backend/nodes/Makefile.inc | 33 + src/backend/nodes/README | 65 + src/backend/nodes/copyfuncs.c | 1675 +++ src/backend/nodes/equalfuncs.c | 703 ++ src/backend/nodes/execnodes.h | 689 ++ src/backend/nodes/list.c | 438 + src/backend/nodes/makefuncs.c | 117 + src/backend/nodes/makefuncs.h | 48 + src/backend/nodes/memnodes.h | 101 + src/backend/nodes/nodeFuncs.c | 116 + src/backend/nodes/nodeFuncs.h | 23 + src/backend/nodes/nodes.c | 45 + src/backend/nodes/nodes.h | 299 + src/backend/nodes/outfuncs.c | 1670 +++ src/backend/nodes/params.h | 90 + src/backend/nodes/parsenodes.h | 731 ++ src/backend/nodes/pg_list.h | 112 + src/backend/nodes/plannodes.h | 330 + src/backend/nodes/primnodes.h | 318 + src/backend/nodes/print.c | 377 + src/backend/nodes/read.c | 270 + src/backend/nodes/readfuncs.c | 1948 +++ src/backend/nodes/readfuncs.h | 27 + src/backend/nodes/relation.h | 279 + src/backend/optimizer/Makefile.inc | 29 + src/backend/optimizer/clauseinfo.h | 24 + src/backend/optimizer/clauses.h | 54 + src/backend/optimizer/cost.h | 59 + src/backend/optimizer/internal.h | 92 + src/backend/optimizer/joininfo.h | 20 + src/backend/optimizer/keys.h | 22 + src/backend/optimizer/ordering.h | 24 + src/backend/optimizer/path/Makefile.inc | 21 + src/backend/optimizer/path/allpaths.c | 351 + src/backend/optimizer/path/clausesel.c | 331 + src/backend/optimizer/path/costsize.c | 456 + src/backend/optimizer/path/hashutils.c | 120 + src/backend/optimizer/path/indxpath.c | 1206 ++ src/backend/optimizer/path/joinpath.c | 623 + src/backend/optimizer/path/joinrels.c | 528 + src/backend/optimizer/path/joinutils.c | 432 + src/backend/optimizer/path/mergeutils.c | 122 + src/backend/optimizer/path/orindxpath.c | 271 + src/backend/optimizer/path/predmig.c | 773 ++ src/backend/optimizer/path/prune.c | 203 + src/backend/optimizer/path/xfunc.c | 1360 +++ src/backend/optimizer/pathnode.h | 50 + src/backend/optimizer/paths.h | 89 + src/backend/optimizer/plan/Makefile.inc | 15 + src/backend/optimizer/plan/createplan.c | 1097 ++ src/backend/optimizer/plan/initsplan.c | 391 + src/backend/optimizer/plan/planmain.c | 422 + src/backend/optimizer/plan/planner.c | 408 + src/backend/optimizer/plan/setrefs.c | 706 ++ src/backend/optimizer/plancat.h | 65 + src/backend/optimizer/planmain.h | 60 + src/backend/optimizer/planner.h | 24 + src/backend/optimizer/prep.h | 51 + src/backend/optimizer/prep/Makefile.inc | 14 + src/backend/optimizer/prep/archive.c | 66 + src/backend/optimizer/prep/prepqual.c | 582 + src/backend/optimizer/prep/preptlist.c | 322 + src/backend/optimizer/prep/prepunion.c | 400 + src/backend/optimizer/tlist.h | 36 + src/backend/optimizer/util/Makefile.inc | 15 + src/backend/optimizer/util/clauseinfo.c | 187 + src/backend/optimizer/util/clauses.c | 736 ++ src/backend/optimizer/util/indexnode.c | 92 + src/backend/optimizer/util/internal.c | 61 + src/backend/optimizer/util/joininfo.c | 107 + src/backend/optimizer/util/keys.c | 193 + src/backend/optimizer/util/ordering.c | 117 + src/backend/optimizer/util/pathnode.c | 566 + src/backend/optimizer/util/plancat.c | 582 + src/backend/optimizer/util/relnode.c | 123 + src/backend/optimizer/util/tlist.c | 577 + src/backend/optimizer/util/var.c | 189 + src/backend/optimizer/var.h | 21 + src/backend/optimizer/xfunc.h | 84 + src/backend/parser/Makefile.inc | 46 + src/backend/parser/analyze.c | 2467 ++++ src/backend/parser/catalog_utils.c | 1470 +++ src/backend/parser/catalog_utils.h | 64 + src/backend/parser/dbcommands.c | 259 + src/backend/parser/dbcommands.h | 28 + src/backend/parser/gram.y | 2113 ++++ src/backend/parser/keywords.c | 179 + src/backend/parser/keywords.h | 25 + src/backend/parser/parse_query.c | 653 + src/backend/parser/parse_query.h | 72 + src/backend/parser/parse_state.h | 27 + src/backend/parser/parser.c | 449 + src/backend/parser/parsetree.h | 80 + src/backend/parser/scan.l | 255 + src/backend/parser/scansup.c | 148 + src/backend/parser/scansup.h | 17 + src/backend/port/BSD44_derived/Makefile.inc | 28 + src/backend/port/BSD44_derived/README | 4 + src/backend/port/BSD44_derived/dl.c | 88 + src/backend/port/BSD44_derived/float.h | 30 + src/backend/port/BSD44_derived/machine.h | 19 + src/backend/port/BSD44_derived/port-protos.h | 41 + src/backend/port/Makefile.inc | 21 + src/backend/port/aix/Makefile.inc | 40 + src/backend/port/aix/README.dlfcn | 167 + src/backend/port/aix/dlfcn.c | 528 + src/backend/port/aix/dlfcn.h | 46 + src/backend/port/aix/machine.h | 19 + src/backend/port/aix/mkldexport.sh | 42 + src/backend/port/aix/port-protos.h | 25 + src/backend/port/alpha/Makefile.inc | 27 + src/backend/port/alpha/machine.h | 19 + src/backend/port/alpha/port-protos.h | 39 + src/backend/port/alpha/port.c | 34 + src/backend/port/bsdi/Makefile.inc | 15 + src/backend/port/bsdi/dynloader.c | 93 + src/backend/port/bsdi/machine.h | 18 + src/backend/port/bsdi/port-protos.h | 33 + src/backend/port/bsdi/port.c | 13 + src/backend/port/hpux/Makefile.inc | 68 + src/backend/port/hpux/dynloader.c | 57 + src/backend/port/hpux/fixade.h | 63 + src/backend/port/hpux/machine.h | 18 + src/backend/port/hpux/port-protos.h | 34 + src/backend/port/hpux/port.c | 47 + src/backend/port/hpux/tas.c.template | 36 + src/backend/port/hpux/tas.s | 28 + src/backend/port/irix5/Makefile.inc | 20 + src/backend/port/irix5/README | 2 + src/backend/port/irix5/machine.h | 19 + src/backend/port/irix5/port-protos.h | 36 + src/backend/port/irix5/port.c | 16 + src/backend/port/linux/Makefile.inc | 36 + src/backend/port/linux/dynloader.c | 93 + src/backend/port/linux/machine.h | 18 + src/backend/port/linux/port-protos.h | 37 + src/backend/port/linux/port.c | 13 + src/backend/port/sparc/Makefile.inc | 23 + src/backend/port/sparc/float.h | 30 + src/backend/port/sparc/machine.h | 19 + src/backend/port/sparc/port-protos.h | 34 + src/backend/port/sparc/strtol.c | 130 + src/backend/port/sparc_solaris/Makefile.inc | 20 + src/backend/port/sparc_solaris/machine.h | 19 + src/backend/port/sparc_solaris/port-protos.h | 38 + src/backend/port/sparc_solaris/port.c | 66 + src/backend/port/sparc_solaris/rusagestub.h | 30 + src/backend/port/sparc_solaris/tas.s | 50 + src/backend/port/ultrix4/Makefile.inc | 27 + src/backend/port/ultrix4/dl.h | 117 + src/backend/port/ultrix4/dynloader.c | 68 + src/backend/port/ultrix4/machine.h | 19 + src/backend/port/ultrix4/port-protos.h | 36 + src/backend/port/ultrix4/port.c | 25 + src/backend/port/ultrix4/strdup.c | 23 + src/backend/port/win32/machine.h | 2 + src/backend/port/win32/nt.c | 625 + src/backend/port/win32/nt.h | 54 + src/backend/port/win32/pglite.mak | 3323 +++++ src/backend/port/win32/port-protos.h | 1 + src/backend/port/win32/pwd.h | 1 + src/backend/port/win32/regex/COPYRIGHT | 56 + src/backend/port/win32/regex/Makefile.inc | 14 + src/backend/port/win32/regex/WHATSNEW | 94 + src/backend/port/win32/regex/cclass.h | 70 + src/backend/port/win32/regex/cname.h | 141 + src/backend/port/win32/regex/engine.c | 1091 ++ src/backend/port/win32/regex/re_format.7 | 269 + src/backend/port/win32/regex/regcomp.c | 1698 +++ src/backend/port/win32/regex/regerror.c | 180 + src/backend/port/win32/regex/regex.3 | 538 + src/backend/port/win32/regex/regex.h | 106 + src/backend/port/win32/regex/regex2.h | 175 + src/backend/port/win32/regex/regexec.c | 181 + src/backend/port/win32/regex/regexp.h | 69 + src/backend/port/win32/regex/regfree.c | 80 + src/backend/port/win32/regex/utils.h | 57 + src/backend/port/win32/rusagestub.h | 29 + src/backend/port/win32/sys/cdefs.h | 124 + src/backend/port/win32/sys/file.h | 1 + src/backend/port/win32/sys/ipc.h | 1 + src/backend/port/win32/sys/param.h | 1 + src/backend/port/win32/sys/sem.h | 1 + src/backend/port/win32/sys/shm.h | 1 + src/backend/port/win32/sys/time.h | 1 + src/backend/postmaster/Makefile.inc | 16 + src/backend/postmaster/postmaster.c | 1122 ++ src/backend/regex/COPYRIGHT | 56 + src/backend/regex/Makefile.inc | 14 + src/backend/regex/WHATSNEW | 94 + src/backend/regex/cclass.h | 70 + src/backend/regex/cdefs.h | 144 + src/backend/regex/cname.h | 141 + src/backend/regex/engine.c | 1092 ++ src/backend/regex/re_format.7 | 269 + src/backend/regex/regcomp.c | 1701 +++ src/backend/regex/regerror.c | 178 + src/backend/regex/regex.3 | 538 + src/backend/regex/regex.h | 108 + src/backend/regex/regex2.h | 175 + src/backend/regex/regexec.c | 181 + src/backend/regex/regexp.h | 71 + src/backend/regex/regfree.c | 80 + src/backend/regex/utils.h | 57 + src/backend/rewrite/Makefile.inc | 22 + src/backend/rewrite/locks.c | 131 + src/backend/rewrite/locks.h | 21 + src/backend/rewrite/prs2lock.h | 43 + src/backend/rewrite/rewriteDefine.c | 255 + src/backend/rewrite/rewriteDefine.h | 18 + src/backend/rewrite/rewriteHandler.c | 622 + src/backend/rewrite/rewriteHandler.h | 35 + src/backend/rewrite/rewriteManip.c | 435 + src/backend/rewrite/rewriteManip.h | 31 + src/backend/rewrite/rewriteRemove.c | 181 + src/backend/rewrite/rewriteRemove.h | 20 + src/backend/rewrite/rewriteSupport.c | 270 + src/backend/rewrite/rewriteSupport.h | 27 + src/backend/storage/Makefile.inc | 31 + src/backend/storage/backendid.h | 32 + src/backend/storage/block.h | 114 + src/backend/storage/buf.h | 47 + src/backend/storage/buf_internals.h | 220 + src/backend/storage/buffer/Makefile.inc | 16 + src/backend/storage/buffer/buf_init.c | 280 + src/backend/storage/buffer/buf_table.c | 162 + src/backend/storage/buffer/bufmgr.c | 1581 +++ src/backend/storage/buffer/freelist.c | 285 + src/backend/storage/buffer/localbuf.c | 284 + src/backend/storage/bufmgr.h | 112 + src/backend/storage/bufpage.h | 256 + src/backend/storage/fd.h | 96 + src/backend/storage/file/Makefile.inc | 14 + src/backend/storage/file/fd.c | 888 ++ src/backend/storage/ipc.h | 285 + src/backend/storage/ipc/Makefile.inc | 15 + src/backend/storage/ipc/README | 31 + src/backend/storage/ipc/ipc.c | 718 ++ src/backend/storage/ipc/ipci.c | 149 + src/backend/storage/ipc/s_lock.c | 440 + src/backend/storage/ipc/shmem.c | 561 + src/backend/storage/ipc/shmqueue.c | 251 + src/backend/storage/ipc/sinval.c | 169 + src/backend/storage/ipc/sinvaladt.c | 797 ++ src/backend/storage/ipc/spin.c | 247 + src/backend/storage/item.h | 20 + src/backend/storage/itemid.h | 75 + src/backend/storage/itempos.h | 44 + src/backend/storage/itemptr.h | 115 + src/backend/storage/large_object.h | 58 + src/backend/storage/large_object/Makefile.inc | 14 + src/backend/storage/large_object/inv_api.c | 1165 ++ src/backend/storage/lmgr.h | 84 + src/backend/storage/lmgr/Makefile.inc | 14 + src/backend/storage/lmgr/README | 93 + src/backend/storage/lmgr/lmgr.c | 933 ++ src/backend/storage/lmgr/lock.c | 1020 ++ src/backend/storage/lmgr/multi.c | 415 + src/backend/storage/lmgr/proc.c | 826 ++ src/backend/storage/lmgr/single.c | 86 + src/backend/storage/lock.h | 218 + src/backend/storage/multilev.h | 64 + src/backend/storage/off.h | 60 + src/backend/storage/page.h | 26 + src/backend/storage/page/Makefile.inc | 16 + src/backend/storage/page/bufpage.c | 519 + src/backend/storage/page/itemptr.c | 40 + src/backend/storage/pagenum.h | 33 + src/backend/storage/pos.h | 64 + src/backend/storage/proc.h | 127 + src/backend/storage/shmem.h | 104 + src/backend/storage/sinval.h | 33 + src/backend/storage/sinvaladt.h | 126 + src/backend/storage/smgr.h | 84 + src/backend/storage/smgr/Makefile.inc | 14 + src/backend/storage/smgr/README | 40 + src/backend/storage/smgr/md.c | 697 ++ src/backend/storage/smgr/mm.c | 586 + src/backend/storage/smgr/smgr.c | 371 + src/backend/storage/smgr/smgrtype.c | 82 + src/backend/storage/spin.h | 38 + src/backend/tcop/Makefile.inc | 18 + src/backend/tcop/aclchk.c | 555 + src/backend/tcop/dest.c | 354 + src/backend/tcop/dest.h | 78 + src/backend/tcop/fastpath.c | 353 + src/backend/tcop/fastpath.h | 31 + src/backend/tcop/postgres.c | 1500 +++ src/backend/tcop/pquery.c | 362 + src/backend/tcop/pquery.h | 36 + src/backend/tcop/tcopdebug.h | 43 + src/backend/tcop/tcopprot.h | 40 + src/backend/tcop/utility.c | 646 + src/backend/tcop/utility.h | 18 + src/backend/tioga/Arr_TgRecipe.h | 120 + src/backend/tioga/Makefile.inc | 21 + src/backend/tioga/Varray.c | 48 + src/backend/tioga/Varray.h | 45 + src/backend/tioga/tgRecipe.c | 694 ++ src/backend/tioga/tgRecipe.h | 121 + src/backend/utils/Gen_fmgrtab.sh | 265 + src/backend/utils/Makefile.inc | 62 + src/backend/utils/acl.h | 163 + src/backend/utils/adt/Makefile.inc | 20 + src/backend/utils/adt/acl.c | 618 + src/backend/utils/adt/arrayfuncs.c | 1375 +++ src/backend/utils/adt/arrayutils.c | 111 + src/backend/utils/adt/bool.c | 65 + src/backend/utils/adt/char.c | 392 + src/backend/utils/adt/chunk.c | 587 + src/backend/utils/adt/date.c | 891 ++ src/backend/utils/adt/datetimes.c | 350 + src/backend/utils/adt/datum.c | 201 + src/backend/utils/adt/dt.c | 58 + src/backend/utils/adt/filename.c | 120 + src/backend/utils/adt/float.c | 1320 ++ src/backend/utils/adt/geo-ops.c | 1947 +++ src/backend/utils/adt/geo-selfuncs.c | 124 + src/backend/utils/adt/int.c | 343 + src/backend/utils/adt/like.c | 225 + src/backend/utils/adt/misc.c | 96 + src/backend/utils/adt/nabstime.c | 866 ++ src/backend/utils/adt/name.c | 198 + src/backend/utils/adt/not_in.c | 124 + src/backend/utils/adt/numutils.c | 401 + src/backend/utils/adt/oid.c | 127 + src/backend/utils/adt/oidint2.c | 120 + src/backend/utils/adt/oidint4.c | 111 + src/backend/utils/adt/oidname.c | 123 + src/backend/utils/adt/regexp.c | 343 + src/backend/utils/adt/regproc.c | 159 + src/backend/utils/adt/selfuncs.c | 585 + src/backend/utils/adt/sets.c | 164 + src/backend/utils/adt/tid.c | 92 + src/backend/utils/adt/varchar.c | 496 + src/backend/utils/adt/varlena.c | 488 + src/backend/utils/array.h | 166 + src/backend/utils/bit.h | 39 + src/backend/utils/builtins.h | 433 + src/backend/utils/cache/Makefile.inc | 15 + src/backend/utils/cache/catcache.c | 1023 ++ src/backend/utils/cache/fcache.c | 297 + src/backend/utils/cache/inval.c | 612 + src/backend/utils/cache/lsyscache.c | 484 + src/backend/utils/cache/rel.c | 77 + src/backend/utils/cache/relcache.c | 1795 +++ src/backend/utils/cache/syscache.c | 630 + src/backend/utils/catcache.h | 85 + src/backend/utils/datum.h | 64 + src/backend/utils/dynamic_loader.h | 53 + src/backend/utils/elog.h | 38 + src/backend/utils/error/Makefile.inc | 14 + src/backend/utils/error/assert.c | 64 + src/backend/utils/error/elog.c | 237 + src/backend/utils/error/exc.c | 183 + src/backend/utils/error/excabort.c | 28 + src/backend/utils/error/excid.c | 64 + src/backend/utils/error/format.c | 40 + src/backend/utils/exc.h | 101 + src/backend/utils/excid.h | 31 + src/backend/utils/fcache.h | 55 + src/backend/utils/fcache2.h | 19 + src/backend/utils/fmgr/Makefile.inc | 15 + src/backend/utils/fmgr/dfmgr.c | 269 + src/backend/utils/fmgr/fmgr.c | 254 + src/backend/utils/fmgrtab.h | 29 + src/backend/utils/geo-decls.h | 248 + src/backend/utils/hash/Makefile.inc | 14 + src/backend/utils/hash/dynahash.c | 868 ++ src/backend/utils/hash/hashfn.c | 156 + src/backend/utils/hsearch.h | 141 + src/backend/utils/init/Makefile.inc | 14 + src/backend/utils/init/enbl.c | 45 + src/backend/utils/init/findbe.c | 251 + src/backend/utils/init/globals.c | 108 + src/backend/utils/init/magic.c | 167 + src/backend/utils/init/miscinit.c | 378 + src/backend/utils/init/postinit.c | 648 + src/backend/utils/inval.h | 56 + src/backend/utils/lselect.h | 40 + src/backend/utils/lsyscache.h | 45 + src/backend/utils/mcxt.h | 56 + src/backend/utils/memutils.h | 281 + src/backend/utils/mmgr/Makefile.inc | 15 + src/backend/utils/mmgr/aset.c | 381 + src/backend/utils/mmgr/mcxt.c | 510 + src/backend/utils/mmgr/oset.c | 173 + src/backend/utils/mmgr/palloc.c | 117 + src/backend/utils/mmgr/portalmem.c | 980 ++ src/backend/utils/module.h | 25 + src/backend/utils/nabstime.h | 165 + src/backend/utils/oidcompos.h | 52 + src/backend/utils/palloc.h | 26 + src/backend/utils/portal.h | 97 + src/backend/utils/psort.h | 86 + src/backend/utils/rel.h | 170 + src/backend/utils/rel2.h | 23 + src/backend/utils/relcache.h | 47 + src/backend/utils/sets.h | 22 + src/backend/utils/sort/Makefile.inc | 14 + src/backend/utils/sort/lselect.c | 365 + src/backend/utils/sort/psort.c | 617 + src/backend/utils/syscache.h | 89 + src/backend/utils/time/Makefile.inc | 14 + src/backend/utils/time/tqual.c | 815 ++ src/backend/utils/tqual.h | 55 + src/bin/Makefile | 30 + src/bin/Makefile.global | 33 + src/bin/cleardbdir/Makefile | 21 + src/bin/cleardbdir/cleardbdir.sh | 37 + src/bin/createdb/Makefile | 21 + src/bin/createdb/createdb.sh | 66 + src/bin/createuser/Makefile | 21 + src/bin/createuser/createuser.sh | 225 + src/bin/destroydb/Makefile | 21 + src/bin/destroydb/destroydb.sh | 69 + src/bin/destroyuser/Makefile | 21 + src/bin/destroyuser/destroyuser.sh | 192 + src/bin/initdb/Makefile | 21 + src/bin/initdb/initdb.sh | 222 + src/bin/ipcclean/Makefile | 21 + src/bin/ipcclean/ipcclean.sh | 8 + src/bin/monitor/Makefile | 23 + src/bin/monitor/monitor.c | 1058 ++ src/bin/pg4_dump/Makefile | 13 + src/bin/pg4_dump/README | 87 + src/bin/pg4_dump/common.c | 417 + src/bin/pg4_dump/pg4_dump.c | 1602 +++ src/bin/pg4_dump/pg_dump.h | 195 + src/bin/pg_dump/Makefile | 23 + src/bin/pg_dump/README | 73 + src/bin/pg_dump/common.c | 397 + src/bin/pg_dump/pg_dump.c | 1443 +++ src/bin/pg_dump/pg_dump.h | 195 + src/bin/pg_id/Makefile | 23 + src/bin/pg_id/pg_id.c | 52 + src/bin/pg_version/Makefile | 26 + src/bin/pg_version/pg_version.c | 35 + src/bin/pgtclsh/Makefile | 46 + src/bin/pgtclsh/README | 21 + src/bin/pgtclsh/pgtclAppInit.c | 114 + src/bin/pgtclsh/pgtclUtils.tcl | 16 + src/bin/pgtclsh/pgtkAppInit.c | 117 + src/bin/pgtclsh/updateStats.tcl | 71 + src/bin/psql/Makefile | 65 + src/bin/psql/psql.c | 1230 ++ src/bin/psql/psqlHelp.h | 168 + src/bin/psql/rlstubs.c | 41 + src/bin/psql/stringutils.c | 104 + src/bin/psql/stringutils.h | 51 + src/interfaces/libpgtcl/Makefile | 38 + src/interfaces/libpgtcl/README | 7 + src/interfaces/libpgtcl/libpgtcl.h | 21 + src/interfaces/libpgtcl/pgtcl.c | 105 + src/interfaces/libpgtcl/pgtclCmds.c | 812 ++ src/interfaces/libpgtcl/pgtclCmds.h | 52 + src/interfaces/libpgtcl/pgtclId.c | 51 + src/interfaces/libpgtcl/pgtclId.h | 18 + src/interfaces/libpq++/Makefile | 54 + src/interfaces/libpq++/README | 22 + src/interfaces/libpq++/examples/Makefile | 70 + src/interfaces/libpq++/examples/testlibpq0.cc | 49 + src/interfaces/libpq++/examples/testlibpq1.cc | 84 + src/interfaces/libpq++/examples/testlibpq2.cc | 71 + .../libpq++/examples/testlibpq2.sql | 5 + src/interfaces/libpq++/examples/testlibpq3.cc | 131 + .../libpq++/examples/testlibpq3.sql | 6 + src/interfaces/libpq++/examples/testlibpq4.cc | 69 + src/interfaces/libpq++/examples/testlo.cc | 63 + src/interfaces/libpq++/libpq++.H | 173 + src/interfaces/libpq++/man/libpq++.3 | 434 + src/interfaces/libpq++/pgconnection.cc | 94 + src/interfaces/libpq++/pgenv.cc | 109 + src/interfaces/libpq++/pglobject.cc | 152 + src/interfaces/libpq/Makefile | 98 + src/interfaces/libpq/README | 1 + src/interfaces/libpq/fe-auth.c | 544 + src/interfaces/libpq/fe-auth.h | 38 + src/interfaces/libpq/fe-connect.c | 460 + src/interfaces/libpq/fe-exec.c | 1061 ++ src/interfaces/libpq/fe-lobj.c | 381 + src/interfaces/libpq/fe-misc.c | 193 + src/interfaces/libpq/libpq-fe.h | 251 + src/interfaces/libpq/pg_hba | 13 + src/interfaces/libpq/pqsignal.c | 40 + src/interfaces/libpq/pqsignal.h | 32 + src/mk/port/postgres.mk.BSD44_derived | 40 + src/mk/port/postgres.mk.aix | 55 + src/mk/port/postgres.mk.alpha | 52 + src/mk/port/postgres.mk.bsdi | 40 + src/mk/port/postgres.mk.hpux | 64 + src/mk/port/postgres.mk.irix5 | 49 + src/mk/port/postgres.mk.linux | 54 + src/mk/port/postgres.mk.sparc | 38 + src/mk/port/postgres.mk.sparc_solaris | 57 + src/mk/port/postgres.mk.svr4 | 35 + src/mk/port/postgres.mk.ultrix4 | 34 + src/mk/postgres.lib.mk | 51 + src/mk/postgres.mk | 150 + src/mk/postgres.prog.mk | 26 + src/mk/postgres.shell.mk | 62 + src/mk/postgres.subdir.mk | 21 + src/mk/postgres.user.mk | 79 + src/test/Makefile | 18 + src/test/bench/Makefile | 62 + src/test/bench/WISC-README | 28 + src/test/bench/create.sh | 24 + src/test/bench/create.source | 17 + src/test/bench/perquery | 12 + src/test/bench/query01 | 4 + src/test/bench/query02 | 4 + src/test/bench/query03 | 4 + src/test/bench/query04 | 4 + src/test/bench/query05 | 4 + src/test/bench/query06 | 4 + src/test/bench/query07 | 2 + src/test/bench/query08 | 2 + src/test/bench/query09 | 4 + src/test/bench/query10 | 4 + src/test/bench/query11 | 4 + src/test/bench/query12 | 4 + src/test/bench/query13 | 4 + src/test/bench/query14 | 4 + src/test/bench/query15 | 4 + src/test/bench/query16 | 4 + src/test/bench/query17 | 4 + src/test/bench/query18 | 4 + src/test/bench/query19 | 4 + src/test/bench/query20 | 4 + src/test/bench/query21 | 0 src/test/bench/query22 | 0 src/test/bench/query23 | 4 + src/test/bench/query24 | 0 src/test/bench/query25 | 0 src/test/bench/query26 | 2 + src/test/bench/query27 | 2 + src/test/bench/query28 | 2 + src/test/bench/query29 | 2 + src/test/bench/query30 | 2 + src/test/bench/query31 | 2 + src/test/bench/query32 | 2 + src/test/bench/runwisc.sh | 17 + src/test/bench/wholebench.sh | 5 + src/test/examples/Makefile | 74 + src/test/examples/testlibpq.c | 118 + src/test/examples/testlibpq2.c | 93 + src/test/examples/testlibpq2.sql | 5 + src/test/examples/testlibpq3.c | 154 + src/test/examples/testlibpq3.sql | 6 + src/test/examples/testlibpq4.c | 127 + src/test/examples/testlo.c | 232 + src/test/examples/testlo2.c | 233 + src/test/regress/Makefile | 62 + src/test/regress/create.source | 765 ++ src/test/regress/data/dept.data | 2 + src/test/regress/data/desc.data | 10000 ++++++++++++++++ src/test/regress/data/emp.data | 3 + src/test/regress/data/hash.data | 10000 ++++++++++++++++ src/test/regress/data/onek.data | 1000 ++ src/test/regress/data/person.data | 50 + src/test/regress/data/real_city.data | 5 + src/test/regress/data/rect.data | 3100 +++++ src/test/regress/data/streets.data | 5124 ++++++++ src/test/regress/data/stud_emp.data | 3 + src/test/regress/data/student.data | 2 + src/test/regress/data/tenk.data | 10000 ++++++++++++++++ src/test/regress/destroy.source | 285 + src/test/regress/errors.source | 275 + src/test/regress/queries.source | 2614 ++++ src/test/regress/regress.c | 271 + src/test/regress/regress.sh | 64 + src/test/regress/sample.regress.out | 6362 ++++++++++ src/test/regress/security.source | 64 + src/test/suite/README | 5 + src/test/suite/agg.sql | 76 + src/test/suite/date.sql | 30 + src/test/suite/float.sql | 113 + src/test/suite/group.sql | 100 + src/test/suite/group_err.sql | 29 + src/test/suite/inh.sql | 73 + src/test/suite/join.sql | 40 + src/test/suite/oper.sql | 27 + src/test/suite/parse.sql | 45 + src/test/suite/quote.sql | 18 + src/test/suite/results/agg.sql.out | 147 + src/test/suite/results/date.sql.out | 72 + src/test/suite/results/float.sql.out | 330 + src/test/suite/results/group.sql.out | 262 + src/test/suite/results/group_err.sql.out | 18 + src/test/suite/results/inh.sql.out | 86 + src/test/suite/results/join.sql.out | 40 + src/test/suite/results/oper.sql.out | 18 + src/test/suite/results/parse.sql.out | 60 + src/test/suite/results/quote.sql.out | 32 + src/test/suite/results/rules.sql.out | 22 + src/test/suite/results/select.sql.out | 31 + src/test/suite/results/sort.sql.out | 229 + src/test/suite/results/sqlcompat.sql.out | 100 + src/test/suite/results/time.sql.out | 72 + src/test/suite/results/varchar.sql.out | 226 + src/test/suite/results/views.sql.out | 125 + src/test/suite/rules.sql | 29 + src/test/suite/runall | 8 + src/test/suite/select.sql | 31 + src/test/suite/sort.sql | 57 + src/test/suite/sqlcompat.sql | 57 + src/test/suite/time.sql | 30 + src/test/suite/varchar.sql | 64 + src/test/suite/views.sql | 77 + src/tools/mkldexport/Makefile | 20 + src/tools/mkldexport/README | 12 + src/tools/mkldexport/mkldexport.sh | 36 + src/tutorial/C-code/beard.c | 64 + src/tutorial/C-code/complex.c | 150 + src/tutorial/C-code/funcs.c | 56 + src/tutorial/Makefile | 39 + src/tutorial/README | 24 + src/tutorial/advanced.source | 125 + src/tutorial/basics.source | 188 + src/tutorial/complex.source | 251 + src/tutorial/funcs.source | 158 + src/tutorial/syscat.source | 151 + 868 files changed, 242656 insertions(+) create mode 100644 src/Makefile create mode 100644 src/Makefile.global create mode 100644 src/backend/Makefile create mode 100644 src/backend/access/Makefile.inc create mode 100644 src/backend/access/attnum.h create mode 100644 src/backend/access/common/Makefile.inc create mode 100644 src/backend/access/common/heaptuple.c create mode 100644 src/backend/access/common/heapvalid.c create mode 100644 src/backend/access/common/indextuple.c create mode 100644 src/backend/access/common/indexvalid.c create mode 100644 src/backend/access/common/printtup.c create mode 100644 src/backend/access/common/scankey.c create mode 100644 src/backend/access/common/tupdesc.c create mode 100644 src/backend/access/funcindex.h create mode 100644 src/backend/access/genam.h create mode 100644 src/backend/access/hash.h create mode 100644 src/backend/access/hash/Makefile.inc create mode 100644 src/backend/access/hash/hash.c create mode 100644 src/backend/access/hash/hashfunc.c create mode 100644 src/backend/access/hash/hashinsert.c create mode 100644 src/backend/access/hash/hashovfl.c create mode 100644 src/backend/access/hash/hashpage.c create mode 100644 src/backend/access/hash/hashscan.c create mode 100644 src/backend/access/hash/hashsearch.c create mode 100644 src/backend/access/hash/hashstrat.c create mode 100644 src/backend/access/hash/hashutil.c create mode 100644 src/backend/access/heap/Makefile.inc create mode 100644 src/backend/access/heap/heapam.c create mode 100644 src/backend/access/heap/hio.c create mode 100644 src/backend/access/heap/stats.c create mode 100644 src/backend/access/heapam.h create mode 100644 src/backend/access/hio.h create mode 100644 src/backend/access/htup.h create mode 100644 src/backend/access/ibit.h create mode 100644 src/backend/access/index/Makefile.inc create mode 100644 src/backend/access/index/genam.c create mode 100644 src/backend/access/index/indexam.c create mode 100644 src/backend/access/index/istrat.c create mode 100644 src/backend/access/iqual.h create mode 100644 src/backend/access/istrat.h create mode 100644 src/backend/access/itup.h create mode 100644 src/backend/access/nbtree.h create mode 100644 src/backend/access/nbtree/Makefile.inc create mode 100644 src/backend/access/nbtree/README create mode 100644 src/backend/access/nbtree/nbtcompare.c create mode 100644 src/backend/access/nbtree/nbtinsert.c create mode 100644 src/backend/access/nbtree/nbtpage.c create mode 100644 src/backend/access/nbtree/nbtree.c create mode 100644 src/backend/access/nbtree/nbtscan.c create mode 100644 src/backend/access/nbtree/nbtsearch.c create mode 100644 src/backend/access/nbtree/nbtsort.c create mode 100644 src/backend/access/nbtree/nbtstrat.c create mode 100644 src/backend/access/nbtree/nbtutils.c create mode 100644 src/backend/access/printtup.h create mode 100644 src/backend/access/relscan.h create mode 100644 src/backend/access/rtree.h create mode 100644 src/backend/access/rtree/Makefile.inc create mode 100644 src/backend/access/rtree/rtget.c create mode 100644 src/backend/access/rtree/rtproc.c create mode 100644 src/backend/access/rtree/rtree.c create mode 100644 src/backend/access/rtree/rtscan.c create mode 100644 src/backend/access/rtree/rtstrat.c create mode 100644 src/backend/access/rtscan.h create mode 100644 src/backend/access/rtstrat.h create mode 100644 src/backend/access/sdir.h create mode 100644 src/backend/access/skey.h create mode 100644 src/backend/access/strat.h create mode 100644 src/backend/access/transam.h create mode 100644 src/backend/access/transam/Makefile.inc create mode 100644 src/backend/access/transam/transam.c create mode 100644 src/backend/access/transam/transsup.c create mode 100644 src/backend/access/transam/varsup.c create mode 100644 src/backend/access/transam/xact.c create mode 100644 src/backend/access/transam/xid.c create mode 100644 src/backend/access/tupdesc.h create mode 100644 src/backend/access/tupmacs.h create mode 100644 src/backend/access/valid.h create mode 100644 src/backend/access/xact.h create mode 100644 src/backend/bootstrap/Makefile.inc create mode 100644 src/backend/bootstrap/boot.sed create mode 100644 src/backend/bootstrap/bootparse.y create mode 100644 src/backend/bootstrap/bootscanner.l create mode 100644 src/backend/bootstrap/bootstrap.c create mode 100644 src/backend/bootstrap/bootstrap.h create mode 100644 src/backend/catalog/Makefile.inc create mode 100644 src/backend/catalog/README create mode 100644 src/backend/catalog/catalog.c create mode 100644 src/backend/catalog/catalog.h create mode 100644 src/backend/catalog/catname.h create mode 100644 src/backend/catalog/genbki.sh create mode 100644 src/backend/catalog/heap.c create mode 100644 src/backend/catalog/heap.h create mode 100644 src/backend/catalog/index.c create mode 100644 src/backend/catalog/index.h create mode 100644 src/backend/catalog/indexing.c create mode 100644 src/backend/catalog/indexing.h create mode 100644 src/backend/catalog/pg_aggregate.c create mode 100644 src/backend/catalog/pg_aggregate.h create mode 100644 src/backend/catalog/pg_am.h create mode 100644 src/backend/catalog/pg_amop.h create mode 100644 src/backend/catalog/pg_amproc.h create mode 100644 src/backend/catalog/pg_attribute.h create mode 100644 src/backend/catalog/pg_class.h create mode 100644 src/backend/catalog/pg_database.h create mode 100644 src/backend/catalog/pg_defaults.h create mode 100644 src/backend/catalog/pg_demon.h create mode 100644 src/backend/catalog/pg_group.h create mode 100644 src/backend/catalog/pg_hosts.h create mode 100644 src/backend/catalog/pg_index.h create mode 100644 src/backend/catalog/pg_inheritproc.h create mode 100644 src/backend/catalog/pg_inherits.h create mode 100644 src/backend/catalog/pg_ipl.h create mode 100644 src/backend/catalog/pg_language.h create mode 100644 src/backend/catalog/pg_listener.h create mode 100644 src/backend/catalog/pg_log.h create mode 100644 src/backend/catalog/pg_magic.h create mode 100644 src/backend/catalog/pg_opclass.h create mode 100644 src/backend/catalog/pg_operator.c create mode 100644 src/backend/catalog/pg_operator.h create mode 100644 src/backend/catalog/pg_parg.h create mode 100644 src/backend/catalog/pg_proc.c create mode 100644 src/backend/catalog/pg_proc.h create mode 100644 src/backend/catalog/pg_rewrite.h create mode 100644 src/backend/catalog/pg_server.h create mode 100644 src/backend/catalog/pg_statistic.h create mode 100644 src/backend/catalog/pg_time.h create mode 100644 src/backend/catalog/pg_type.c create mode 100644 src/backend/catalog/pg_type.h create mode 100644 src/backend/catalog/pg_user.h create mode 100644 src/backend/catalog/pg_variable.h create mode 100644 src/backend/catalog/pg_version.h create mode 100644 src/backend/catalog/unused_oids create mode 100644 src/backend/commands/Makefile.inc create mode 100644 src/backend/commands/_deadcode/version.c create mode 100644 src/backend/commands/async.c create mode 100644 src/backend/commands/async.h create mode 100644 src/backend/commands/cluster.c create mode 100644 src/backend/commands/cluster.h create mode 100644 src/backend/commands/command.c create mode 100644 src/backend/commands/command.h create mode 100644 src/backend/commands/copy.c create mode 100644 src/backend/commands/copy.h create mode 100644 src/backend/commands/creatinh.c create mode 100644 src/backend/commands/creatinh.h create mode 100644 src/backend/commands/defind.c create mode 100644 src/backend/commands/define.c create mode 100644 src/backend/commands/defrem.h create mode 100644 src/backend/commands/explain.c create mode 100644 src/backend/commands/explain.h create mode 100644 src/backend/commands/purge.c create mode 100644 src/backend/commands/purge.h create mode 100644 src/backend/commands/recipe.c create mode 100644 src/backend/commands/recipe.h create mode 100644 src/backend/commands/remove.c create mode 100644 src/backend/commands/rename.c create mode 100644 src/backend/commands/rename.h create mode 100644 src/backend/commands/vacuum.c create mode 100644 src/backend/commands/vacuum.h create mode 100644 src/backend/commands/version.h create mode 100644 src/backend/commands/view.c create mode 100644 src/backend/commands/view.h create mode 100644 src/backend/executor/Makefile.inc create mode 100644 src/backend/executor/execAmi.c create mode 100644 src/backend/executor/execFlatten.c create mode 100644 src/backend/executor/execFlatten.h create mode 100644 src/backend/executor/execJunk.c create mode 100644 src/backend/executor/execMain.c create mode 100644 src/backend/executor/execProcnode.c create mode 100644 src/backend/executor/execQual.c create mode 100644 src/backend/executor/execScan.c create mode 100644 src/backend/executor/execTuples.c create mode 100644 src/backend/executor/execUtils.c create mode 100644 src/backend/executor/execdebug.h create mode 100644 src/backend/executor/execdefs.h create mode 100644 src/backend/executor/execdesc.h create mode 100644 src/backend/executor/executor.h create mode 100644 src/backend/executor/functions.c create mode 100644 src/backend/executor/functions.h create mode 100644 src/backend/executor/hashjoin.h create mode 100644 src/backend/executor/nodeAgg.c create mode 100644 src/backend/executor/nodeAgg.h create mode 100644 src/backend/executor/nodeAppend.c create mode 100644 src/backend/executor/nodeAppend.h create mode 100644 src/backend/executor/nodeGroup.c create mode 100644 src/backend/executor/nodeGroup.h create mode 100644 src/backend/executor/nodeHash.c create mode 100644 src/backend/executor/nodeHash.h create mode 100644 src/backend/executor/nodeHashjoin.c create mode 100644 src/backend/executor/nodeHashjoin.h create mode 100644 src/backend/executor/nodeIndexscan.c create mode 100644 src/backend/executor/nodeIndexscan.h create mode 100644 src/backend/executor/nodeMaterial.c create mode 100644 src/backend/executor/nodeMaterial.h create mode 100644 src/backend/executor/nodeMergejoin.c create mode 100644 src/backend/executor/nodeMergejoin.h create mode 100644 src/backend/executor/nodeNestloop.c create mode 100644 src/backend/executor/nodeNestloop.h create mode 100644 src/backend/executor/nodeResult.c create mode 100644 src/backend/executor/nodeResult.h create mode 100644 src/backend/executor/nodeSeqscan.c create mode 100644 src/backend/executor/nodeSeqscan.h create mode 100644 src/backend/executor/nodeSort.c create mode 100644 src/backend/executor/nodeSort.h create mode 100644 src/backend/executor/nodeTee.c create mode 100644 src/backend/executor/nodeTee.h create mode 100644 src/backend/executor/nodeUnique.c create mode 100644 src/backend/executor/nodeUnique.h create mode 100644 src/backend/executor/tuptable.h create mode 100644 src/backend/include/Makefile.inc create mode 100644 src/backend/include/c.h create mode 100644 src/backend/include/miscadmin.h create mode 100644 src/backend/include/postgres.h create mode 100644 src/backend/lib/Makefile.inc create mode 100644 src/backend/lib/bit.c create mode 100644 src/backend/lib/dllist.c create mode 100644 src/backend/lib/dllist.h create mode 100644 src/backend/lib/fstack.c create mode 100644 src/backend/lib/fstack.h create mode 100644 src/backend/lib/hasht.c create mode 100644 src/backend/lib/hasht.h create mode 100644 src/backend/lib/lispsort.c create mode 100644 src/backend/lib/lispsort.h create mode 100644 src/backend/lib/qsort.c create mode 100644 src/backend/lib/qsort.h create mode 100644 src/backend/lib/stringinfo.c create mode 100644 src/backend/lib/stringinfo.h create mode 100644 src/backend/libpq/Makefile.inc create mode 100644 src/backend/libpq/auth.c create mode 100644 src/backend/libpq/auth.h create mode 100644 src/backend/libpq/be-dumpdata.c create mode 100644 src/backend/libpq/be-fsstubs.c create mode 100644 src/backend/libpq/be-fsstubs.h create mode 100644 src/backend/libpq/be-pqexec.c create mode 100644 src/backend/libpq/libpq-be.h create mode 100644 src/backend/libpq/libpq-fs.h create mode 100644 src/backend/libpq/libpq.h create mode 100644 src/backend/libpq/portal.c create mode 100644 src/backend/libpq/portalbuf.c create mode 100644 src/backend/libpq/pqcomm.c create mode 100644 src/backend/libpq/pqcomm.h create mode 100644 src/backend/libpq/pqpacket.c create mode 100644 src/backend/libpq/pqsignal.c create mode 100644 src/backend/libpq/pqsignal.h create mode 100644 src/backend/main/Makefile.inc create mode 100644 src/backend/main/main.c create mode 100644 src/backend/makeID create mode 100644 src/backend/nodes/Makefile.inc create mode 100644 src/backend/nodes/README create mode 100644 src/backend/nodes/copyfuncs.c create mode 100644 src/backend/nodes/equalfuncs.c create mode 100644 src/backend/nodes/execnodes.h create mode 100644 src/backend/nodes/list.c create mode 100644 src/backend/nodes/makefuncs.c create mode 100644 src/backend/nodes/makefuncs.h create mode 100644 src/backend/nodes/memnodes.h create mode 100644 src/backend/nodes/nodeFuncs.c create mode 100644 src/backend/nodes/nodeFuncs.h create mode 100644 src/backend/nodes/nodes.c create mode 100644 src/backend/nodes/nodes.h create mode 100644 src/backend/nodes/outfuncs.c create mode 100644 src/backend/nodes/params.h create mode 100644 src/backend/nodes/parsenodes.h create mode 100644 src/backend/nodes/pg_list.h create mode 100644 src/backend/nodes/plannodes.h create mode 100644 src/backend/nodes/primnodes.h create mode 100644 src/backend/nodes/print.c create mode 100644 src/backend/nodes/read.c create mode 100644 src/backend/nodes/readfuncs.c create mode 100644 src/backend/nodes/readfuncs.h create mode 100644 src/backend/nodes/relation.h create mode 100644 src/backend/optimizer/Makefile.inc create mode 100644 src/backend/optimizer/clauseinfo.h create mode 100644 src/backend/optimizer/clauses.h create mode 100644 src/backend/optimizer/cost.h create mode 100644 src/backend/optimizer/internal.h create mode 100644 src/backend/optimizer/joininfo.h create mode 100644 src/backend/optimizer/keys.h create mode 100644 src/backend/optimizer/ordering.h create mode 100644 src/backend/optimizer/path/Makefile.inc create mode 100644 src/backend/optimizer/path/allpaths.c create mode 100644 src/backend/optimizer/path/clausesel.c create mode 100644 src/backend/optimizer/path/costsize.c create mode 100644 src/backend/optimizer/path/hashutils.c create mode 100644 src/backend/optimizer/path/indxpath.c create mode 100644 src/backend/optimizer/path/joinpath.c create mode 100644 src/backend/optimizer/path/joinrels.c create mode 100644 src/backend/optimizer/path/joinutils.c create mode 100644 src/backend/optimizer/path/mergeutils.c create mode 100644 src/backend/optimizer/path/orindxpath.c create mode 100644 src/backend/optimizer/path/predmig.c create mode 100644 src/backend/optimizer/path/prune.c create mode 100644 src/backend/optimizer/path/xfunc.c create mode 100644 src/backend/optimizer/pathnode.h create mode 100644 src/backend/optimizer/paths.h create mode 100644 src/backend/optimizer/plan/Makefile.inc create mode 100644 src/backend/optimizer/plan/createplan.c create mode 100644 src/backend/optimizer/plan/initsplan.c create mode 100644 src/backend/optimizer/plan/planmain.c create mode 100644 src/backend/optimizer/plan/planner.c create mode 100644 src/backend/optimizer/plan/setrefs.c create mode 100644 src/backend/optimizer/plancat.h create mode 100644 src/backend/optimizer/planmain.h create mode 100644 src/backend/optimizer/planner.h create mode 100644 src/backend/optimizer/prep.h create mode 100644 src/backend/optimizer/prep/Makefile.inc create mode 100644 src/backend/optimizer/prep/archive.c create mode 100644 src/backend/optimizer/prep/prepqual.c create mode 100644 src/backend/optimizer/prep/preptlist.c create mode 100644 src/backend/optimizer/prep/prepunion.c create mode 100644 src/backend/optimizer/tlist.h create mode 100644 src/backend/optimizer/util/Makefile.inc create mode 100644 src/backend/optimizer/util/clauseinfo.c create mode 100644 src/backend/optimizer/util/clauses.c create mode 100644 src/backend/optimizer/util/indexnode.c create mode 100644 src/backend/optimizer/util/internal.c create mode 100644 src/backend/optimizer/util/joininfo.c create mode 100644 src/backend/optimizer/util/keys.c create mode 100644 src/backend/optimizer/util/ordering.c create mode 100644 src/backend/optimizer/util/pathnode.c create mode 100644 src/backend/optimizer/util/plancat.c create mode 100644 src/backend/optimizer/util/relnode.c create mode 100644 src/backend/optimizer/util/tlist.c create mode 100644 src/backend/optimizer/util/var.c create mode 100644 src/backend/optimizer/var.h create mode 100644 src/backend/optimizer/xfunc.h create mode 100644 src/backend/parser/Makefile.inc create mode 100644 src/backend/parser/analyze.c create mode 100644 src/backend/parser/catalog_utils.c create mode 100644 src/backend/parser/catalog_utils.h create mode 100644 src/backend/parser/dbcommands.c create mode 100644 src/backend/parser/dbcommands.h create mode 100644 src/backend/parser/gram.y create mode 100644 src/backend/parser/keywords.c create mode 100644 src/backend/parser/keywords.h create mode 100644 src/backend/parser/parse_query.c create mode 100644 src/backend/parser/parse_query.h create mode 100644 src/backend/parser/parse_state.h create mode 100644 src/backend/parser/parser.c create mode 100644 src/backend/parser/parsetree.h create mode 100644 src/backend/parser/scan.l create mode 100644 src/backend/parser/scansup.c create mode 100644 src/backend/parser/scansup.h create mode 100644 src/backend/port/BSD44_derived/Makefile.inc create mode 100644 src/backend/port/BSD44_derived/README create mode 100644 src/backend/port/BSD44_derived/dl.c create mode 100644 src/backend/port/BSD44_derived/float.h create mode 100644 src/backend/port/BSD44_derived/machine.h create mode 100644 src/backend/port/BSD44_derived/port-protos.h create mode 100644 src/backend/port/Makefile.inc create mode 100644 src/backend/port/aix/Makefile.inc create mode 100644 src/backend/port/aix/README.dlfcn create mode 100644 src/backend/port/aix/dlfcn.c create mode 100644 src/backend/port/aix/dlfcn.h create mode 100644 src/backend/port/aix/machine.h create mode 100755 src/backend/port/aix/mkldexport.sh create mode 100644 src/backend/port/aix/port-protos.h create mode 100644 src/backend/port/alpha/Makefile.inc create mode 100644 src/backend/port/alpha/machine.h create mode 100644 src/backend/port/alpha/port-protos.h create mode 100644 src/backend/port/alpha/port.c create mode 100644 src/backend/port/bsdi/Makefile.inc create mode 100644 src/backend/port/bsdi/dynloader.c create mode 100644 src/backend/port/bsdi/machine.h create mode 100644 src/backend/port/bsdi/port-protos.h create mode 100644 src/backend/port/bsdi/port.c create mode 100644 src/backend/port/hpux/Makefile.inc create mode 100644 src/backend/port/hpux/dynloader.c create mode 100644 src/backend/port/hpux/fixade.h create mode 100644 src/backend/port/hpux/machine.h create mode 100644 src/backend/port/hpux/port-protos.h create mode 100644 src/backend/port/hpux/port.c create mode 100644 src/backend/port/hpux/tas.c.template create mode 100644 src/backend/port/hpux/tas.s create mode 100644 src/backend/port/irix5/Makefile.inc create mode 100644 src/backend/port/irix5/README create mode 100644 src/backend/port/irix5/machine.h create mode 100644 src/backend/port/irix5/port-protos.h create mode 100644 src/backend/port/irix5/port.c create mode 100644 src/backend/port/linux/Makefile.inc create mode 100644 src/backend/port/linux/dynloader.c create mode 100644 src/backend/port/linux/machine.h create mode 100644 src/backend/port/linux/port-protos.h create mode 100644 src/backend/port/linux/port.c create mode 100644 src/backend/port/sparc/Makefile.inc create mode 100644 src/backend/port/sparc/float.h create mode 100644 src/backend/port/sparc/machine.h create mode 100644 src/backend/port/sparc/port-protos.h create mode 100644 src/backend/port/sparc/strtol.c create mode 100644 src/backend/port/sparc_solaris/Makefile.inc create mode 100644 src/backend/port/sparc_solaris/machine.h create mode 100644 src/backend/port/sparc_solaris/port-protos.h create mode 100644 src/backend/port/sparc_solaris/port.c create mode 100644 src/backend/port/sparc_solaris/rusagestub.h create mode 100644 src/backend/port/sparc_solaris/tas.s create mode 100644 src/backend/port/ultrix4/Makefile.inc create mode 100644 src/backend/port/ultrix4/dl.h create mode 100644 src/backend/port/ultrix4/dynloader.c create mode 100644 src/backend/port/ultrix4/machine.h create mode 100644 src/backend/port/ultrix4/port-protos.h create mode 100644 src/backend/port/ultrix4/port.c create mode 100644 src/backend/port/ultrix4/strdup.c create mode 100644 src/backend/port/win32/machine.h create mode 100644 src/backend/port/win32/nt.c create mode 100644 src/backend/port/win32/nt.h create mode 100644 src/backend/port/win32/pglite.mak create mode 100644 src/backend/port/win32/port-protos.h create mode 100644 src/backend/port/win32/pwd.h create mode 100644 src/backend/port/win32/regex/COPYRIGHT create mode 100644 src/backend/port/win32/regex/Makefile.inc create mode 100644 src/backend/port/win32/regex/WHATSNEW create mode 100644 src/backend/port/win32/regex/cclass.h create mode 100644 src/backend/port/win32/regex/cname.h create mode 100644 src/backend/port/win32/regex/engine.c create mode 100644 src/backend/port/win32/regex/re_format.7 create mode 100644 src/backend/port/win32/regex/regcomp.c create mode 100644 src/backend/port/win32/regex/regerror.c create mode 100644 src/backend/port/win32/regex/regex.3 create mode 100644 src/backend/port/win32/regex/regex.h create mode 100644 src/backend/port/win32/regex/regex2.h create mode 100644 src/backend/port/win32/regex/regexec.c create mode 100644 src/backend/port/win32/regex/regexp.h create mode 100644 src/backend/port/win32/regex/regfree.c create mode 100644 src/backend/port/win32/regex/utils.h create mode 100644 src/backend/port/win32/rusagestub.h create mode 100644 src/backend/port/win32/sys/cdefs.h create mode 100644 src/backend/port/win32/sys/file.h create mode 100644 src/backend/port/win32/sys/ipc.h create mode 100644 src/backend/port/win32/sys/param.h create mode 100644 src/backend/port/win32/sys/sem.h create mode 100644 src/backend/port/win32/sys/shm.h create mode 100644 src/backend/port/win32/sys/time.h create mode 100644 src/backend/postmaster/Makefile.inc create mode 100644 src/backend/postmaster/postmaster.c create mode 100644 src/backend/regex/COPYRIGHT create mode 100644 src/backend/regex/Makefile.inc create mode 100644 src/backend/regex/WHATSNEW create mode 100644 src/backend/regex/cclass.h create mode 100644 src/backend/regex/cdefs.h create mode 100644 src/backend/regex/cname.h create mode 100644 src/backend/regex/engine.c create mode 100644 src/backend/regex/re_format.7 create mode 100644 src/backend/regex/regcomp.c create mode 100644 src/backend/regex/regerror.c create mode 100644 src/backend/regex/regex.3 create mode 100644 src/backend/regex/regex.h create mode 100644 src/backend/regex/regex2.h create mode 100644 src/backend/regex/regexec.c create mode 100644 src/backend/regex/regexp.h create mode 100644 src/backend/regex/regfree.c create mode 100644 src/backend/regex/utils.h create mode 100644 src/backend/rewrite/Makefile.inc create mode 100644 src/backend/rewrite/locks.c create mode 100644 src/backend/rewrite/locks.h create mode 100644 src/backend/rewrite/prs2lock.h create mode 100644 src/backend/rewrite/rewriteDefine.c create mode 100644 src/backend/rewrite/rewriteDefine.h create mode 100644 src/backend/rewrite/rewriteHandler.c create mode 100644 src/backend/rewrite/rewriteHandler.h create mode 100644 src/backend/rewrite/rewriteManip.c create mode 100644 src/backend/rewrite/rewriteManip.h create mode 100644 src/backend/rewrite/rewriteRemove.c create mode 100644 src/backend/rewrite/rewriteRemove.h create mode 100644 src/backend/rewrite/rewriteSupport.c create mode 100644 src/backend/rewrite/rewriteSupport.h create mode 100644 src/backend/storage/Makefile.inc create mode 100644 src/backend/storage/backendid.h create mode 100644 src/backend/storage/block.h create mode 100644 src/backend/storage/buf.h create mode 100644 src/backend/storage/buf_internals.h create mode 100644 src/backend/storage/buffer/Makefile.inc create mode 100644 src/backend/storage/buffer/buf_init.c create mode 100644 src/backend/storage/buffer/buf_table.c create mode 100644 src/backend/storage/buffer/bufmgr.c create mode 100644 src/backend/storage/buffer/freelist.c create mode 100644 src/backend/storage/buffer/localbuf.c create mode 100644 src/backend/storage/bufmgr.h create mode 100644 src/backend/storage/bufpage.h create mode 100644 src/backend/storage/fd.h create mode 100644 src/backend/storage/file/Makefile.inc create mode 100644 src/backend/storage/file/fd.c create mode 100644 src/backend/storage/ipc.h create mode 100644 src/backend/storage/ipc/Makefile.inc create mode 100644 src/backend/storage/ipc/README create mode 100644 src/backend/storage/ipc/ipc.c create mode 100644 src/backend/storage/ipc/ipci.c create mode 100644 src/backend/storage/ipc/s_lock.c create mode 100644 src/backend/storage/ipc/shmem.c create mode 100644 src/backend/storage/ipc/shmqueue.c create mode 100644 src/backend/storage/ipc/sinval.c create mode 100644 src/backend/storage/ipc/sinvaladt.c create mode 100644 src/backend/storage/ipc/spin.c create mode 100644 src/backend/storage/item.h create mode 100644 src/backend/storage/itemid.h create mode 100644 src/backend/storage/itempos.h create mode 100644 src/backend/storage/itemptr.h create mode 100644 src/backend/storage/large_object.h create mode 100644 src/backend/storage/large_object/Makefile.inc create mode 100644 src/backend/storage/large_object/inv_api.c create mode 100644 src/backend/storage/lmgr.h create mode 100644 src/backend/storage/lmgr/Makefile.inc create mode 100644 src/backend/storage/lmgr/README create mode 100644 src/backend/storage/lmgr/lmgr.c create mode 100644 src/backend/storage/lmgr/lock.c create mode 100644 src/backend/storage/lmgr/multi.c create mode 100644 src/backend/storage/lmgr/proc.c create mode 100644 src/backend/storage/lmgr/single.c create mode 100644 src/backend/storage/lock.h create mode 100644 src/backend/storage/multilev.h create mode 100644 src/backend/storage/off.h create mode 100644 src/backend/storage/page.h create mode 100644 src/backend/storage/page/Makefile.inc create mode 100644 src/backend/storage/page/bufpage.c create mode 100644 src/backend/storage/page/itemptr.c create mode 100644 src/backend/storage/pagenum.h create mode 100644 src/backend/storage/pos.h create mode 100644 src/backend/storage/proc.h create mode 100644 src/backend/storage/shmem.h create mode 100644 src/backend/storage/sinval.h create mode 100644 src/backend/storage/sinvaladt.h create mode 100644 src/backend/storage/smgr.h create mode 100644 src/backend/storage/smgr/Makefile.inc create mode 100644 src/backend/storage/smgr/README create mode 100644 src/backend/storage/smgr/md.c create mode 100644 src/backend/storage/smgr/mm.c create mode 100644 src/backend/storage/smgr/smgr.c create mode 100644 src/backend/storage/smgr/smgrtype.c create mode 100644 src/backend/storage/spin.h create mode 100644 src/backend/tcop/Makefile.inc create mode 100644 src/backend/tcop/aclchk.c create mode 100644 src/backend/tcop/dest.c create mode 100644 src/backend/tcop/dest.h create mode 100644 src/backend/tcop/fastpath.c create mode 100644 src/backend/tcop/fastpath.h create mode 100644 src/backend/tcop/postgres.c create mode 100644 src/backend/tcop/pquery.c create mode 100644 src/backend/tcop/pquery.h create mode 100644 src/backend/tcop/tcopdebug.h create mode 100644 src/backend/tcop/tcopprot.h create mode 100644 src/backend/tcop/utility.c create mode 100644 src/backend/tcop/utility.h create mode 100644 src/backend/tioga/Arr_TgRecipe.h create mode 100644 src/backend/tioga/Makefile.inc create mode 100644 src/backend/tioga/Varray.c create mode 100644 src/backend/tioga/Varray.h create mode 100644 src/backend/tioga/tgRecipe.c create mode 100644 src/backend/tioga/tgRecipe.h create mode 100644 src/backend/utils/Gen_fmgrtab.sh create mode 100644 src/backend/utils/Makefile.inc create mode 100644 src/backend/utils/acl.h create mode 100644 src/backend/utils/adt/Makefile.inc create mode 100644 src/backend/utils/adt/acl.c create mode 100644 src/backend/utils/adt/arrayfuncs.c create mode 100644 src/backend/utils/adt/arrayutils.c create mode 100644 src/backend/utils/adt/bool.c create mode 100644 src/backend/utils/adt/char.c create mode 100644 src/backend/utils/adt/chunk.c create mode 100644 src/backend/utils/adt/date.c create mode 100644 src/backend/utils/adt/datetimes.c create mode 100644 src/backend/utils/adt/datum.c create mode 100644 src/backend/utils/adt/dt.c create mode 100644 src/backend/utils/adt/filename.c create mode 100644 src/backend/utils/adt/float.c create mode 100644 src/backend/utils/adt/geo-ops.c create mode 100644 src/backend/utils/adt/geo-selfuncs.c create mode 100644 src/backend/utils/adt/int.c create mode 100644 src/backend/utils/adt/like.c create mode 100644 src/backend/utils/adt/misc.c create mode 100644 src/backend/utils/adt/nabstime.c create mode 100644 src/backend/utils/adt/name.c create mode 100644 src/backend/utils/adt/not_in.c create mode 100644 src/backend/utils/adt/numutils.c create mode 100644 src/backend/utils/adt/oid.c create mode 100644 src/backend/utils/adt/oidint2.c create mode 100644 src/backend/utils/adt/oidint4.c create mode 100644 src/backend/utils/adt/oidname.c create mode 100644 src/backend/utils/adt/regexp.c create mode 100644 src/backend/utils/adt/regproc.c create mode 100644 src/backend/utils/adt/selfuncs.c create mode 100644 src/backend/utils/adt/sets.c create mode 100644 src/backend/utils/adt/tid.c create mode 100644 src/backend/utils/adt/varchar.c create mode 100644 src/backend/utils/adt/varlena.c create mode 100644 src/backend/utils/array.h create mode 100644 src/backend/utils/bit.h create mode 100644 src/backend/utils/builtins.h create mode 100644 src/backend/utils/cache/Makefile.inc create mode 100644 src/backend/utils/cache/catcache.c create mode 100644 src/backend/utils/cache/fcache.c create mode 100644 src/backend/utils/cache/inval.c create mode 100644 src/backend/utils/cache/lsyscache.c create mode 100644 src/backend/utils/cache/rel.c create mode 100644 src/backend/utils/cache/relcache.c create mode 100644 src/backend/utils/cache/syscache.c create mode 100644 src/backend/utils/catcache.h create mode 100644 src/backend/utils/datum.h create mode 100644 src/backend/utils/dynamic_loader.h create mode 100644 src/backend/utils/elog.h create mode 100644 src/backend/utils/error/Makefile.inc create mode 100644 src/backend/utils/error/assert.c create mode 100644 src/backend/utils/error/elog.c create mode 100644 src/backend/utils/error/exc.c create mode 100644 src/backend/utils/error/excabort.c create mode 100644 src/backend/utils/error/excid.c create mode 100644 src/backend/utils/error/format.c create mode 100644 src/backend/utils/exc.h create mode 100644 src/backend/utils/excid.h create mode 100644 src/backend/utils/fcache.h create mode 100644 src/backend/utils/fcache2.h create mode 100644 src/backend/utils/fmgr/Makefile.inc create mode 100644 src/backend/utils/fmgr/dfmgr.c create mode 100644 src/backend/utils/fmgr/fmgr.c create mode 100644 src/backend/utils/fmgrtab.h create mode 100644 src/backend/utils/geo-decls.h create mode 100644 src/backend/utils/hash/Makefile.inc create mode 100644 src/backend/utils/hash/dynahash.c create mode 100644 src/backend/utils/hash/hashfn.c create mode 100644 src/backend/utils/hsearch.h create mode 100644 src/backend/utils/init/Makefile.inc create mode 100644 src/backend/utils/init/enbl.c create mode 100644 src/backend/utils/init/findbe.c create mode 100644 src/backend/utils/init/globals.c create mode 100644 src/backend/utils/init/magic.c create mode 100644 src/backend/utils/init/miscinit.c create mode 100644 src/backend/utils/init/postinit.c create mode 100644 src/backend/utils/inval.h create mode 100644 src/backend/utils/lselect.h create mode 100644 src/backend/utils/lsyscache.h create mode 100644 src/backend/utils/mcxt.h create mode 100644 src/backend/utils/memutils.h create mode 100644 src/backend/utils/mmgr/Makefile.inc create mode 100644 src/backend/utils/mmgr/aset.c create mode 100644 src/backend/utils/mmgr/mcxt.c create mode 100644 src/backend/utils/mmgr/oset.c create mode 100644 src/backend/utils/mmgr/palloc.c create mode 100644 src/backend/utils/mmgr/portalmem.c create mode 100644 src/backend/utils/module.h create mode 100644 src/backend/utils/nabstime.h create mode 100644 src/backend/utils/oidcompos.h create mode 100644 src/backend/utils/palloc.h create mode 100644 src/backend/utils/portal.h create mode 100644 src/backend/utils/psort.h create mode 100644 src/backend/utils/rel.h create mode 100644 src/backend/utils/rel2.h create mode 100644 src/backend/utils/relcache.h create mode 100644 src/backend/utils/sets.h create mode 100644 src/backend/utils/sort/Makefile.inc create mode 100644 src/backend/utils/sort/lselect.c create mode 100644 src/backend/utils/sort/psort.c create mode 100644 src/backend/utils/syscache.h create mode 100644 src/backend/utils/time/Makefile.inc create mode 100644 src/backend/utils/time/tqual.c create mode 100644 src/backend/utils/tqual.h create mode 100644 src/bin/Makefile create mode 100644 src/bin/Makefile.global create mode 100644 src/bin/cleardbdir/Makefile create mode 100644 src/bin/cleardbdir/cleardbdir.sh create mode 100644 src/bin/createdb/Makefile create mode 100644 src/bin/createdb/createdb.sh create mode 100644 src/bin/createuser/Makefile create mode 100644 src/bin/createuser/createuser.sh create mode 100644 src/bin/destroydb/Makefile create mode 100644 src/bin/destroydb/destroydb.sh create mode 100644 src/bin/destroyuser/Makefile create mode 100644 src/bin/destroyuser/destroyuser.sh create mode 100644 src/bin/initdb/Makefile create mode 100644 src/bin/initdb/initdb.sh create mode 100644 src/bin/ipcclean/Makefile create mode 100644 src/bin/ipcclean/ipcclean.sh create mode 100644 src/bin/monitor/Makefile create mode 100644 src/bin/monitor/monitor.c create mode 100644 src/bin/pg4_dump/Makefile create mode 100644 src/bin/pg4_dump/README create mode 100644 src/bin/pg4_dump/common.c create mode 100644 src/bin/pg4_dump/pg4_dump.c create mode 100644 src/bin/pg4_dump/pg_dump.h create mode 100644 src/bin/pg_dump/Makefile create mode 100644 src/bin/pg_dump/README create mode 100644 src/bin/pg_dump/common.c create mode 100644 src/bin/pg_dump/pg_dump.c create mode 100644 src/bin/pg_dump/pg_dump.h create mode 100644 src/bin/pg_id/Makefile create mode 100644 src/bin/pg_id/pg_id.c create mode 100644 src/bin/pg_version/Makefile create mode 100644 src/bin/pg_version/pg_version.c create mode 100644 src/bin/pgtclsh/Makefile create mode 100644 src/bin/pgtclsh/README create mode 100644 src/bin/pgtclsh/pgtclAppInit.c create mode 100644 src/bin/pgtclsh/pgtclUtils.tcl create mode 100644 src/bin/pgtclsh/pgtkAppInit.c create mode 100644 src/bin/pgtclsh/updateStats.tcl create mode 100644 src/bin/psql/Makefile create mode 100644 src/bin/psql/psql.c create mode 100644 src/bin/psql/psqlHelp.h create mode 100644 src/bin/psql/rlstubs.c create mode 100644 src/bin/psql/stringutils.c create mode 100644 src/bin/psql/stringutils.h create mode 100644 src/interfaces/libpgtcl/Makefile create mode 100644 src/interfaces/libpgtcl/README create mode 100644 src/interfaces/libpgtcl/libpgtcl.h create mode 100644 src/interfaces/libpgtcl/pgtcl.c create mode 100644 src/interfaces/libpgtcl/pgtclCmds.c create mode 100644 src/interfaces/libpgtcl/pgtclCmds.h create mode 100644 src/interfaces/libpgtcl/pgtclId.c create mode 100644 src/interfaces/libpgtcl/pgtclId.h create mode 100644 src/interfaces/libpq++/Makefile create mode 100644 src/interfaces/libpq++/README create mode 100644 src/interfaces/libpq++/examples/Makefile create mode 100644 src/interfaces/libpq++/examples/testlibpq0.cc create mode 100644 src/interfaces/libpq++/examples/testlibpq1.cc create mode 100644 src/interfaces/libpq++/examples/testlibpq2.cc create mode 100644 src/interfaces/libpq++/examples/testlibpq2.sql create mode 100644 src/interfaces/libpq++/examples/testlibpq3.cc create mode 100644 src/interfaces/libpq++/examples/testlibpq3.sql create mode 100644 src/interfaces/libpq++/examples/testlibpq4.cc create mode 100644 src/interfaces/libpq++/examples/testlo.cc create mode 100644 src/interfaces/libpq++/libpq++.H create mode 100644 src/interfaces/libpq++/man/libpq++.3 create mode 100644 src/interfaces/libpq++/pgconnection.cc create mode 100644 src/interfaces/libpq++/pgenv.cc create mode 100644 src/interfaces/libpq++/pglobject.cc create mode 100644 src/interfaces/libpq/Makefile create mode 100644 src/interfaces/libpq/README create mode 100644 src/interfaces/libpq/fe-auth.c create mode 100644 src/interfaces/libpq/fe-auth.h create mode 100644 src/interfaces/libpq/fe-connect.c create mode 100644 src/interfaces/libpq/fe-exec.c create mode 100644 src/interfaces/libpq/fe-lobj.c create mode 100644 src/interfaces/libpq/fe-misc.c create mode 100644 src/interfaces/libpq/libpq-fe.h create mode 100644 src/interfaces/libpq/pg_hba create mode 100644 src/interfaces/libpq/pqsignal.c create mode 100644 src/interfaces/libpq/pqsignal.h create mode 100644 src/mk/port/postgres.mk.BSD44_derived create mode 100644 src/mk/port/postgres.mk.aix create mode 100644 src/mk/port/postgres.mk.alpha create mode 100644 src/mk/port/postgres.mk.bsdi create mode 100644 src/mk/port/postgres.mk.hpux create mode 100644 src/mk/port/postgres.mk.irix5 create mode 100644 src/mk/port/postgres.mk.linux create mode 100644 src/mk/port/postgres.mk.sparc create mode 100644 src/mk/port/postgres.mk.sparc_solaris create mode 100644 src/mk/port/postgres.mk.svr4 create mode 100644 src/mk/port/postgres.mk.ultrix4 create mode 100644 src/mk/postgres.lib.mk create mode 100644 src/mk/postgres.mk create mode 100644 src/mk/postgres.prog.mk create mode 100644 src/mk/postgres.shell.mk create mode 100644 src/mk/postgres.subdir.mk create mode 100644 src/mk/postgres.user.mk create mode 100644 src/test/Makefile create mode 100644 src/test/bench/Makefile create mode 100644 src/test/bench/WISC-README create mode 100755 src/test/bench/create.sh create mode 100644 src/test/bench/create.source create mode 100644 src/test/bench/perquery create mode 100644 src/test/bench/query01 create mode 100644 src/test/bench/query02 create mode 100644 src/test/bench/query03 create mode 100644 src/test/bench/query04 create mode 100644 src/test/bench/query05 create mode 100644 src/test/bench/query06 create mode 100644 src/test/bench/query07 create mode 100644 src/test/bench/query08 create mode 100644 src/test/bench/query09 create mode 100644 src/test/bench/query10 create mode 100644 src/test/bench/query11 create mode 100644 src/test/bench/query12 create mode 100644 src/test/bench/query13 create mode 100644 src/test/bench/query14 create mode 100644 src/test/bench/query15 create mode 100644 src/test/bench/query16 create mode 100644 src/test/bench/query17 create mode 100644 src/test/bench/query18 create mode 100644 src/test/bench/query19 create mode 100644 src/test/bench/query20 create mode 100644 src/test/bench/query21 create mode 100644 src/test/bench/query22 create mode 100644 src/test/bench/query23 create mode 100644 src/test/bench/query24 create mode 100644 src/test/bench/query25 create mode 100644 src/test/bench/query26 create mode 100644 src/test/bench/query27 create mode 100644 src/test/bench/query28 create mode 100644 src/test/bench/query29 create mode 100644 src/test/bench/query30 create mode 100644 src/test/bench/query31 create mode 100644 src/test/bench/query32 create mode 100755 src/test/bench/runwisc.sh create mode 100755 src/test/bench/wholebench.sh create mode 100644 src/test/examples/Makefile create mode 100644 src/test/examples/testlibpq.c create mode 100644 src/test/examples/testlibpq2.c create mode 100644 src/test/examples/testlibpq2.sql create mode 100644 src/test/examples/testlibpq3.c create mode 100644 src/test/examples/testlibpq3.sql create mode 100644 src/test/examples/testlibpq4.c create mode 100644 src/test/examples/testlo.c create mode 100644 src/test/examples/testlo2.c create mode 100644 src/test/regress/Makefile create mode 100644 src/test/regress/create.source create mode 100644 src/test/regress/data/dept.data create mode 100644 src/test/regress/data/desc.data create mode 100644 src/test/regress/data/emp.data create mode 100644 src/test/regress/data/hash.data create mode 100644 src/test/regress/data/onek.data create mode 100644 src/test/regress/data/person.data create mode 100644 src/test/regress/data/real_city.data create mode 100644 src/test/regress/data/rect.data create mode 100644 src/test/regress/data/streets.data create mode 100644 src/test/regress/data/stud_emp.data create mode 100644 src/test/regress/data/student.data create mode 100644 src/test/regress/data/tenk.data create mode 100644 src/test/regress/destroy.source create mode 100644 src/test/regress/errors.source create mode 100644 src/test/regress/queries.source create mode 100644 src/test/regress/regress.c create mode 100755 src/test/regress/regress.sh create mode 100644 src/test/regress/sample.regress.out create mode 100644 src/test/regress/security.source create mode 100644 src/test/suite/README create mode 100644 src/test/suite/agg.sql create mode 100644 src/test/suite/date.sql create mode 100644 src/test/suite/float.sql create mode 100644 src/test/suite/group.sql create mode 100644 src/test/suite/group_err.sql create mode 100644 src/test/suite/inh.sql create mode 100644 src/test/suite/join.sql create mode 100644 src/test/suite/oper.sql create mode 100644 src/test/suite/parse.sql create mode 100644 src/test/suite/quote.sql create mode 100644 src/test/suite/results/agg.sql.out create mode 100644 src/test/suite/results/date.sql.out create mode 100644 src/test/suite/results/float.sql.out create mode 100644 src/test/suite/results/group.sql.out create mode 100644 src/test/suite/results/group_err.sql.out create mode 100644 src/test/suite/results/inh.sql.out create mode 100644 src/test/suite/results/join.sql.out create mode 100644 src/test/suite/results/oper.sql.out create mode 100644 src/test/suite/results/parse.sql.out create mode 100644 src/test/suite/results/quote.sql.out create mode 100644 src/test/suite/results/rules.sql.out create mode 100644 src/test/suite/results/select.sql.out create mode 100644 src/test/suite/results/sort.sql.out create mode 100644 src/test/suite/results/sqlcompat.sql.out create mode 100644 src/test/suite/results/time.sql.out create mode 100644 src/test/suite/results/varchar.sql.out create mode 100644 src/test/suite/results/views.sql.out create mode 100644 src/test/suite/rules.sql create mode 100755 src/test/suite/runall create mode 100644 src/test/suite/select.sql create mode 100644 src/test/suite/sort.sql create mode 100644 src/test/suite/sqlcompat.sql create mode 100644 src/test/suite/time.sql create mode 100644 src/test/suite/varchar.sql create mode 100644 src/test/suite/views.sql create mode 100644 src/tools/mkldexport/Makefile create mode 100644 src/tools/mkldexport/README create mode 100644 src/tools/mkldexport/mkldexport.sh create mode 100644 src/tutorial/C-code/beard.c create mode 100644 src/tutorial/C-code/complex.c create mode 100644 src/tutorial/C-code/funcs.c create mode 100644 src/tutorial/Makefile create mode 100644 src/tutorial/README create mode 100644 src/tutorial/advanced.source create mode 100644 src/tutorial/basics.source create mode 100644 src/tutorial/complex.source create mode 100644 src/tutorial/funcs.source create mode 100644 src/tutorial/syscat.source diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000000..7e047c0cce --- /dev/null +++ b/src/Makefile @@ -0,0 +1,48 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Build and install postgres. +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/Makefile,v 1.1.1.1 1996/07/09 06:21:07 scrappy Exp $ +# +# 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 index 0000000000..1ecd62acce --- /dev/null +++ b/src/Makefile.global @@ -0,0 +1,306 @@ +#------------------------------------------------------------------------- +# +# Makefile.global-- +# global configuration for the Makefiles +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/Attic/Makefile.global,v 1.1.1.1 1996/07/09 06:21:07 scrappy Exp $ +# +# 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 index 0000000000..4cdc7adaf4 --- /dev/null +++ b/src/backend/Makefile @@ -0,0 +1,289 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for the postgres backend (and the postmaster) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/Makefile,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# 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 index 0000000000..6adc2c692b --- /dev/null +++ b/src/backend/access/Makefile.inc @@ -0,0 +1,35 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the access methods module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..7c999e58e9 --- /dev/null +++ b/src/backend/access/attnum.h @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- + * + * attnum.h-- + * POSTGRES attribute number definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: attnum.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5d5dd47627 --- /dev/null +++ b/src/backend/access/common/Makefile.inc @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/common +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/common/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..c3e72fb97e --- /dev/null +++ b/src/backend/access/common/heaptuple.c @@ -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: /cvsroot/pgsql/src/backend/access/common/heaptuple.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + * NOTES + * The old interface functions have been converted to macros + * and moved to heapam.h + * + *------------------------------------------------------------------------- + */ +#include + +#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; inatts; + + 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 index 0000000000..b80c5dd9eb --- /dev/null +++ b/src/backend/access/common/heapvalid.c @@ -0,0 +1,134 @@ +/*------------------------------------------------------------------------- + * + * heapvalid.c-- + * heap tuple qualification validity checking code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/heapvalid.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..be5d2ccbd9 --- /dev/null +++ b/src/backend/access/common/indextuple.c @@ -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: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..b437718cec --- /dev/null +++ b/src/backend/access/common/indexvalid.c @@ -0,0 +1,84 @@ +/*------------------------------------------------------------------------- + * + * indexvalid.c-- + * index tuple qualification validity checking code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/common/Attic/indexvalid.c,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..556b73b9df --- /dev/null +++ b/src/backend/access/common/printtup.c @@ -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: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include + +#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 index 0000000000..7a47219a73 --- /dev/null +++ b/src/backend/access/common/scankey.c @@ -0,0 +1,68 @@ +/*------------------------------------------------------------------------- + * + * scan.c-- + * scan direction and key code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..527eb5113d --- /dev/null +++ b/src/backend/access/common/tupdesc.c @@ -0,0 +1,398 @@ +/*------------------------------------------------------------------------- + * + * tupdesc.c-- + * POSTGRES tuple descriptor support code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + * NOTES + * some of the executor utility code such as "ExecTypeFromTL" should be + * moved here. + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#include + +#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;inatts;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 index 0000000000..4689df19c0 --- /dev/null +++ b/src/backend/access/funcindex.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + * + * funcindex.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: funcindex.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b2544650de --- /dev/null +++ b/src/backend/access/genam.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * genam.h-- + * POSTGRES general access method definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: genam.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..21407696b4 --- /dev/null +++ b/src/backend/access/hash.h @@ -0,0 +1,336 @@ +/*------------------------------------------------------------------------- + * + * hash.h-- + * header file for postgres hash access method implementation + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: hash.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + * 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 index 0000000000..8ea221bc26 --- /dev/null +++ b/src/backend/access/hash/Makefile.inc @@ -0,0 +1,18 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/hash (hash access method) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/hash/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..a4a4e16e59 --- /dev/null +++ b/src/backend/access/hash/hash.c @@ -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: /cvsroot/pgsql/src/backend/access/hash/hash.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + * 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 index 0000000000..6b37de2991 --- /dev/null +++ b/src/backend/access/hash/hashfunc.c @@ -0,0 +1,276 @@ +/*------------------------------------------------------------------------- + * + * hashfunc.c-- + * Comparison functions for hash access method. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashfunc.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + * 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 index 0000000000..c514cc614d --- /dev/null +++ b/src/backend/access/hash/hashinsert.c @@ -0,0 +1,239 @@ +/*------------------------------------------------------------------------- + * + * hashinsert.c-- + * Item insertion in hash tables for Postgres. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashinsert.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..55ee9e9ce7 --- /dev/null +++ b/src/backend/access/hash/hashovfl.c @@ -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: /cvsroot/pgsql/src/backend/access/hash/hashovfl.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + * 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 index 0000000000..2c6ebed835 --- /dev/null +++ b/src/backend/access/hash/hashpage.c @@ -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: /cvsroot/pgsql/src/backend/access/hash/hashpage.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + * 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 index 0000000000..c4cce0e70d --- /dev/null +++ b/src/backend/access/hash/hashscan.c @@ -0,0 +1,172 @@ +/*------------------------------------------------------------------------- + * + * hashscan.c-- + * manage scans on hash tables + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashscan.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + * 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 index 0000000000..056235dec8 --- /dev/null +++ b/src/backend/access/hash/hashsearch.c @@ -0,0 +1,425 @@ +/*------------------------------------------------------------------------- + * + * hashsearch.c-- + * search code for postgres hash tables + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashsearch.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..cac2a58690 --- /dev/null +++ b/src/backend/access/hash/hashstrat.c @@ -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: /cvsroot/pgsql/src/backend/access/hash/Attic/hashstrat.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f8f49fe798 --- /dev/null +++ b/src/backend/access/hash/hashutil.c @@ -0,0 +1,147 @@ +/*------------------------------------------------------------------------- + * + * btutils.c-- + * Utility code for Postgres btree implementation. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/hash/hashutil.c,v 1.1.1.1 1996/07/09 06:21:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f4f4bbb703 --- /dev/null +++ b/src/backend/access/heap/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/heap +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..4bf31efd83 --- /dev/null +++ b/src/backend/access/heap/heapam.c @@ -0,0 +1,1507 @@ +/*------------------------------------------------------------------------- + * + * heapam.c-- + * heap access method code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ + * + * + * 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 +#include + +#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 index 0000000000..457e1174a3 --- /dev/null +++ b/src/backend/access/heap/hio.c @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------- + * + * hio.c-- + * POSTGRES heap access method input/output code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Id: hio.c,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..d41d01ac1b --- /dev/null +++ b/src/backend/access/heap/stats.c @@ -0,0 +1,329 @@ +/*------------------------------------------------------------------------- + * + * stats.c-- + * heap access method debugging statistic collection routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/heap/Attic/stats.c,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ + * + * 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 index 0000000000..9938dbeea7 --- /dev/null +++ b/src/backend/access/heapam.h @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------- + * + * heapam.h-- + * POSTGRES heap access method definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: heapam.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef HEAPAM_H +#define HEAPAM_H + +#include + +#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 index 0000000000..4a699ffcd9 --- /dev/null +++ b/src/backend/access/hio.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * hio.h-- + * POSTGRES heap access method input/output definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: hio.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7cf1ecf176 --- /dev/null +++ b/src/backend/access/htup.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- + * + * htup.h-- + * POSTGRES heap tuple definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: htup.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..990c23ab4d --- /dev/null +++ b/src/backend/access/ibit.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * ibit.h-- + * POSTGRES index valid attribute bit map definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: ibit.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0bc58830c8 --- /dev/null +++ b/src/backend/access/index/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/index +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/index/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..3d02ba5700 --- /dev/null +++ b/src/backend/access/index/genam.c @@ -0,0 +1,275 @@ +/*------------------------------------------------------------------------- + * + * genam.c-- + * general index access method routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/index/genam.c,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ + * + * 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 index 0000000000..bffe3a41f3 --- /dev/null +++ b/src/backend/access/index/indexam.c @@ -0,0 +1,411 @@ +/*------------------------------------------------------------------------- + * + * indexam.c-- + * general index access method routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ + * + * 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 index 0000000000..602d2bd9e9 --- /dev/null +++ b/src/backend/access/index/istrat.c @@ -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: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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; attributeIndexindkey[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 index 0000000000..5fab98a15b --- /dev/null +++ b/src/backend/access/iqual.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * iqual.h-- + * Index scan key qualification definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: iqual.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..201e70e660 --- /dev/null +++ b/src/backend/access/istrat.h @@ -0,0 +1,80 @@ +/*------------------------------------------------------------------------- + * + * istrat.h-- + * POSTGRES index strategy definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: istrat.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..028bf430b0 --- /dev/null +++ b/src/backend/access/itup.h @@ -0,0 +1,104 @@ +/*------------------------------------------------------------------------- + * + * itup.h-- + * POSTGRES index tuple definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: itup.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d5c37a2395 --- /dev/null +++ b/src/backend/access/nbtree.h @@ -0,0 +1,264 @@ +/*------------------------------------------------------------------------- + * + * nbtree.h-- + * header file for postgres btree access method implementation. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nbtree.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..50854008c0 --- /dev/null +++ b/src/backend/access/nbtree/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/nbtree (btree acess methods) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..a204ad4af0 --- /dev/null +++ b/src/backend/access/nbtree/README @@ -0,0 +1,68 @@ +$Header: /cvsroot/pgsql/src/backend/access/nbtree/README,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + +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 index 0000000000..e567b3c44c --- /dev/null +++ b/src/backend/access/nbtree/nbtcompare.c @@ -0,0 +1,173 @@ +/*------------------------------------------------------------------------- + * + * btcompare.c-- + * Comparison functions for btree access method. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtcompare.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + * 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 +#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 index 0000000000..536c0aa385 --- /dev/null +++ b/src/backend/access/nbtree/nbtinsert.c @@ -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: /cvsroot/pgsql/src/backend/access/nbtree/nbtinsert.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ce411a80d1 --- /dev/null +++ b/src/backend/access/nbtree/nbtpage.c @@ -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: /cvsroot/pgsql/src/backend/access/nbtree/nbtpage.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + * 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 index 0000000000..0601611996 --- /dev/null +++ b/src/backend/access/nbtree/nbtree.c @@ -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: /cvsroot/pgsql/src/backend/access/nbtree/nbtree.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + * 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 index 0000000000..62a029bc06 --- /dev/null +++ b/src/backend/access/nbtree/nbtscan.c @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- + * + * btscan.c-- + * manage scans on btrees. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtscan.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + * + * 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 index 0000000000..d7a7fc7d62 --- /dev/null +++ b/src/backend/access/nbtree/nbtsearch.c @@ -0,0 +1,1133 @@ +/*------------------------------------------------------------------------- + * + * btsearch.c-- + * search code for postgres btrees. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 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 index 0000000000..3d2676324a --- /dev/null +++ b/src/backend/access/nbtree/nbtsort.c @@ -0,0 +1,1196 @@ +/*------------------------------------------------------------------------- + * btsort.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Id: nbtsort.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + * 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 + +#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 index 0000000000..2214c60950 --- /dev/null +++ b/src/backend/access/nbtree/nbtstrat.c @@ -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: /cvsroot/pgsql/src/backend/access/nbtree/Attic/nbtstrat.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..695a2b637c --- /dev/null +++ b/src/backend/access/nbtree/nbtutils.c @@ -0,0 +1,239 @@ +/*------------------------------------------------------------------------- + * + * btutils.c-- + * Utility code for Postgres btree implementation. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/nbtree/nbtutils.c,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..b5843daf7e --- /dev/null +++ b/src/backend/access/printtup.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * printtup.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: printtup.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7899e9d945 --- /dev/null +++ b/src/backend/access/relscan.h @@ -0,0 +1,87 @@ +/*------------------------------------------------------------------------- + * + * relscan.h-- + * POSTGRES internal relation scan descriptor definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: relscan.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..79f1622e48 --- /dev/null +++ b/src/backend/access/rtree.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------- + * + * rtree.h-- + * common declarations for the rtree access method code. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rtree.h,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a93a5e5329 --- /dev/null +++ b/src/backend/access/rtree/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/access/rtree/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..fb2e169297 --- /dev/null +++ b/src/backend/access/rtree/rtget.c @@ -0,0 +1,320 @@ +/*------------------------------------------------------------------------- + * + * rtget.c-- + * fetch tuples from an rtree scan. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtget.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a2f7bef46b --- /dev/null +++ b/src/backend/access/rtree/rtproc.c @@ -0,0 +1,150 @@ +/*------------------------------------------------------------------------- + * + * rtproc.c-- + * pg_amproc entries for rtrees. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtproc.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..96efc3bc90 --- /dev/null +++ b/src/backend/access/rtree/rtree.c @@ -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: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtree.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..aa68f0db70 --- /dev/null +++ b/src/backend/access/rtree/rtscan.c @@ -0,0 +1,392 @@ +/*------------------------------------------------------------------------- + * + * rtscan.c-- + * routines to manage scans on index relations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtscan.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..c5d934a22a --- /dev/null +++ b/src/backend/access/rtree/rtstrat.c @@ -0,0 +1,239 @@ +/*------------------------------------------------------------------------- + * + * rtstrat.c-- + * strategy map data for rtrees. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/rtree/Attic/rtstrat.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a928303f3f --- /dev/null +++ b/src/backend/access/rtscan.h @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------- + * + * rtscan.h-- + * routines defined in access/rtree/rtscan.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rtscan.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5b439e7b33 --- /dev/null +++ b/src/backend/access/rtstrat.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * rtstrat.h-- + * routines defined in access/rtree/rtstrat.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rtstrat.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..030007d39c --- /dev/null +++ b/src/backend/access/sdir.h @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------- + * + * sdir.h-- + * POSTGRES scan direction definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: sdir.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..3cadf348f4 --- /dev/null +++ b/src/backend/access/skey.h @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * skey.h-- + * POSTGRES scan key definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: skey.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + * + * 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 index 0000000000..4ddb2190d8 --- /dev/null +++ b/src/backend/access/strat.h @@ -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: strat.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0f5a9724dc --- /dev/null +++ b/src/backend/access/transam.h @@ -0,0 +1,213 @@ +/*------------------------------------------------------------------------- + * + * transam.h-- + * postgres transaction access method support code header + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: transam.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + * 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 index 0000000000..c4f5b95a0a --- /dev/null +++ b/src/backend/access/transam/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/transam +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..b3789a8c2c --- /dev/null +++ b/src/backend/access/transam/transam.c @@ -0,0 +1,675 @@ +/*------------------------------------------------------------------------- + * + * transam.c-- + * postgres transaction log/time interface routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + * 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 index 0000000000..a1e5b17ec1 --- /dev/null +++ b/src/backend/access/transam/transsup.c @@ -0,0 +1,663 @@ +/*------------------------------------------------------------------------- + * + * transsup.c-- + * postgres transaction access method support code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + * 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 index 0000000000..a53cc7d35b --- /dev/null +++ b/src/backend/access/transam/varsup.c @@ -0,0 +1,606 @@ +/*------------------------------------------------------------------------- + * + * varsup.c-- + * postgres variable relation support routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..1798d09d05 --- /dev/null +++ b/src/backend/access/transam/xact.c @@ -0,0 +1,1314 @@ +/*------------------------------------------------------------------------- + * + * xact.c-- + * top level transaction system support routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + * 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 index 0000000000..faeeb623d5 --- /dev/null +++ b/src/backend/access/transam/xid.c @@ -0,0 +1,156 @@ +/*------------------------------------------------------------------------- + * + * xid.c-- + * POSTGRES transaction identifier code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ + * + * 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 +#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 index 0000000000..a26bbc704d --- /dev/null +++ b/src/backend/access/tupdesc.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- + * + * tupdesc.h-- + * POSTGRES tuple descriptor definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tupdesc.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9a9bcce3b4 --- /dev/null +++ b/src/backend/access/tupmacs.h @@ -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: tupmacs.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1c5cf8cdeb --- /dev/null +++ b/src/backend/access/valid.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------- + * + * valid.h-- + * POSTGRES tuple qualification validity definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: valid.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..15f376ec5e --- /dev/null +++ b/src/backend/access/xact.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- + * + * xact.h-- + * postgres transaction system header + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: xact.h,v 1.1.1.1 1996/07/09 06:21:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef XACT_H +#define XACT_H + +#include + +#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 index 0000000000..72871343e8 --- /dev/null +++ b/src/backend/bootstrap/Makefile.inc @@ -0,0 +1,63 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the bootstrap module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/bootstrap/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ +# +# +# 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 index 0000000000..8ec71025ce --- /dev/null +++ b/src/backend/bootstrap/boot.sed @@ -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: /cvsroot/pgsql/src/backend/bootstrap/Attic/boot.sed,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ +# +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 index 0000000000..0362b302b1 --- /dev/null +++ b/src/backend/bootstrap/bootparse.y @@ -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: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 arg_list +%type index_params index_on +%type const ident +%type optbootstrap optoideq tuple tuplelist + +%token 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 index 0000000000..9dbd92cb93 --- /dev/null +++ b/src/backend/bootstrap/bootscanner.l @@ -0,0 +1,108 @@ +%{ +/*------------------------------------------------------------------------- + * + * bootscanner.lex-- + * a lexical scanner for the bootstrap parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootscanner.l,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e2df755109 --- /dev/null +++ b/src/backend/bootstrap/bootstrap.c @@ -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: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include "libpq/pqsignal.h" /* substitute for */ +#if defined(PORTNAME_linux) +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#endif /* defined(PORTNAME_linux) */ +#include + +#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 */ +#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)(); + + + +/* ---------------------------------------------------------------- + * 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; + } +} + + +/* ---------------- + * DEFINEATTR() + * + * define a 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= 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; istrnum); + } 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 index 0000000000..8ade7664f1 --- /dev/null +++ b/src/backend/bootstrap/bootstrap.h @@ -0,0 +1,78 @@ +/*------------------------------------------------------------------------- + * + * bootstrap.h-- + * include file for the bootstrapping code + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: bootstrap.h,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef BOOTSTRAP_H +#define BOOTSTRAP_H + +#include +#include +#include +#include +#include + +#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 index 0000000000..b29a0bfad2 --- /dev/null +++ b/src/backend/catalog/Makefile.inc @@ -0,0 +1,69 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the system catalogs module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/catalog/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..5bfc359e38 --- /dev/null +++ b/src/backend/catalog/README @@ -0,0 +1,66 @@ +$Header: /cvsroot/pgsql/src/backend/catalog/README,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + +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 index 0000000000..25588c0f88 --- /dev/null +++ b/src/backend/catalog/catalog.c @@ -0,0 +1,205 @@ +/*------------------------------------------------------------------------- + * + * catalog.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* 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 index 0000000000..9a54e833b1 --- /dev/null +++ b/src/backend/catalog/catalog.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * catalog.h-- + * prototypes for functions in lib/catalog/catalog.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: catalog.h,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..8d96541949 --- /dev/null +++ b/src/backend/catalog/catname.h @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * catname.h-- + * POSTGRES system catalog relation name definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: catname.h,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2f7e4025b8 --- /dev/null +++ b/src/backend/catalog/genbki.sh @@ -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: /cvsroot/pgsql/src/backend/catalog/Attic/genbki.sh,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ +# +# 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 /* for sprintf() */ +#include +#include + +#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; inum; 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;inum;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 index 0000000000..edcd5bf5ed --- /dev/null +++ b/src/backend/catalog/heap.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * heap.h-- + * prototypes for functions in lib/catalog/heap.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: heap.h,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b04010bf95 --- /dev/null +++ b/src/backend/catalog/index.c @@ -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: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + * + * 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 index 0000000000..1734f866a0 --- /dev/null +++ b/src/backend/catalog/index.h @@ -0,0 +1,59 @@ +/*------------------------------------------------------------------------- + * + * index.h-- + * prototypes for index.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: index.h,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..74bf48a443 --- /dev/null +++ b/src/backend/catalog/indexing.c @@ -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: /cvsroot/pgsql/src/backend/catalog/indexing.c,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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; ird_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 index 0000000000..c1a83cbaf3 --- /dev/null +++ b/src/backend/catalog/indexing.h @@ -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: indexing.h,v 1.1.1.1 1996/07/09 06:21:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7fe895e0f0 --- /dev/null +++ b/src/backend/catalog/pg_aggregate.c @@ -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: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..7ed983506b --- /dev/null +++ b/src/backend/catalog/pg_aggregate.h @@ -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: pg_aggregate.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..0f36e7c433 --- /dev/null +++ b/src/backend/catalog/pg_am.h @@ -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: pg_am.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..e9d12127aa --- /dev/null +++ b/src/backend/catalog/pg_amop.h @@ -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: pg_amop.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..cacc2b7270 --- /dev/null +++ b/src/backend/catalog/pg_amproc.h @@ -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: pg_amproc.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..d8133177d5 --- /dev/null +++ b/src/backend/catalog/pg_attribute.h @@ -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: pg_attribute.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..b1adb68be4 --- /dev/null +++ b/src/backend/catalog/pg_class.h @@ -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: pg_class.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..78a657e8d3 --- /dev/null +++ b/src/backend/catalog/pg_database.h @@ -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: pg_database.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..66efb7b3d4 --- /dev/null +++ b/src/backend/catalog/pg_defaults.h @@ -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: pg_defaults.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..1089f57152 --- /dev/null +++ b/src/backend/catalog/pg_demon.h @@ -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: pg_demon.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..76d51bec4b --- /dev/null +++ b/src/backend/catalog/pg_group.h @@ -0,0 +1,42 @@ +/*------------------------------------------------------------------------- + * + * pg_group.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pg_group.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..3924c264d0 --- /dev/null +++ b/src/backend/catalog/pg_hosts.h @@ -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: pg_hosts.h,v 1.1.1.1 1996/07/09 06:21:16 scrappy Exp $ + * + * 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 index 0000000000..da75b025bc --- /dev/null +++ b/src/backend/catalog/pg_index.h @@ -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: pg_index.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..1527e99286 --- /dev/null +++ b/src/backend/catalog/pg_inheritproc.h @@ -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: pg_inheritproc.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..1caa1cd017 --- /dev/null +++ b/src/backend/catalog/pg_inherits.h @@ -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: pg_inherits.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..df90cd42ce --- /dev/null +++ b/src/backend/catalog/pg_ipl.h @@ -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: pg_ipl.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..7e5a31af7a --- /dev/null +++ b/src/backend/catalog/pg_language.h @@ -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: pg_language.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..05e077ec53 --- /dev/null +++ b/src/backend/catalog/pg_listener.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * pg_listener.h-- + * Asynchronous notification + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pg_listener.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..987825a776 --- /dev/null +++ b/src/backend/catalog/pg_log.h @@ -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: pg_log.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..c5e0d98491 --- /dev/null +++ b/src/backend/catalog/pg_magic.h @@ -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: pg_magic.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..46aecd35c6 --- /dev/null +++ b/src/backend/catalog/pg_opclass.h @@ -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: pg_opclass.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..2784297129 --- /dev/null +++ b/src/backend/catalog/pg_operator.c @@ -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: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * NOTES + * these routines moved here from commands/define.c and somewhat cleaned up. + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..9f9533b2ff --- /dev/null +++ b/src/backend/catalog/pg_operator.h @@ -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: pg_operator.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..aa08827845 --- /dev/null +++ b/src/backend/catalog/pg_parg.h @@ -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: pg_parg.h,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + * 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 index 0000000000..d8273efcce --- /dev/null +++ b/src/backend/catalog/pg_proc.c @@ -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: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.1.1.1 1996/07/09 06:21:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..f282839419 --- /dev/null +++ b/src/backend/catalog/pg_proc.h @@ -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: pg_proc.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..9f20074627 --- /dev/null +++ b/src/backend/catalog/pg_rewrite.h @@ -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: pg_rewrite.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..6305238195 --- /dev/null +++ b/src/backend/catalog/pg_server.h @@ -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: pg_server.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..d8f0c19dff --- /dev/null +++ b/src/backend/catalog/pg_statistic.h @@ -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: pg_statistic.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..4990f231ba --- /dev/null +++ b/src/backend/catalog/pg_time.h @@ -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: pg_time.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..fe9baa05a4 --- /dev/null +++ b/src/backend/catalog/pg_type.c @@ -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: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..dc3fe94e8a --- /dev/null +++ b/src/backend/catalog/pg_type.h @@ -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: pg_type.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..25fd02cc0c --- /dev/null +++ b/src/backend/catalog/pg_user.h @@ -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: pg_user.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..d38a118574 --- /dev/null +++ b/src/backend/catalog/pg_variable.h @@ -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: pg_variable.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..fea795bd49 --- /dev/null +++ b/src/backend/catalog/pg_version.h @@ -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: pg_version.h,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ + * + * 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 index 0000000000..9608204f49 --- /dev/null +++ b/src/backend/catalog/unused_oids @@ -0,0 +1,41 @@ +#!/bin/sh +# unused_oids +# +# $Header: /cvsroot/pgsql/src/backend/catalog/Attic/unused_oids,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ +# +# 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 index 0000000000..d05052dfcc --- /dev/null +++ b/src/backend/commands/Makefile.inc @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the commands module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/commands/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:18 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..6dd311cee7 --- /dev/null +++ b/src/backend/commands/_deadcode/version.c @@ -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: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $ + * + * NOTES + * At the point the version is defined, 2 physical relations are created + * _added and _deleted. + * + * In addition, 4 rules are defined which govern the semantics of versions + * w.r.t retrieves, appends, replaces and deletes. + * + *------------------------------------------------------------------------- + */ +#include + +#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 + * _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 _added relation. + * 2. Retrieve all tuples in the base relation which are not in + * the _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 _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 _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 _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 _del relation. + * 2.2 A copy of the tuple is appended to the _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 index 0000000000..2d3064fa47 --- /dev/null +++ b/src/backend/commands/async.c @@ -0,0 +1,605 @@ +/*------------------------------------------------------------------------- + * + * async.c-- + * Asynchronous notification + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* New Async Notification Model: + * 1. Multiple backends on same machine. Multiple backends listening on + * one relation. + * + * 2. One of the backend does a 'notify '. 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 +#include +#include +#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 ' 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 ' 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 ' 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 ' + * 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 index 0000000000..65e4bd69d5 --- /dev/null +++ b/src/backend/commands/async.h @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------- + * + * async.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: async.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..8400832d6a --- /dev/null +++ b/src/backend/commands/cluster.c @@ -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: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..2194e13f9a --- /dev/null +++ b/src/backend/commands/cluster.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * cluster.h-- + * header file for postgres cluster command stuff + * + * Copyright (c) 1994-5, Regents of the University of California + * + * $Id: cluster.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4283b594d5 --- /dev/null +++ b/src/backend/commands/command.c @@ -0,0 +1,511 @@ +/*------------------------------------------------------------------------- + * + * command.c-- + * random postgres portal and utility support code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + * 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 +#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 index 0000000000..266c6b4be1 --- /dev/null +++ b/src/backend/commands/command.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * command.h-- + * prototypes for command.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: command.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7e10818abf --- /dev/null +++ b/src/backend/commands/copy.c @@ -0,0 +1,782 @@ +/*------------------------------------------------------------------------- + * + * copy.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include /* for mode_t */ +#include /* 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 index 0000000000..ccd2955562 --- /dev/null +++ b/src/backend/commands/copy.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * copy.h-- + * Definitions for using the POSTGRES copy command. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: copy.h,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a0e3a9f682 --- /dev/null +++ b/src/backend/commands/creatinh.c @@ -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: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.1.1.1 1996/07/09 06:21:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#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 index 0000000000..a86fd4ed82 --- /dev/null +++ b/src/backend/commands/creatinh.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * creatinh.h-- + * prototypes for creatinh.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: creatinh.h,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..da797e23cb --- /dev/null +++ b/src/backend/commands/defind.c @@ -0,0 +1,505 @@ +/*------------------------------------------------------------------------- + * + * defind.c-- + * POSTGRES define, extend and remove index code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/defind.c,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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; iindkey[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; iindkey[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 index 0000000000..4ba38c793c --- /dev/null +++ b/src/backend/commands/define.c @@ -0,0 +1,564 @@ +/*------------------------------------------------------------------------- + * + * define.c-- + * POSTGRES "define" utility code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $ + * + * 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 +#include +#include + +#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 index 0000000000..3658dc5008 --- /dev/null +++ b/src/backend/commands/defrem.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- + * + * defrem.h-- + * POSTGRES define and remove utility definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: defrem.h,v 1.1.1.1 1996/07/09 06:21:20 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a37f0f9cf4 --- /dev/null +++ b/src/backend/commands/explain.c @@ -0,0 +1,219 @@ +/*------------------------------------------------------------------------- + * + * explain.c-- + * Explain the query execution plan + * + * Copyright (c) 1994-5, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e0848bb771 --- /dev/null +++ b/src/backend/commands/explain.h @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------- + * + * explain.h-- + * prototypes for explain.c + * + * Copyright (c) 1994-5, Regents of the University of California + * + * $Id: explain.h,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b8b8317ab9 --- /dev/null +++ b/src/backend/commands/purge.c @@ -0,0 +1,168 @@ +/*------------------------------------------------------------------------- + * + * purge.c-- + * the POSTGRES purge command. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/purge.c,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $ + * + * 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 index 0000000000..2017418288 --- /dev/null +++ b/src/backend/commands/purge.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * purge.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: purge.h,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..97d0df6d37 --- /dev/null +++ b/src/backend/commands/recipe.c @@ -0,0 +1,1181 @@ +/*------------------------------------------------------------------------- + * + * recipe.c-- + * routines for handling execution of Tioga recipes + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +#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;ival[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;ieyes->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; tnum;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;iinNodes->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;jinTypes->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;ilen > 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;iinNodes->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;ilen;i++) + newq->qtrees[i] = q1->qtrees[i]; + for (j=0;jlen;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;inum;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;inum;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 index 0000000000..62fcc314a3 --- /dev/null +++ b/src/backend/commands/recipe.h @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------- + * + * recipe.h-- + * recipe handling routines + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: recipe.h,v 1.1.1.1 1996/07/09 06:21:21 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..95830c6cc0 --- /dev/null +++ b/src/backend/commands/remove.c @@ -0,0 +1,435 @@ +/*------------------------------------------------------------------------- + * + * remove.c-- + * POSTGRES remove (function | type | operator ) utilty code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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; iname; */ + 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 index 0000000000..83dc8944ea --- /dev/null +++ b/src/backend/commands/rename.c @@ -0,0 +1,275 @@ +/*------------------------------------------------------------------------- + * + * rename.c-- + * renameatt() and renamerel() reside here. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..c3889e12f8 --- /dev/null +++ b/src/backend/commands/rename.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * rename.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rename.h,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7e1514cd2a --- /dev/null +++ b/src/backend/commands/vacuum.c @@ -0,0 +1,853 @@ +/*------------------------------------------------------------------------- + * + * vacuum.c-- + * the postgres vacuum cleaner + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..f5994d7d6d --- /dev/null +++ b/src/backend/commands/vacuum.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * vacuum.h-- + * header file for postgres vacuum cleaner + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: vacuum.h,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..20d49d2c0c --- /dev/null +++ b/src/backend/commands/version.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * version.h-- + * Header file for versions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: version.h,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f6023ca08d --- /dev/null +++ b/src/backend/commands/view.c @@ -0,0 +1,325 @@ +/*------------------------------------------------------------------------- + * + * view.c-- + * use rewrite rules to construct views + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.1.1.1 1996/07/09 06:21:22 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* 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 index 0000000000..1515123771 --- /dev/null +++ b/src/backend/commands/view.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * view.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: view.h,v 1.1.1.1 1996/07/09 06:21:23 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..211e725cec --- /dev/null +++ b/src/backend/executor/Makefile.inc @@ -0,0 +1,29 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the executor module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/executor/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..08d3e70d8b --- /dev/null +++ b/src/backend/executor/execAmi.c @@ -0,0 +1,439 @@ +/*------------------------------------------------------------------------- + * + * execAmi.c-- + * miscellanious executor access method routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* 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; iiterexpr; + + /* + * 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 index 0000000000..fe06823619 --- /dev/null +++ b/src/backend/executor/execFlatten.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * execFlatten.h-- + * prototypes for execFlatten.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: execFlatten.h,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7ee1543299 --- /dev/null +++ b/src/backend/executor/execJunk.c @@ -0,0 +1,389 @@ +/*------------------------------------------------------------------------- + * + * junk.c-- + * Junk attribute support stuff.... + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.1.1.1 1996/07/09 06:21:24 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 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 index 0000000000..07aac34acc --- /dev/null +++ b/src/backend/executor/execMain.c @@ -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: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..11a6f63a77 --- /dev/null +++ b/src/backend/executor/execProcnode.c @@ -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: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..104c1f2f50 --- /dev/null +++ b/src/backend/executor/execQual.c @@ -0,0 +1,1504 @@ +/*------------------------------------------------------------------------- + * + * execQual.c-- + * Routines to evaluate qualification and targetlist expressions + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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;inatts;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: + * + * (( expr) ( 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 index 0000000000..96dd555128 --- /dev/null +++ b/src/backend/executor/execScan.c @@ -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: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#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 index 0000000000..8e7b5283dc --- /dev/null +++ b/src/backend/executor/execTuples.c @@ -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: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..8d1108aca2 --- /dev/null +++ b/src/backend/executor/execUtils.c @@ -0,0 +1,1092 @@ +/*------------------------------------------------------------------------- + * + * execUtils.c-- + * miscellanious executor utility routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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; iindkey[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; iii_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; iii_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; iii_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 index 0000000000..b5200ca257 --- /dev/null +++ b/src/backend/executor/execdebug.h @@ -0,0 +1,377 @@ +/*------------------------------------------------------------------------- + * + * execdebug.h-- + * #defines governing debugging behaviour in the executor + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: execdebug.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5aec485c95 --- /dev/null +++ b/src/backend/executor/execdefs.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------- + * + * execdefs.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: execdefs.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..54752625f5 --- /dev/null +++ b/src/backend/executor/execdesc.h @@ -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: execdesc.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..65caf098f1 --- /dev/null +++ b/src/backend/executor/executor.h @@ -0,0 +1,229 @@ +/*------------------------------------------------------------------------- + * + * executor.h-- + * support for the POSTGRES executor module + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: executor.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef EXECUTOR_H +#define EXECUTOR_H + +/* ---------------------------------------------------------------- + * #includes + * ---------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..2f6e29d827 --- /dev/null +++ b/src/backend/executor/functions.c @@ -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: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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; ikind = 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 index 0000000000..1a1a88b36a --- /dev/null +++ b/src/backend/executor/functions.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * functions.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: functions.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e7ae086fe1 --- /dev/null +++ b/src/backend/executor/hashjoin.h @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------- + * + * hashjoin.h-- + * internal structures for hash table and buckets + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: hashjoin.h,v 1.1.1.1 1996/07/09 06:21:25 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ee187367c7 --- /dev/null +++ b/src/backend/executor/nodeAgg.c @@ -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;ixfn1) { + 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 index 0000000000..51c2b2b227 --- /dev/null +++ b/src/backend/executor/nodeAgg.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * nodeAgg.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeAgg.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0a6cd5d01b --- /dev/null +++ b/src/backend/executor/nodeAppend.c @@ -0,0 +1,483 @@ +/*------------------------------------------------------------------------- + * + * nodeAppend.c-- + * routines to handle append nodes. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* 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 index 0000000000..fd2cdbbe81 --- /dev/null +++ b/src/backend/executor/nodeAppend.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * nodeAppend.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeAppend.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e9b0847b5f --- /dev/null +++ b/src/backend/executor/nodeGroup.c @@ -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: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..067028ea8e --- /dev/null +++ b/src/backend/executor/nodeGroup.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * nodeGroup.h-- + * prototypes for nodeGroup.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeGroup.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..55a5e1f027 --- /dev/null +++ b/src/backend/executor/nodeHash.c @@ -0,0 +1,828 @@ +/*------------------------------------------------------------------------- + * + * nodeHash.c-- + * Routines to hash relations for hashjoin + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * ExecHash - generate an in-memory hash table of the relation + * ExecInitHash - initialize node and subnodes.. + * ExecEndHash - shutdown node and subnodes + * + */ + +#include /* for sprintf() */ +#include +#include +#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; ihashBatches = 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; ibatch)+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; iouterbatchNames = 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; iinnerbatchNames = 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; itop = 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; inbuckets; 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 index 0000000000..cec479dbb0 --- /dev/null +++ b/src/backend/executor/nodeHash.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * nodeHash.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeHash.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7ed4c141b9 --- /dev/null +++ b/src/backend/executor/nodeHashjoin.c @@ -0,0 +1,792 @@ +/*------------------------------------------------------------------------- + * + * nodeHashjoin.c-- + * Routines to handle hash join nodes + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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; ihj_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; ibatch) + 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 index 0000000000..b8c12942b3 --- /dev/null +++ b/src/backend/executor/nodeHashjoin.h @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------- + * + * nodeHashjoin.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeHashjoin.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..758fabdefe --- /dev/null +++ b/src/backend/executor/nodeIndexscan.c @@ -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: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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; icstate.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 */ + ¤tRelation, /* return: rel desc */ + (Pointer *) ¤tScanDesc); /* 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 index 0000000000..27bbff0a29 --- /dev/null +++ b/src/backend/executor/nodeIndexscan.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * nodeIndexscan.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeIndexscan.h,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..88fc93d2d4 --- /dev/null +++ b/src/backend/executor/nodeMaterial.c @@ -0,0 +1,392 @@ +/*------------------------------------------------------------------------- + * + * nodeMaterial.c-- + * Routines to handle materialization nodes. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.1.1.1 1996/07/09 06:21:26 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..d85b025b7b --- /dev/null +++ b/src/backend/executor/nodeMaterial.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * nodeMaterial.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeMaterial.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..54a3aa28c4 --- /dev/null +++ b/src/backend/executor/nodeMergejoin.c @@ -0,0 +1,1194 @@ +/*------------------------------------------------------------------------- + * + * nodeMergejoin.c-- + * routines supporting merge joins + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..ebdca08e32 --- /dev/null +++ b/src/backend/executor/nodeMergejoin.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * nodeMergejoin.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeMergejoin.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1ef30ce431 --- /dev/null +++ b/src/backend/executor/nodeNestloop.c @@ -0,0 +1,370 @@ +/*------------------------------------------------------------------------- + * + * nodeNestloop.c-- + * routines to support nest-loop joins + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..c227c90a73 --- /dev/null +++ b/src/backend/executor/nodeNestloop.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * nodeNestloop.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeNestloop.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..793119244d --- /dev/null +++ b/src/backend/executor/nodeResult.c @@ -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: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..a2ab286c08 --- /dev/null +++ b/src/backend/executor/nodeResult.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * nodeResult.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeResult.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a0e434c4bb --- /dev/null +++ b/src/backend/executor/nodeSeqscan.c @@ -0,0 +1,449 @@ +/*------------------------------------------------------------------------- + * + * nodeSeqscan.c-- + * Support routines for sequential scans of relations. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 */ + ¤tRelation, /* return: rel desc */ + (Pointer *) ¤tScanDesc); /* 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 index 0000000000..cce029d40b --- /dev/null +++ b/src/backend/executor/nodeSeqscan.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * nodeSeqscan.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeSeqscan.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9a7f4f9456 --- /dev/null +++ b/src/backend/executor/nodeSort.c @@ -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: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..504b8a1f19 --- /dev/null +++ b/src/backend/executor/nodeSort.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * nodeSort.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeSort.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5be700b9ae --- /dev/null +++ b/src/backend/executor/nodeTee.c @@ -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: /cvsroot/pgsql/src/backend/executor/Attic/nodeTee.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#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 index 0000000000..aa50efdead --- /dev/null +++ b/src/backend/executor/nodeTee.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * nodeTee.h-- + * support functions for a Tee executor node + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeTee.h,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..8be0bd8497 --- /dev/null +++ b/src/backend/executor/nodeUnique.c @@ -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: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.1.1.1 1996/07/09 06:21:27 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..a8dfc9bd6b --- /dev/null +++ b/src/backend/executor/nodeUnique.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * nodeUnique.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeUnique.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..33f7de3358 --- /dev/null +++ b/src/backend/executor/tuptable.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * tuptable.h-- + * tuple table support stuff + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tuptable.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + * 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 index 0000000000..b27b9fb009 --- /dev/null +++ b/src/backend/include/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/include/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..3b4ea8b207 --- /dev/null +++ b/src/backend/include/c.h @@ -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: c.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#include +#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 index 0000000000..85c5699205 --- /dev/null +++ b/src/backend/include/miscadmin.h @@ -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: miscadmin.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + * NOTES + * some of the information in this file will be moved to + * other files. + * + *------------------------------------------------------------------------- + */ +#ifndef MISCADMIN_H +#define MISCADMIN_H + +/* ---------------- + * note: was in unix.h This should be moved + * to the .c files. + * ---------------- + */ +#include + +#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 index 0000000000..429a25e4ff --- /dev/null +++ b/src/backend/include/postgres.h @@ -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: postgres.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..c3a46d546c --- /dev/null +++ b/src/backend/lib/Makefile.inc @@ -0,0 +1,20 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the lib module (miscellaneous stuff) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/lib/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..9aa12b4850 --- /dev/null +++ b/src/backend/lib/bit.c @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * bit.c-- + * Standard bit array code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/bit.c,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +/* + * 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 index 0000000000..92526632c9 --- /dev/null +++ b/src/backend/lib/dllist.c @@ -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: /cvsroot/pgsql/src/backend/lib/dllist.c,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..cd9ac42a12 --- /dev/null +++ b/src/backend/lib/dllist.h @@ -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: dllist.h,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..541767183d --- /dev/null +++ b/src/backend/lib/fstack.c @@ -0,0 +1,153 @@ +/*------------------------------------------------------------------------- + * + * fstack.c-- + * Fixed format stack definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/fstack.c,v 1.1.1.1 1996/07/09 06:21:28 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b0b1df00d8 --- /dev/null +++ b/src/backend/lib/fstack.h @@ -0,0 +1,113 @@ +/*------------------------------------------------------------------------- + * + * fstack.h-- + * Fixed format stack definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fstack.h,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..5487bed44b --- /dev/null +++ b/src/backend/lib/hasht.c @@ -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: /cvsroot/pgsql/src/backend/lib/Attic/hasht.c,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..543c8c95d8 --- /dev/null +++ b/src/backend/lib/hasht.h @@ -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: hasht.h,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0cf49bf0c0 --- /dev/null +++ b/src/backend/lib/lispsort.c @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * lispsort.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/lispsort.c,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e49ee54362 --- /dev/null +++ b/src/backend/lib/lispsort.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * lispsort.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lispsort.h,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5af64bf800 --- /dev/null +++ b/src/backend/lib/qsort.c @@ -0,0 +1,281 @@ +/*------------------------------------------------------------------------- + * + * qsort.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/lib/Attic/qsort.c,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/*- + * 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 index 0000000000..d81d4e2e07 --- /dev/null +++ b/src/backend/lib/qsort.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * qsort.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: qsort.h,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef QSORT_H +#define QSORT_H + +#include + +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 index 0000000000..7167ffcf65 --- /dev/null +++ b/src/backend/lib/stringinfo.c @@ -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: /cvsroot/pgsql/src/backend/lib/stringinfo.c,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..717f2ad598 --- /dev/null +++ b/src/backend/lib/stringinfo.h @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------- + * + * stringinfo.h-- + * Declarations/definitons for "string" functions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: stringinfo.h,v 1.1.1.1 1996/07/09 06:21:29 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..67052518d3 --- /dev/null +++ b/src/backend/libpq/Makefile.inc @@ -0,0 +1,26 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the (backend side) libpq module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/libpq/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# 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 index 0000000000..7b4437736f --- /dev/null +++ b/src/backend/libpq/auth.c @@ -0,0 +1,668 @@ +/*------------------------------------------------------------------------- + * + * auth.c-- + * Routines to handle network authentication + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 post_hackers@postgres.Berkeley.EDU 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 +#include +#include /* for MAX{HOSTNAME,PATH}LEN, NOFILE */ +#include +#include /* isspace() declaration */ + +#include +#include +#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. Aoki@CS.BERKELEY.EDU + * 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 + * (phil@eecs.nwu.edu) - orginal configuration file code handling + * by Sam Horrocks (sam@ics.uci.edu) + * + * modified and adapted for use with Postgres95 by Paul Fisher + * (pnfisher@unity.ncsu.edu) + */ + +#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 "" + * enables a service, whereas + * -a "no" + * 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 index 0000000000..adda8dc13c --- /dev/null +++ b/src/backend/libpq/auth.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * auth.h-- + * Definitions for network authentication routines + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: auth.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..fb6b90c149 --- /dev/null +++ b/src/backend/libpq/be-dumpdata.c @@ -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: /cvsroot/pgsql/src/backend/libpq/Attic/be-dumpdata.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..e32cd3b474 --- /dev/null +++ b/src/backend/libpq/be-fsstubs.c @@ -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: /cvsroot/pgsql/src/backend/libpq/be-fsstubs.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + * 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 index 0000000000..3929f42a69 --- /dev/null +++ b/src/backend/libpq/be-fsstubs.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * be-fsstubs.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: be-fsstubs.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1b1738d4fc --- /dev/null +++ b/src/backend/libpq/be-pqexec.c @@ -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: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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) + 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 +#include + +#include /* for O_ on some */ +#ifndef WIN32 +#include /* for SEEK_ on most */ +#endif /* WIN32 */ +#ifndef SEEK_SET +#include /* 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 index 0000000000..5fafbb148d --- /dev/null +++ b/src/backend/libpq/libpq.h @@ -0,0 +1,261 @@ +/*------------------------------------------------------------------------- + * + * libpq.h-- + * POSTGRES LIBPQ buffer structure definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: libpq.h,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + * 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 +#include +#include +#ifdef WIN32 +#include +#else +#include +#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 index 0000000000..ca27fd8308 --- /dev/null +++ b/src/backend/libpq/portal.c @@ -0,0 +1,783 @@ +/*------------------------------------------------------------------------- + * + * portal.c-- + * generalized portal support routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/portal.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* for sprintf() */ +#include + +#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 index 0000000000..f927e268ed --- /dev/null +++ b/src/backend/libpq/portalbuf.c @@ -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: /cvsroot/pgsql/src/backend/libpq/Attic/portalbuf.c,v 1.1.1.1 1996/07/09 06:21:30 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#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;irule_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 index 0000000000..7fc4a85f20 --- /dev/null +++ b/src/backend/libpq/pqcomm.c @@ -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: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 */ +#include +#include +#ifndef WIN32 +#include /* for ttyname() */ +#include +#include +#include +#include +#else +#include +#endif /* WIN32 */ +#include +#include + +#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 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 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 index 0000000000..a7870871ea --- /dev/null +++ b/src/backend/libpq/pqcomm.h @@ -0,0 +1,124 @@ +/*------------------------------------------------------------------------- + * + * pqcomm.h-- + * Parameters for the communication module + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pqcomm.h,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + * NOTES + * Some of this should move to libpq.h + * + *------------------------------------------------------------------------- + */ +#ifndef PQCOMM_H +#define PQCOMM_H + +#include +#ifdef WIN32 +#include +#else +#include +#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 index 0000000000..edf373d1af --- /dev/null +++ b/src/backend/libpq/pqpacket.c @@ -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: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* 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 +#include +#ifndef WIN32 +#include +#include +#include +#else +#include +#endif /*WIN32 */ +#include +#include + +#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 index 0000000000..b60a5659cc --- /dev/null +++ b/src/backend/libpq/pqsignal.c @@ -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: /cvsroot/pgsql/src/backend/libpq/pqsignal.c,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + * 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 index 0000000000..44f10882f2 --- /dev/null +++ b/src/backend/libpq/pqsignal.h @@ -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: pqsignal.h,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#ifndef PQSIGNAL_H +#define PQSIGNAL_H + +#include + +#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 index 0000000000..1e66ad03ca --- /dev/null +++ b/src/backend/main/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/main/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:31 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..0c8dbc478d --- /dev/null +++ b/src/backend/main/main.c @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * main.c-- + * Stub main() routine for the postgres backend. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/main/main.c,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..d1c97c9488 --- /dev/null +++ b/src/backend/makeID @@ -0,0 +1,17 @@ +#!/bin/sh +# +# $Header: /cvsroot/pgsql/src/backend/Attic/makeID,v 1.1.1.1 1996/07/09 06:21:08 scrappy Exp $ +# + + +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 index 0000000000..a32fdbad6d --- /dev/null +++ b/src/backend/nodes/Makefile.inc @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the nodes module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/nodes/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ +# +# 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 index 0000000000..59db30ceae --- /dev/null +++ b/src/backend/nodes/README @@ -0,0 +1,65 @@ +******************************************************************************* +* * +* EXPLANATION OF THE NODE STRUCTURES * +* - Andrew Yu (11/94) * +* * +* Copyright (c) 1994, Regents of the University of California * +* * +* $Id: README,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ +* * +******************************************************************************* + +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 index 0000000000..c04bf3627e --- /dev/null +++ b/src/backend/nodes/copyfuncs.c @@ -0,0 +1,1675 @@ +/*------------------------------------------------------------------------- + * + * copyfuncs.c-- + * Copy functions for Postgres tree nodes. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..d2dbef2bea --- /dev/null +++ b/src/backend/nodes/equalfuncs.c @@ -0,0 +1,703 @@ +/*------------------------------------------------------------------------- + * + * equalfuncs.c-- + * equal functions to compare the nodes + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..6fb093a2e2 --- /dev/null +++ b/src/backend/nodes/execnodes.h @@ -0,0 +1,689 @@ +/*------------------------------------------------------------------------- + * + * execnodes.h-- + * definitions for executor state nodes + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: execnodes.h,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..20617747c2 --- /dev/null +++ b/src/backend/nodes/list.c @@ -0,0 +1,438 @@ +/*------------------------------------------------------------------------- + * + * list.c-- + * various list handling routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + * 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 +#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 index 0000000000..a461524e7a --- /dev/null +++ b/src/backend/nodes/makefuncs.c @@ -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: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + * 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 index 0000000000..4c6b029167 --- /dev/null +++ b/src/backend/nodes/makefuncs.h @@ -0,0 +1,48 @@ +/*------------------------------------------------------------------------- + * + * makefuncs.h-- + * prototypes for the creator functions (for primitive nodes) + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: makefuncs.h,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..35adee0d9c --- /dev/null +++ b/src/backend/nodes/memnodes.h @@ -0,0 +1,101 @@ +/*------------------------------------------------------------------------- + * + * memnodes.h-- + * POSTGRES memory context node definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: memnodes.h,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + * 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 index 0000000000..c28e5da979 --- /dev/null +++ b/src/backend/nodes/nodeFuncs.c @@ -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: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..c725f25177 --- /dev/null +++ b/src/backend/nodes/nodeFuncs.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * nodeFuncs.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodeFuncs.h,v 1.1.1.1 1996/07/09 06:21:32 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..82845cca15 --- /dev/null +++ b/src/backend/nodes/nodes.c @@ -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: /cvsroot/pgsql/src/backend/nodes/nodes.c,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + * HISTORY + * Andrew Yu Oct 20, 1994 file creation + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..7fa9fdb5a9 --- /dev/null +++ b/src/backend/nodes/nodes.h @@ -0,0 +1,299 @@ +/*------------------------------------------------------------------------- + * + * nodes.h-- + * Definitions for tagged nodes. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nodes.h,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5281d52691 --- /dev/null +++ b/src/backend/nodes/outfuncs.c @@ -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: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + * 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 +#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; ifj_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; iiterexpr); +} + +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 index 0000000000..57ee1a023c --- /dev/null +++ b/src/backend/nodes/params.h @@ -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: params.h,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..bc994bb1a0 --- /dev/null +++ b/src/backend/nodes/parsenodes.h @@ -0,0 +1,731 @@ +/*------------------------------------------------------------------------- + * + * parsenodes.h-- + * definitions for parse tree nodes + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parsenodes.h,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 + * (( expr) ( 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 index 0000000000..83eaa5f93a --- /dev/null +++ b/src/backend/nodes/pg_list.h @@ -0,0 +1,112 @@ +/*------------------------------------------------------------------------- + * + * pg_list.h-- + * POSTGRES generic list package + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pg_list.h,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PG_LIST_H +#define PG_LIST_H + +#include +#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 index 0000000000..a01a93c774 --- /dev/null +++ b/src/backend/nodes/plannodes.h @@ -0,0 +1,330 @@ +/*------------------------------------------------------------------------- + * + * plannodes.h-- + * definitions for query plan nodes + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: plannodes.h,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7ceb9c6101 --- /dev/null +++ b/src/backend/nodes/primnodes.h @@ -0,0 +1,318 @@ +/*------------------------------------------------------------------------- + * + * primnodes.h-- + * Definitions for parse tree/query tree ("primitive") nodes. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: primnodes.h,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0297b445b1 --- /dev/null +++ b/src/backend/nodes/print.c @@ -0,0 +1,377 @@ +/*------------------------------------------------------------------------- + * + * print.c-- + * various print routines (used mostly for debugging) + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + * HISTORY + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Oct 26, 1994 file creation + * + *------------------------------------------------------------------------- + */ +#include +#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; jrelname,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;icost, 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 index 0000000000..dbcc59a6f9 --- /dev/null +++ b/src/backend/nodes/read.c @@ -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: /cvsroot/pgsql/src/backend/nodes/read.c,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + * HISTORY + * AUTHOR DATE MAJOR EVENT + * Andrew Yu Nov 2, 1994 file creation + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#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 index 0000000000..fc909fe7aa --- /dev/null +++ b/src/backend/nodes/readfuncs.c @@ -0,0 +1,1948 @@ +/*------------------------------------------------------------------------- + * + * readfuncs.c-- + * Reader functions for Postgres tree nodes. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.1.1.1 1996/07/09 06:21:33 scrappy Exp $ + * + * 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 +#include +#include + +#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= 1) { + s = (char*)palloc(length); + Assert( s!=NULL ); + for (i=0; iinactive=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 index 0000000000..beda9a67de --- /dev/null +++ b/src/backend/optimizer/joininfo.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * joininfo.h-- + * prototypes for joininfo.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: joininfo.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ac579089f5 --- /dev/null +++ b/src/backend/optimizer/keys.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * keys.h-- + * prototypes for keys.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: keys.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0b598fb71a --- /dev/null +++ b/src/backend/optimizer/ordering.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * ordering.h-- + * prototypes for ordering.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: ordering.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..6bb014b0a9 --- /dev/null +++ b/src/backend/optimizer/path/Makefile.inc @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for optimizer/path +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..0ed2139095 --- /dev/null +++ b/src/backend/optimizer/path/allpaths.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..634e113079 --- /dev/null +++ b/src/backend/optimizer/path/clausesel.c @@ -0,0 +1,331 @@ +/*------------------------------------------------------------------------- + * + * clausesel.c-- + * Routines to compute and set clause selectivities + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..14bbb5f8e4 --- /dev/null +++ b/src/backend/optimizer/path/costsize.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#ifdef WIN32 +#include +#include +#define MAXINT INT_MAX +#else +# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi) +# include +# define MAXINT INT_MAX +# else +# include +# 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 index 0000000000..cdbd9b6d90 --- /dev/null +++ b/src/backend/optimizer/path/hashutils.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..844571847f --- /dev/null +++ b/src/backend/optimizer/path/indxpath.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..e727388715 --- /dev/null +++ b/src/backend/optimizer/path/joinpath.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..b26e3364f9 --- /dev/null +++ b/src/backend/optimizer/path/joinrels.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1be5a57f2e --- /dev/null +++ b/src/backend/optimizer/path/joinutils.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d5f0fdcb65 --- /dev/null +++ b/src/backend/optimizer/path/mergeutils.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e040675e6e --- /dev/null +++ b/src/backend/optimizer/path/orindxpath.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2d3b5c5767 --- /dev/null +++ b/src/backend/optimizer/path/predmig.c @@ -0,0 +1,773 @@ +/*------------------------------------------------------------------------- + * + * predmig.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* +** 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 index 0000000000..70f9b209e0 --- /dev/null +++ b/src/backend/optimizer/path/prune.c @@ -0,0 +1,203 @@ +/*------------------------------------------------------------------------- + * + * prune.c-- + * Routines to prune redundant paths and relations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..405b3b77f0 --- /dev/null +++ b/src/backend/optimizer/path/xfunc.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef WIN32 +#include /* for MAXFLOAT on most systems */ +#else +#include +#define MAXFLOAT DBL_MAX +#endif /* WIN32 */ + +#include /* for MAXFLOAT on SunOS */ +#include + +#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 index 0000000000..0617600d4e --- /dev/null +++ b/src/backend/optimizer/pathnode.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + * + * pathnode.h-- + * prototypes for pathnode.c, indexnode.c, relnode.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pathnode.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..62468041cf --- /dev/null +++ b/src/backend/optimizer/paths.h @@ -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: paths.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..eccd412e9f --- /dev/null +++ b/src/backend/optimizer/plan/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for optimizer/plan +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/optimizer/plan/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..70a5e6461b --- /dev/null +++ b/src/backend/optimizer/plan/createplan.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..fc80402a05 --- /dev/null +++ b/src/backend/optimizer/plan/initsplan.c @@ -0,0 +1,391 @@ +/*------------------------------------------------------------------------- + * + * initsplan.c-- + * Target list, qualification, joininfo initialization routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5740b83a2d --- /dev/null +++ b/src/backend/optimizer/plan/planmain.c @@ -0,0 +1,422 @@ +/*------------------------------------------------------------------------- + * + * planmain.c-- + * Routines to plan a single query + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4e57af8aa2 --- /dev/null +++ b/src/backend/optimizer/plan/planner.c @@ -0,0 +1,408 @@ +/*------------------------------------------------------------------------- + * + * planner.c-- + * The query optimizer external interface. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e698c930e1 --- /dev/null +++ b/src/backend/optimizer/plan/setrefs.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..426778577d --- /dev/null +++ b/src/backend/optimizer/plancat.h @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------- + * + * plancat.h-- + * prototypes for plancat.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: plancat.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b224e89550 --- /dev/null +++ b/src/backend/optimizer/planmain.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * planmain.h-- + * prototypes for various files in optimizer/plan + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: planmain.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5f04949480 --- /dev/null +++ b/src/backend/optimizer/planner.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * planner.h-- + * prototypes for planner.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: planner.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..679097641f --- /dev/null +++ b/src/backend/optimizer/prep.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * prep.h-- + * prototypes for files in prep.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: prep.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..40026716c9 --- /dev/null +++ b/src/backend/optimizer/prep/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for optimizer/prep +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..0303eca70f --- /dev/null +++ b/src/backend/optimizer/prep/archive.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * archive.c-- + * Support for planning scans on archived relations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Attic/archive.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include /* 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 index 0000000000..e1aafa7db1 --- /dev/null +++ b/src/backend/optimizer/prep/prepqual.c @@ -0,0 +1,582 @@ +/*------------------------------------------------------------------------- + * + * prepqual.c-- + * Routines for preprocessing the parse tree qualification + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..fe1c2c92ba --- /dev/null +++ b/src/backend/optimizer/prep/preptlist.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..8e1491940d --- /dev/null +++ b/src/backend/optimizer/prep/prepunion.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..8906460de9 --- /dev/null +++ b/src/backend/optimizer/tlist.h @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + * + * tlist.h-- + * prototypes for tlist.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tlist.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..18955d282c --- /dev/null +++ b/src/backend/optimizer/util/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for optimizer/util +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..1ab747ee17 --- /dev/null +++ b/src/backend/optimizer/util/clauseinfo.c @@ -0,0 +1,187 @@ +/*------------------------------------------------------------------------- + * + * clauseinfo.c-- + * ClauseInfo node manipulation routines. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/clauseinfo.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..daba4d8fdb --- /dev/null +++ b/src/backend/optimizer/util/clauses.c @@ -0,0 +1,736 @@ +/*------------------------------------------------------------------------- + * + * clauses.c-- + * routines to manipulate qualification clauses + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + * 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 index 0000000000..7fd7488920 --- /dev/null +++ b/src/backend/optimizer/util/indexnode.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/util/Attic/indexnode.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1db22f2b94 --- /dev/null +++ b/src/backend/optimizer/util/internal.c @@ -0,0 +1,61 @@ +/*------------------------------------------------------------------------- + * + * internal.c-- + * Definitions required throughout the query optimizer. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/internal.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +/* + * ---------- 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 index 0000000000..85416db8b3 --- /dev/null +++ b/src/backend/optimizer/util/joininfo.c @@ -0,0 +1,107 @@ +/*------------------------------------------------------------------------- + * + * joininfo.c-- + * JoinInfo node manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ac0915b909 --- /dev/null +++ b/src/backend/optimizer/util/keys.c @@ -0,0 +1,193 @@ +/*------------------------------------------------------------------------- + * + * keys.c-- + * Key manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/keys.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..3dffbff9f3 --- /dev/null +++ b/src/backend/optimizer/util/ordering.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/util/Attic/ordering.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..728ac9b422 --- /dev/null +++ b/src/backend/optimizer/util/pathnode.c @@ -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: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..4dca017f95 --- /dev/null +++ b/src/backend/optimizer/util/plancat.c @@ -0,0 +1,582 @@ +/*------------------------------------------------------------------------- + * + * plancat.c-- + * routines for accessing the system catalogs + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..351fb18210 --- /dev/null +++ b/src/backend/optimizer/util/relnode.c @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------- + * + * relnode.c-- + * Relation manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..073c2a0823 --- /dev/null +++ b/src/backend/optimizer/util/tlist.c @@ -0,0 +1,577 @@ +/*------------------------------------------------------------------------- + * + * tlist.c-- + * Target list manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b4f8436c77 --- /dev/null +++ b/src/backend/optimizer/util/var.c @@ -0,0 +1,189 @@ +/*------------------------------------------------------------------------- + * + * var.c-- + * Var node manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..fdcf1ea647 --- /dev/null +++ b/src/backend/optimizer/var.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * var.h-- + * prototypes for var.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: var.h,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a3ee1b99cc --- /dev/null +++ b/src/backend/optimizer/xfunc.h @@ -0,0 +1,84 @@ +/*------------------------------------------------------------------------- + * + * xfunc.h-- + * prototypes for xfunc.c and predmig.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: xfunc.h,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..38607f6fb0 --- /dev/null +++ b/src/backend/parser/Makefile.inc @@ -0,0 +1,46 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the parser module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/parser/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..504e557abe --- /dev/null +++ b/src/backend/parser/analyze.c @@ -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: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#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 = ""; + 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; + iresdom = 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)), + "", + 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 index 0000000000..a4fc775c45 --- /dev/null +++ b/src/backend/parser/catalog_utils.c @@ -0,0 +1,1470 @@ +/*------------------------------------------------------------------------- + * + * catalog_utils.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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; iargs[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; inext) { + 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, + ¤t_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 +#include + +#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 index 0000000000..a2811493c7 --- /dev/null +++ b/src/backend/parser/dbcommands.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * dbcommands.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: dbcommands.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..19529d1fa2 --- /dev/null +++ b/src/backend/parser/gram.y @@ -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: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + * 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 +#include +#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 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 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 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 privileges, operation_commalist, grantee +%type operation + +%type 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 opt_inh_star, opt_binary, opt_instead + +%type copy_dirn, archive_type, OptArchiveType, OptArchiveLocation, + def_type, opt_direction, remove_type, opt_column, event + +%type OptLocation, opt_move_where, fetch_how_many + +%type def_rest +%type purge_quals +%type insert_rest + +%type Typename, typname +%type columnDef +%type def_elem +%type def_arg, columnElem, exprElem, where_clause, + a_expr, AexprConst, having_clause, groupby +%type NumConst +%type event_object, attr +%type sortby +%type index_elem, func_index +%type from_val +%type relation_expr +%type time_range +%type res_target_el, res_target_el2 +%type ParamNo + +%type Iconst +%type Sconst +%type 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 IDENT, SCONST, Op +%token ICONST, PARAM +%token 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 [*] + * + *****************************************************************************/ + +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 + * + *****************************************************************************/ + +ClosePortalStmt: CLOSE opt_id + { + ClosePortalStmt *n = makeNode(ClosePortalStmt); + n->portalname = $2; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * QUERY : + * COPY [BINARY] FROM/TO + * [USING DELIMITERS ] + * + *****************************************************************************/ + +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 [, .. ] + * + *****************************************************************************/ + +DestroyStmt: DROP TABLE relation_name_list + { + DestroyStmt *n = makeNode(DestroyStmt); + n->relNames = $3; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * + * QUERY: + * fetch [forward | backward] [number | all ] [ in ] + * + *****************************************************************************/ + +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 [] [] [] + * + *****************************************************************************/ + +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 on + * using "(" ( with )+ ")" [with + * ] + * + * [where ] 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 [where ] + * + *****************************************************************************/ + +ExtendStmt: EXTEND INDEX index_name where_clause + { + ExtendStmt *n = makeNode(ExtendStmt); + n->idxname = $3; + n->whereClause = $4; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * QUERY: + * execute recipe + * + *****************************************************************************/ + +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 + * (language = , returntype = + * [, arch_pct = ] + * [, disk_pct = ] + * [, byte_pct = ] + * [, perbyte_cpu = ] + * [, percall_cpu = ] + * [, iscachable]) + * [arg is ( { , })] + * as + * + *****************************************************************************/ + +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 [before ] [after ] + * or + * purge [after][before ] + * + *****************************************************************************/ + +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 + * (REMOVE FUNCTION "funcname" (arg1, arg2, ...)) + * remove operator + * (REMOVE OPERATOR "opname" (leftoperand_typ rightoperand_typ)) + * remove type + * (REMOVE TYPE "typename") + * remove rule + * (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 in [*] to + * rename to + * + *****************************************************************************/ + +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 + * + * 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 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 '('target-list ')' [where ] + * + *****************************************************************************/ + +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 on + * + *****************************************************************************/ + +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 index 0000000000..b6cd549bf9 --- /dev/null +++ b/src/backend/parser/keywords.c @@ -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: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#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 index 0000000000..d26d76fbae --- /dev/null +++ b/src/backend/parser/keywords.h @@ -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: keywords.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..37c955017e --- /dev/null +++ b/src/backend/parser/parse_query.c @@ -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: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#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 index 0000000000..d9541c56cd --- /dev/null +++ b/src/backend/parser/parse_query.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * parse_query.h-- + * prototypes for parse_query.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_query.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..6ea7219e6b --- /dev/null +++ b/src/backend/parser/parse_state.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * parse_state.h-- + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_state.h,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..e0dae90758 --- /dev/null +++ b/src/backend/parser/parser.c @@ -0,0 +1,449 @@ +/*------------------------------------------------------------------------- + * + * ylib.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.1.1.1 1996/07/09 06:21:40 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#ifndef WIN32 +#include +#endif /*WIN32 */ +#include /* 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 index 0000000000..37a9f4a176 --- /dev/null +++ b/src/backend/parser/parsetree.h @@ -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: parsetree.h,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d3b3b9a3f2 --- /dev/null +++ b/src/backend/parser/scan.l @@ -0,0 +1,255 @@ +%{ +/*------------------------------------------------------------------------- + * + * scan.l-- + * lexical scanner for POSTGRES + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/scan.l,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#ifndef WIN32 +#include +#endif /* WIN32 */ +#ifndef __linux__ +#include +#else +#include +#endif /* __linux__ */ +#include + +#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 index 0000000000..bd7ef26004 --- /dev/null +++ b/src/backend/parser/scansup.c @@ -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: /cvsroot/pgsql/src/backend/parser/scansup.c,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#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 index 0000000000..95e625aabc --- /dev/null +++ b/src/backend/parser/scansup.h @@ -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: scansup.h,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +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 index 0000000000..9ef7ffc463 --- /dev/null +++ b/src/backend/port/BSD44_derived/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/BSD44_derived/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..acfd1e66af --- /dev/null +++ b/src/backend/port/BSD44_derived/README @@ -0,0 +1,4 @@ +The NetBSD port was done by Alistair G. Crooks (agc@uts.amdahl.com). + +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 index 0000000000..394fb8a2e0 --- /dev/null +++ b/src/backend/port/BSD44_derived/dl.c @@ -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 +#include +#include + +#include +#include + +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 index 0000000000..f1348bf9ea --- /dev/null +++ b/src/backend/port/BSD44_derived/float.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * float.h-- + * definitions for ANSI floating point + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: float.h,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ + * + * 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 index 0000000000..3d4e765660 --- /dev/null +++ b/src/backend/port/BSD44_derived/machine.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d397b4d3ed --- /dev/null +++ b/src/backend/port/BSD44_derived/port-protos.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * port-specific prototypes for NetBSD 1.0 + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PORT_PROTOS_H +#define PORT_PROTOS_H + +#include +#include +#include + +#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 index 0000000000..a21fd46968 --- /dev/null +++ b/src/backend/port/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..6954174c7d --- /dev/null +++ b/src/backend/port/aix/Makefile.inc @@ -0,0 +1,40 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for port/aix (AIX specific stuff) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/port/aix/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# 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 index 0000000000..f64446d49b --- /dev/null +++ b/src/backend/port/aix/README.dlfcn @@ -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: jum@helios.de + +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 (kirk@rsinc.com) 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 index 0000000000..9ae113ce06 --- /dev/null +++ b/src/backend/port/aix/dlfcn.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000000..5671e9caa3 --- /dev/null +++ b/src/backend/port/aix/dlfcn.h @@ -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 index 0000000000..e05a134649 --- /dev/null +++ b/src/backend/port/aix/machine.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..378baf9262 --- /dev/null +++ b/src/backend/port/aix/mkldexport.sh @@ -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 index 0000000000..986e5bd7d4 --- /dev/null +++ b/src/backend/port/aix/port-protos.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * port-specific prototypes for AIX + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:41 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..52ebfe486f --- /dev/null +++ b/src/backend/port/alpha/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/alpha/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..3d4e765660 --- /dev/null +++ b/src/backend/port/alpha/machine.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..3bd8d454a5 --- /dev/null +++ b/src/backend/port/alpha/port-protos.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * prototypes for OSF/1-specific routines + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#ifndef PORT_PROTOS_H +#define PORT_PROTOS_H + +#include +#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 index 0000000000..d7c17b0a5b --- /dev/null +++ b/src/backend/port/alpha/port.c @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * port.c-- + * OSF/1-specific routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/port/alpha/Attic/port.c,v 1.1.1.1 1996/07/09 06:21:42 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#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 index 0000000000..96eb2c5fc4 --- /dev/null +++ b/src/backend/port/bsdi/Makefile.inc @@ -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, lidl@pix.com +# +#------------------------------------------------------------------------- + +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 index 0000000000..c167a01897 --- /dev/null +++ b/src/backend/port/bsdi/dynloader.c @@ -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 +#include +#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 index 0000000000..d53defbfd4 --- /dev/null +++ b/src/backend/port/bsdi/machine.h @@ -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 index 0000000000..6583571d35 --- /dev/null +++ b/src/backend/port/bsdi/port-protos.h @@ -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 index 0000000000..8819b1a648 --- /dev/null +++ b/src/backend/port/bsdi/port.c @@ -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 index 0000000000..4ff60d2019 --- /dev/null +++ b/src/backend/port/hpux/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/hpux/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:43 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# 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 index 0000000000..deea2e1dc2 --- /dev/null +++ b/src/backend/port/hpux/dynloader.c @@ -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: /cvsroot/pgsql/src/backend/port/hpux/Attic/dynloader.c,v 1.1.1.1 1996/07/09 06:21:43 scrappy Exp $ + * + * NOTES + * all functions are defined here -- it's impossible to trace the + * shl_* routines from the bundled HP-UX debugger. + * + *------------------------------------------------------------------------- + */ +/* System includes */ +#include +#include +#include +#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 index 0000000000..62324eb05d --- /dev/null +++ b/src/backend/port/hpux/fixade.h @@ -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: fixade.h,v 1.1.1.1 1996/07/09 06:21:43 scrappy Exp $ + * + * 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 index 0000000000..b178a62172 --- /dev/null +++ b/src/backend/port/hpux/machine.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:43 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..16206bb250 --- /dev/null +++ b/src/backend/port/hpux/port-protos.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * port-specific prototypes for HP-UX + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:43 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PORT_PROTOS_H +#define PORT_PROTOS_H + +#include /* for struct rusage */ +#include /* 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 index 0000000000..eccf3dc5c1 --- /dev/null +++ b/src/backend/port/hpux/port.c @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------- + * + * port.c-- + * port-specific routines for HP-UX + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/port/hpux/Attic/port.c,v 1.1.1.1 1996/07/09 06:21:43 scrappy Exp $ + * + * NOTES + * For the most part, this file gets around some non-POSIX calls + * in POSTGRES. + * + *------------------------------------------------------------------------- + */ +#include /* for rand()/srand() prototypes */ +#include /* for pow() prototype */ +#include /* 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 index 0000000000..3ab37eb966 --- /dev/null +++ b/src/backend/port/hpux/tas.c.template @@ -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 index 0000000000..d978a7cb03 --- /dev/null +++ b/src/backend/port/hpux/tas.s @@ -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 index 0000000000..40527b3b1f --- /dev/null +++ b/src/backend/port/irix5/Makefile.inc @@ -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 index 0000000000..2c66463018 --- /dev/null +++ b/src/backend/port/irix5/README @@ -0,0 +1,2 @@ +The IRIX 5 port was contributed by + Paul 'Shag' Walmsley diff --git a/src/backend/port/irix5/machine.h b/src/backend/port/irix5/machine.h new file mode 100644 index 0000000000..fd1f22c8a9 --- /dev/null +++ b/src/backend/port/irix5/machine.h @@ -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 index 0000000000..7313e49c13 --- /dev/null +++ b/src/backend/port/irix5/port-protos.h @@ -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 index 0000000000..82303ed7fc --- /dev/null +++ b/src/backend/port/irix5/port.c @@ -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 /* for pow() prototype */ + +#include diff --git a/src/backend/port/linux/Makefile.inc b/src/backend/port/linux/Makefile.inc new file mode 100644 index 0000000000..cc35929a26 --- /dev/null +++ b/src/backend/port/linux/Makefile.inc @@ -0,0 +1,36 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for port/linux (Linux specific stuff) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/port/linux/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ +# +# NOTES +# The Linux port is included here by courtesy of Kai Petzke. +# +# (C) 1994, Kai Petzke, wpp@marie.physik.tu-berlin.de +# +#------------------------------------------------------------------------- + +# +# 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 index 0000000000..ebf0625dd7 --- /dev/null +++ b/src/backend/port/linux/dynloader.c @@ -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: /cvsroot/pgsql/src/backend/port/linux/Attic/dynloader.c,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#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 index 0000000000..90458810e9 --- /dev/null +++ b/src/backend/port/linux/machine.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f80cd62d42 --- /dev/null +++ b/src/backend/port/linux/port-protos.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * port-specific prototypes for SunOS 4 + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e4c5edfb9e --- /dev/null +++ b/src/backend/port/linux/port.c @@ -0,0 +1,13 @@ +/*------------------------------------------------------------------------- + * + * port.c-- + * Linux-specific routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/port/linux/Attic/port.c,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ diff --git a/src/backend/port/sparc/Makefile.inc b/src/backend/port/sparc/Makefile.inc new file mode 100644 index 0000000000..3b102fa324 --- /dev/null +++ b/src/backend/port/sparc/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/sparc/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..13e284571f --- /dev/null +++ b/src/backend/port/sparc/float.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * float.h-- + * definitions for ANSI floating point + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: float.h,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + * 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 index 0000000000..d34b65ff33 --- /dev/null +++ b/src/backend/port/sparc/machine.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2765c9307a --- /dev/null +++ b/src/backend/port/sparc/port-protos.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * port-specific prototypes for SunOS 4 + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:44 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PORT_PROTOS_H +#define PORT_PROTOS_H + +#include +#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 index 0000000000..5850848e66 --- /dev/null +++ b/src/backend/port/sparc/strtol.c @@ -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 +#include +#include +#include + +#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 index 0000000000..ee4a8f82f0 --- /dev/null +++ b/src/backend/port/sparc_solaris/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/sparc_solaris/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..35fe7afe61 --- /dev/null +++ b/src/backend/port/sparc_solaris/machine.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..777e66310f --- /dev/null +++ b/src/backend/port/sparc_solaris/port-protos.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * port-specific prototypes for SunOS 4 + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PORT_PROTOS_H +#define PORT_PROTOS_H + +#include +#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 index 0000000000..f3c6b8a794 --- /dev/null +++ b/src/backend/port/sparc_solaris/port.c @@ -0,0 +1,66 @@ +/*------------------------------------------------------------------------- + * + * port.c-- + * SunOS5-specific routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/port/sparc_solaris/Attic/port.c,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for pow() prototype */ + +#include +#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 index 0000000000..5e413bd0d9 --- /dev/null +++ b/src/backend/port/sparc_solaris/rusagestub.h @@ -0,0 +1,30 @@ +/*------------------------------------------------------------------------- + * + * rusagestub.h-- + * Stubs for getrusage(3). + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rusagestub.h,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef RUSAGESTUB_H +#define RUSAGESTUB_H + +#include /* for struct timeval */ +#include /* for struct tms */ +#include /* 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 index 0000000000..e09c2d4ad3 --- /dev/null +++ b/src/backend/port/sparc_solaris/tas.s @@ -0,0 +1,50 @@ + !! + !! $Header: /cvsroot/pgsql/src/backend/port/sparc_solaris/Attic/tas.s,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + !! + !! 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 index 0000000000..fca568f5d5 --- /dev/null +++ b/src/backend/port/ultrix4/Makefile.inc @@ -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: /cvsroot/pgsql/src/backend/port/ultrix4/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..bca3602a21 --- /dev/null +++ b/src/backend/port/ultrix4/dl.h @@ -0,0 +1,117 @@ +/*------------------------------------------------------------------------- + * + * dl.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: dl.h,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#include +#include +#include +#include + + +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 index 0000000000..887dff44ab --- /dev/null +++ b/src/backend/port/ultrix4/dynloader.c @@ -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: /cvsroot/pgsql/src/backend/port/ultrix4/Attic/dynloader.c,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..35fe7afe61 --- /dev/null +++ b/src/backend/port/ultrix4/machine.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * machine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: machine.h,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ed055dd19c --- /dev/null +++ b/src/backend/port/ultrix4/port-protos.h @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + * + * port-protos.h-- + * prototypes for Ultrix-specific routines + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: port-protos.h,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..6ece3210ec --- /dev/null +++ b/src/backend/port/ultrix4/port.c @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * port.c-- + * Ultrix-specific routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/port/ultrix4/Attic/port.c,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..f140711226 --- /dev/null +++ b/src/backend/port/ultrix4/strdup.c @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * strdup.c-- + * copies a null-terminated string. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/port/ultrix4/Attic/strdup.c,v 1.1.1.1 1996/07/09 06:21:45 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +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 index 0000000000..43e4057496 --- /dev/null +++ b/src/backend/port/win32/machine.h @@ -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 index 0000000000..b594005104 --- /dev/null +++ b/src/backend/port/win32/nt.c @@ -0,0 +1,625 @@ +#include +#include +#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 index 0000000000..abe3519ab5 --- /dev/null +++ b/src/backend/port/win32/nt.h @@ -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 index 0000000000..c922191c5d --- /dev/null +++ b/src/backend/port/win32/pglite.mak @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/port-protos.h @@ -0,0 +1 @@ + diff --git a/src/backend/port/win32/pwd.h b/src/backend/port/win32/pwd.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/pwd.h @@ -0,0 +1 @@ + diff --git a/src/backend/port/win32/regex/COPYRIGHT b/src/backend/port/win32/regex/COPYRIGHT new file mode 100644 index 0000000000..574f6bcec6 --- /dev/null +++ b/src/backend/port/win32/regex/COPYRIGHT @@ -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 index 0000000000..f9853f31ce --- /dev/null +++ b/src/backend/port/win32/regex/Makefile.inc @@ -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 index 0000000000..f4301d300d --- /dev/null +++ b/src/backend/port/win32/regex/WHATSNEW @@ -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' + 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 index 0000000000..a29a92ee9c --- /dev/null +++ b/src/backend/port/win32/regex/cclass.h @@ -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 index 0000000000..c1632ebb1f --- /dev/null +++ b/src/backend/port/win32/regex/cname.h @@ -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 index 0000000000..02c841afa4 --- /dev/null +++ b/src/backend/port/win32/regex/engine.c @@ -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®_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®_NOSUB) + nmatch = 0; + if (eflags®_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®_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®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOL: + if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OBOW: + if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_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®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_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®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_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®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_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®_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®_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 index 0000000000..db2f6349c4 --- /dev/null +++ b/src/backend/port/win32/regex/re_format.7 @@ -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 index 0000000000..feb24ecced --- /dev/null +++ b/src/backend/port/win32/regex/regcomp.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 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®_EXTENDED) && (cflags®_NOSPEC)) + return(REG_INVARG); + + if (cflags®_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®_EXTENDED) + p_ere(p, OUT); + else if (cflags®_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®_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<g->cflags®_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®_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®_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®_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<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<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 index 0000000000..a8d0945419 --- /dev/null +++ b/src/backend/port/win32/regex/regerror.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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®_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 index 0000000000..66a7285d86 --- /dev/null +++ b/src/backend/port/win32/regex/regex.3 @@ -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 +.br +#include +.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 +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 index 0000000000..1611d4b879 --- /dev/null +++ b/src/backend/port/win32/regex/regex.h @@ -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 + +/* 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 index 0000000000..0261b535d0 --- /dev/null +++ b/src/backend/port/win32/regex/regex2.h @@ -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< 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 index 0000000000..3890b61786 --- /dev/null +++ b/src/backend/port/win32/regex/regexec.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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®_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 index 0000000000..47c8e88fa6 --- /dev/null +++ b/src/backend/port/win32/regex/regexp.h @@ -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 + +__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 index 0000000000..580fb758c0 --- /dev/null +++ b/src/backend/port/win32/regex/regfree.c @@ -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 +#include +#include +#include + +#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 index 0000000000..6fb1e8970e --- /dev/null +++ b/src/backend/port/win32/regex/utils.h @@ -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 + +/* 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 index 0000000000..ddd0e086a4 --- /dev/null +++ b/src/backend/port/win32/rusagestub.h @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * rusagestub.h-- + * Stubs for getrusage(3). + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rusagestub.h,v 1.1.1.1 1996/07/09 06:21:46 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef RUSAGESTUB_H +#define RUSAGESTUB_H + +#include /* for struct timeval */ +#include /* 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 index 0000000000..d98596bb8f --- /dev/null +++ b/src/backend/port/win32/sys/cdefs.h @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/sys/file.h @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/sys/ipc.h @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/sys/param.h @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/sys/sem.h @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/sys/shm.h @@ -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 index 0000000000..8b13789179 --- /dev/null +++ b/src/backend/port/win32/sys/time.h @@ -0,0 +1 @@ + diff --git a/src/backend/postmaster/Makefile.inc b/src/backend/postmaster/Makefile.inc new file mode 100644 index 0000000000..8f6bfd5742 --- /dev/null +++ b/src/backend/postmaster/Makefile.inc @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the postmaster module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/postmaster/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:49 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..fe09e16e80 --- /dev/null +++ b/src/backend/postmaster/postmaster.c @@ -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: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.1.1.1 1996/07/09 06:21:49 scrappy Exp $ + * + * 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 */ +#include +#include +#ifndef WIN32 +#include +#endif /* WIN32 */ +#include +#include /* for fd_set stuff */ +#include /* for umask */ +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifdef WIN32 +#include +#include +#define MAXINT INT_MAX +#else +#include /* for MAXHOSTNAMELEN on some */ +# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi) +# include +# define MAXINT INT_MAX +# else +# include +# endif /* !PORTNAME_BSD44_derived */ +#include +#endif /* WIN32 */ +#include +#include +#include + +#if defined(PORTNAME_aix) +#include +#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 index 0000000000..574f6bcec6 --- /dev/null +++ b/src/backend/regex/COPYRIGHT @@ -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 index 0000000000..229fd2d964 --- /dev/null +++ b/src/backend/regex/Makefile.inc @@ -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 index 0000000000..f4301d300d --- /dev/null +++ b/src/backend/regex/WHATSNEW @@ -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' + 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 index 0000000000..cabe7faf22 --- /dev/null +++ b/src/backend/regex/cclass.h @@ -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 index 0000000000..07c493b2fb --- /dev/null +++ b/src/backend/regex/cdefs.h @@ -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: cdefs.h,v 1.1.1.1 1996/07/09 06:21:49 scrappy Exp $ + */ + +#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 index 0000000000..c6ba15bdc3 --- /dev/null +++ b/src/backend/regex/cname.h @@ -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 index 0000000000..45b37a4b4a --- /dev/null +++ b/src/backend/regex/engine.c @@ -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®_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®_NOSUB) + nmatch = 0; + if (eflags®_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®_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®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOL: + if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OBOW: + if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_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®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_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®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_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®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_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®_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®_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 index 0000000000..db2f6349c4 --- /dev/null +++ b/src/backend/regex/re_format.7 @@ -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 index 0000000000..27c8678446 --- /dev/null +++ b/src/backend/regex/regcomp.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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 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®_EXTENDED) && (cflags®_NOSPEC)) + return(REG_INVARG); + + if (cflags®_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®_EXTENDED) + p_ere(p, OUT); + else if (cflags®_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®_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<g->cflags®_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®_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®_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®_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<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<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 index 0000000000..2e19fa0c35 --- /dev/null +++ b/src/backend/regex/regerror.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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®_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 index 0000000000..66a7285d86 --- /dev/null +++ b/src/backend/regex/regex.3 @@ -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 +.br +#include +.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 +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 index 0000000000..fd5484f13f --- /dev/null +++ b/src/backend/regex/regex.h @@ -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 */ +/* 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 index 0000000000..0261b535d0 --- /dev/null +++ b/src/backend/regex/regex2.h @@ -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< 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 index 0000000000..32c59b1048 --- /dev/null +++ b/src/backend/regex/regexec.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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®_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 index 0000000000..bf39dab693 --- /dev/null +++ b/src/backend/regex/regexp.h @@ -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 */ +/* 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 index 0000000000..b2fd1e0583 --- /dev/null +++ b/src/backend/regex/regfree.c @@ -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 +#include +#include +#include + +#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 index 0000000000..cbab44259e --- /dev/null +++ b/src/backend/regex/utils.h @@ -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 + +/* 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 index 0000000000..9212f50a96 --- /dev/null +++ b/src/backend/rewrite/Makefile.inc @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the rewrite rules module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..a45457f61d --- /dev/null +++ b/src/backend/rewrite/locks.c @@ -0,0 +1,131 @@ +/*------------------------------------------------------------------------- + * + * locks.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/Attic/locks.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a1e56c4b5a --- /dev/null +++ b/src/backend/rewrite/locks.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * locks.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: locks.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..6385158f5e --- /dev/null +++ b/src/backend/rewrite/prs2lock.h @@ -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: prs2lock.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..36026a5358 --- /dev/null +++ b/src/backend/rewrite/rewriteDefine.c @@ -0,0 +1,255 @@ +/*------------------------------------------------------------------------- + * + * rewriteDefine.c-- + * routines for defining a rewrite rule + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#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 index 0000000000..e4fa8048a9 --- /dev/null +++ b/src/backend/rewrite/rewriteDefine.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * rewriteDefine.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteDefine.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ee61d293de --- /dev/null +++ b/src/backend/rewrite/rewriteHandler.c @@ -0,0 +1,622 @@ +/*------------------------------------------------------------------------- + * + * rewriteHandler.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a76360479e --- /dev/null +++ b/src/backend/rewrite/rewriteHandler.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * rewriteHandler.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteHandler.h,v 1.1.1.1 1996/07/09 06:21:51 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d5b6934649 --- /dev/null +++ b/src/backend/rewrite/rewriteManip.c @@ -0,0 +1,435 @@ +/*------------------------------------------------------------------------- + * + * rewriteManip.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9f5804fc3f --- /dev/null +++ b/src/backend/rewrite/rewriteManip.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * rewriteManip.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteManip.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9146d137c1 --- /dev/null +++ b/src/backend/rewrite/rewriteRemove.c @@ -0,0 +1,181 @@ +/*------------------------------------------------------------------------- + * + * rewriteRemove.c-- + * routines for removing rewrite rules + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..245b612ef4 --- /dev/null +++ b/src/backend/rewrite/rewriteRemove.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * rewriteRemove.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteRemove.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1a05fc87b0 --- /dev/null +++ b/src/backend/rewrite/rewriteSupport.c @@ -0,0 +1,270 @@ +/*------------------------------------------------------------------------- + * + * rewriteSupport.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteSupport.c,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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(irules[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 index 0000000000..5cc4b674f8 --- /dev/null +++ b/src/backend/rewrite/rewriteSupport.h @@ -0,0 +1,27 @@ +/*------------------------------------------------------------------------- + * + * rewriteSupport.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rewriteSupport.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..aef287ca71 --- /dev/null +++ b/src/backend/storage/Makefile.inc @@ -0,0 +1,31 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the storage modules +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..eb874bbad7 --- /dev/null +++ b/src/backend/storage/backendid.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * backendid.h-- + * POSTGRES backend id communication definitions + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: backendid.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5c006aa9d9 --- /dev/null +++ b/src/backend/storage/block.h @@ -0,0 +1,114 @@ +/*------------------------------------------------------------------------- + * + * block.h-- + * POSTGRES disk block definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: block.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..73582e8a61 --- /dev/null +++ b/src/backend/storage/buf.h @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------- + * + * buf.h-- + * Basic buffer manager data types. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: buf.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..84583867fa --- /dev/null +++ b/src/backend/storage/buf_internals.h @@ -0,0 +1,220 @@ +/*------------------------------------------------------------------------- + * + * buf_internals.h-- + * Internal definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: buf_internals.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + * 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 index 0000000000..1d507f9227 --- /dev/null +++ b/src/backend/storage/buffer/Makefile.inc @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/buffer +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..823bf41eec --- /dev/null +++ b/src/backend/storage/buffer/buf_init.c @@ -0,0 +1,280 @@ +/*------------------------------------------------------------------------- + * + * buf_init.c-- + * buffer manager initialization routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include + +/* 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 index 0000000000..502ded954e --- /dev/null +++ b/src/backend/storage/buffer/buf_table.c @@ -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: /cvsroot/pgsql/src/backend/storage/buffer/buf_table.c,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..655f1f408e --- /dev/null +++ b/src/backend/storage/buffer/bufmgr.c @@ -0,0 +1,1581 @@ +/*------------------------------------------------------------------------- + * + * bufmgr.c-- + * buffer manager interface routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/buffer/bufmgr.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * + * 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 +#include +#include +#include + +/* 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 +#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;ifreeNext])) { + + 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 index 0000000000..ec62594086 --- /dev/null +++ b/src/backend/storage/buffer/localbuf.c @@ -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: /cvsroot/pgsql/src/backend/storage/buffer/localbuf.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include + +/* 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 index 0000000000..581d3237ca --- /dev/null +++ b/src/backend/storage/bufmgr.h @@ -0,0 +1,112 @@ +/*------------------------------------------------------------------------- + * + * bufmgr.h-- + * POSTGRES buffer manager definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: bufmgr.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9fda973889 --- /dev/null +++ b/src/backend/storage/bufpage.h @@ -0,0 +1,256 @@ +/*------------------------------------------------------------------------- + * + * bufpage.h-- + * Standard POSTGRES buffer page definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: bufpage.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..da28b031bb --- /dev/null +++ b/src/backend/storage/fd.h @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------- + * + * fd.h-- + * Virtual file descriptor definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fd.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* for O_ on most */ +#ifndef O_RDONLY +#include /* for O_ on the rest */ +#endif /* O_RDONLY */ + +/* + * FileSeek uses the standard UNIX lseek(2) flags. + */ +#ifndef WIN32 +#include /* for SEEK_ on most */ +#else +#ifndef SEEK_SET +#include /* 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 index 0000000000..767cbecd38 --- /dev/null +++ b/src/backend/storage/file/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/file +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/file/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= fd.c diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c new file mode 100644 index 0000000000..bb94c4c5de --- /dev/null +++ b/src/backend/storage/file/fd.c @@ -0,0 +1,888 @@ +/*------------------------------------------------------------------------- + * + * fd.c-- + * Virtual file descriptor code. + * + * Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $Id: fd.c,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 +#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 +#ifndef _IPC_ +#define _IPC_ +#include +#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 + +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 +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 +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 index 0000000000..b426dba0ff --- /dev/null +++ b/src/backend/storage/ipc/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/ipc +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..02d66045f8 --- /dev/null +++ b/src/backend/storage/ipc/README @@ -0,0 +1,31 @@ +$Header: /cvsroot/pgsql/src/backend/storage/ipc/README,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ +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 index 0000000000..306300b90c --- /dev/null +++ b/src/backend/storage/ipc/ipc.c @@ -0,0 +1,718 @@ +/*------------------------------------------------------------------------- + * + * ipc.c-- + * POSTGRES inter-process communication definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipc.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + * 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 +#include +#include +#include + +/* XXX - the following dependency should be moved into the defaults.mk file */ +#ifndef _IPC_ +#define _IPC_ +#include +#include +#include +#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 *) 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 index 0000000000..18d3cccd0e --- /dev/null +++ b/src/backend/storage/ipc/ipci.c @@ -0,0 +1,149 @@ +/*------------------------------------------------------------------------- + * + * ipci.c-- + * POSTGRES inter-process communication initialization code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..3cbe796fc5 --- /dev/null +++ b/src/backend/storage/ipc/s_lock.c @@ -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: /cvsroot/pgsql/src/backend/storage/ipc/Attic/s_lock.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#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 index 0000000000..4eba3729ac --- /dev/null +++ b/src/backend/storage/ipc/shmem.c @@ -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: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#include +#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=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 index 0000000000..f08546742b --- /dev/null +++ b/src/backend/storage/ipc/shmqueue.c @@ -0,0 +1,251 @@ +/*------------------------------------------------------------------------- + * + * shmqueue.c-- + * shared memory linked lists + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmqueue.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + * 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 /* 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 index 0000000000..9151ee7768 --- /dev/null +++ b/src/backend/storage/ipc/sinval.c @@ -0,0 +1,169 @@ +/*------------------------------------------------------------------------- + * + * sinval.c-- + * POSTGRES shared cache invalidation communication code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* #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 index 0000000000..a30afdb6fe --- /dev/null +++ b/src/backend/storage/ipc/sinvaladt.c @@ -0,0 +1,797 @@ +/*------------------------------------------------------------------------- + * + * sinvaladt.c-- + * POSTGRES shared cache invalidation segment definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.1.1.1 1996/07/09 06:21:54 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 */ +/* 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 index 0000000000..7ff2561f23 --- /dev/null +++ b/src/backend/storage/ipc/spin.c @@ -0,0 +1,247 @@ +/*------------------------------------------------------------------------- + * + * spin.c-- + * routines for managing spin locks + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/Attic/spin.c,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#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 index 0000000000..ca989fec65 --- /dev/null +++ b/src/backend/storage/item.h @@ -0,0 +1,20 @@ +/*------------------------------------------------------------------------- + * + * item.h-- + * POSTGRES disk item definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: item.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f5cd0c62cc --- /dev/null +++ b/src/backend/storage/itemid.h @@ -0,0 +1,75 @@ +/*------------------------------------------------------------------------- + * + * itemid.h-- + * Standard POSTGRES buffer page item identifier definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: itemid.h,v 1.1.1.1 1996/07/09 06:21:52 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..c3b895ae07 --- /dev/null +++ b/src/backend/storage/itempos.h @@ -0,0 +1,44 @@ +/*------------------------------------------------------------------------- + * + * itempos.h-- + * Standard POSTGRES buffer page long item subposition definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: itempos.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ba3c154ef1 --- /dev/null +++ b/src/backend/storage/itemptr.h @@ -0,0 +1,115 @@ +/*------------------------------------------------------------------------- + * + * itemptr.h-- + * POSTGRES disk item pointer definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: itemptr.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..177d2c26e4 --- /dev/null +++ b/src/backend/storage/large_object.h @@ -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: large_object.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..fd27b46a49 --- /dev/null +++ b/src/backend/storage/large_object/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/large_object +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/large_object/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..ae57032f94 --- /dev/null +++ b/src/backend/storage/large_object/inv_api.c @@ -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: /cvsroot/pgsql/src/backend/storage/large_object/inv_api.c,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#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 index 0000000000..fe87eb0554 --- /dev/null +++ b/src/backend/storage/lmgr.h @@ -0,0 +1,84 @@ +/*------------------------------------------------------------------------- + * + * lmgr.h-- + * POSTGRES lock manager definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lmgr.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..ac507558b5 --- /dev/null +++ b/src/backend/storage/lmgr/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/lmgr +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/lmgr/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..e382003f2a --- /dev/null +++ b/src/backend/storage/lmgr/README @@ -0,0 +1,93 @@ +$Header: /cvsroot/pgsql/src/backend/storage/lmgr/README,v 1.1.1.1 1996/07/09 06:21:55 scrappy Exp $ + +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 index 0000000000..bfc2f5b2ee --- /dev/null +++ b/src/backend/storage/lmgr/lmgr.c @@ -0,0 +1,933 @@ +/*------------------------------------------------------------------------- + * + * lmgr.c-- + * POSTGRES lock manager code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lmgr.c,v 1.1.1.1 1996/07/09 06:21:56 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* #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 index 0000000000..8df898a006 --- /dev/null +++ b/src/backend/storage/lmgr/lock.c @@ -0,0 +1,1020 @@ +/*------------------------------------------------------------------------- + * + * lock.c-- + * simple lock acquisition + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.1.1.1 1996/07/09 06:21:56 scrappy Exp $ + * + * 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 /* 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;ictl->nLockTypes = ntypes; + ntypes++; + for (i=0;ictl->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 index 0000000000..c1702d18cb --- /dev/null +++ b/src/backend/storage/lmgr/multi.c @@ -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: /cvsroot/pgsql/src/backend/storage/lmgr/Attic/multi.c,v 1.1.1.1 1996/07/09 06:21:56 scrappy Exp $ + * + * NOTES: + * (1) The lock.c module assumes that the caller here is doing + * two phase locking. + * + *------------------------------------------------------------------------- + */ +#include +#include +#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;itupleId.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 index 0000000000..0955cdfc2f --- /dev/null +++ b/src/backend/storage/lmgr/proc.c @@ -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: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.1.1.1 1996/07/09 06:21:57 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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: /cvsroot/pgsql/src/backend/storage/lmgr/proc.c,v 1.1.1.1 1996/07/09 06:21:57 scrappy Exp $ + */ +#include +#ifndef WIN32 +#include +#endif /* WIN32 */ +#include +#include +#include "libpq/pqsignal.h" /* substitute for */ + +#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;isize;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 index 0000000000..8d41ea38bb --- /dev/null +++ b/src/backend/storage/lmgr/single.c @@ -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: /cvsroot/pgsql/src/backend/storage/lmgr/Attic/single.c,v 1.1.1.1 1996/07/09 06:21:57 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..df490e7651 --- /dev/null +++ b/src/backend/storage/lock.h @@ -0,0 +1,218 @@ +/*------------------------------------------------------------------------- + * + * lock.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lock.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..582c1cb6c3 --- /dev/null +++ b/src/backend/storage/multilev.h @@ -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: multilev.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..e5f5cbf548 --- /dev/null +++ b/src/backend/storage/off.h @@ -0,0 +1,60 @@ +/*------------------------------------------------------------------------- + * + * off.h-- + * POSTGRES disk "offset" definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: off.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a012ea522c --- /dev/null +++ b/src/backend/storage/page.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * page.h-- + * POSTGRES buffer page abstraction definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: page.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2a7d840851 --- /dev/null +++ b/src/backend/storage/page/Makefile.inc @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/page +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/page/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:58 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..14b5ead85b --- /dev/null +++ b/src/backend/storage/page/bufpage.c @@ -0,0 +1,519 @@ +/*------------------------------------------------------------------------- + * + * bufpage.c-- + * POSTGRES standard buffer page code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.1.1.1 1996/07/09 06:21:58 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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; ipd_linp + i; + if ((*lp).lp_flags & LP_USED) + nused++; + } + + if (nused == 0) { + for (i=0; ipd_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; ipd_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; ipd_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 index 0000000000..9d06337403 --- /dev/null +++ b/src/backend/storage/page/itemptr.c @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * itemptr.c-- + * POSTGRES disk item pointer code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/page/itemptr.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f32624c226 --- /dev/null +++ b/src/backend/storage/pagenum.h @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------- + * + * pagenum.h-- + * POSTGRES page number definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pagenum.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9a7f603416 --- /dev/null +++ b/src/backend/storage/pos.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * pos.h-- + * POSTGRES "position" definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pos.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef POS_H +#define POS_H + +#include "c.h" + +/* + * a 'position' used to be in postgres. this has + * been changed to just 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 index 0000000000..1ec89dedc2 --- /dev/null +++ b/src/backend/storage/proc.h @@ -0,0 +1,127 @@ +/*------------------------------------------------------------------------- + * + * proc.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: proc.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef _PROC_H_ +#define _PROC_H_ + +#include "storage/ipc.h" +#include "storage/lock.h" +#ifndef WIN32 +#include +#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 index 0000000000..a00b33581a --- /dev/null +++ b/src/backend/storage/shmem.h @@ -0,0 +1,104 @@ +/*------------------------------------------------------------------------- + * + * shmem.h-- + * shared memory management structures + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: shmem.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..036597dbb7 --- /dev/null +++ b/src/backend/storage/sinval.h @@ -0,0 +1,33 @@ +/*------------------------------------------------------------------------- + * + * sinval.h-- + * POSTGRES shared cache invalidation communication definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: sinval.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0602997898 --- /dev/null +++ b/src/backend/storage/sinvaladt.h @@ -0,0 +1,126 @@ +/*------------------------------------------------------------------------- + * + * sinvaladt.h-- + * POSTGRES shared cache invalidation segment definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: sinvaladt.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2e91938290 --- /dev/null +++ b/src/backend/storage/smgr.h @@ -0,0 +1,84 @@ +/*------------------------------------------------------------------------- + * + * smgr.h-- + * storage manager switch public interface declarations. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: smgr.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..8ff067afbe --- /dev/null +++ b/src/backend/storage/smgr/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for storage/smgr +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..4dbb2dce70 --- /dev/null +++ b/src/backend/storage/smgr/README @@ -0,0 +1,40 @@ +# $Header: /cvsroot/pgsql/src/backend/storage/smgr/README,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + +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 index 0000000000..31aa1336a8 --- /dev/null +++ b/src/backend/storage/smgr/md.c @@ -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: /cvsroot/pgsql/src/backend/storage/smgr/md.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include + +#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 index 0000000000..24a8d2472a --- /dev/null +++ b/src/backend/storage/smgr/mm.c @@ -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: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#ifdef MAIN_MEMORY + +#include +#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 index 0000000000..426c3d9348 --- /dev/null +++ b/src/backend/storage/smgr/smgr.c @@ -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: /cvsroot/pgsql/src/backend/storage/smgr/smgr.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..5c90d59091 --- /dev/null +++ b/src/backend/storage/smgr/smgrtype.c @@ -0,0 +1,82 @@ +/*------------------------------------------------------------------------- + * + * smgrtype.c-- + * storage manager type + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/storage/smgr/smgrtype.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..32037684ec --- /dev/null +++ b/src/backend/storage/spin.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * spin.h-- + * synchronization routines + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: spin.h,v 1.1.1.1 1996/07/09 06:21:53 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7bc44a67fd --- /dev/null +++ b/src/backend/tcop/Makefile.inc @@ -0,0 +1,18 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the traffic cop module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/tcop/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..4cb1e92953 --- /dev/null +++ b/src/backend/tcop/aclchk.c @@ -0,0 +1,555 @@ +/*------------------------------------------------------------------------- + * + * aclchk.c-- + * Routines to check access control permissions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/tcop/Attic/aclchk.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + * NOTES + * See acl.h. + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..0e10c1403e --- /dev/null +++ b/src/backend/tcop/dest.c @@ -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: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* 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 index 0000000000..369a4060db --- /dev/null +++ b/src/backend/tcop/dest.h @@ -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: dest.h,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..39014fe0db --- /dev/null +++ b/src/backend/tcop/fastpath.c @@ -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: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + * 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 index 0000000000..501522385a --- /dev/null +++ b/src/backend/tcop/fastpath.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * fastpath.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fastpath.h,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $ + * + * 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 index 0000000000..d6447eaffc --- /dev/null +++ b/src/backend/tcop/postgres.c @@ -0,0 +1,1500 @@ +/*------------------------------------------------------------------------- + * + * postgres.c-- + * POSTGRES C Backend Interface + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + * 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 */ +#if defined(PORTNAME_linux) +#ifndef __USE_POSIX +#define __USE_POSIX +#endif +#endif /* defined(PORTNAME_linux) */ +#include +#include +#include +#include +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifndef WIN32 +#include /* for MAXHOSTNAMELEN on some */ +#endif /* WIN32 */ +#include +#ifdef PORTNAME_aix +#include +#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;ilen;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;kqtrees[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;ilen;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; ilen; i++) { + print(querytree_list->qtrees[i]); + printf("\n"); + } + } + + for (i=0; ilen;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;ilen;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 +#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 index 0000000000..cbe6c6d0ab --- /dev/null +++ b/src/backend/tcop/pquery.c @@ -0,0 +1,362 @@ +/*------------------------------------------------------------------------- + * + * pquery.c-- + * POSTGRES process query command code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0661d12ad8 --- /dev/null +++ b/src/backend/tcop/pquery.h @@ -0,0 +1,36 @@ +/*------------------------------------------------------------------------- + * + * pquery.h-- + * prototypes for pquery.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pquery.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..202be631af --- /dev/null +++ b/src/backend/tcop/tcopdebug.h @@ -0,0 +1,43 @@ +/*------------------------------------------------------------------------- + * + * tcopdebug.h-- + * #defines governing debugging behaviour in the traffic cop + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tcopdebug.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..787bc0ed08 --- /dev/null +++ b/src/backend/tcop/tcopprot.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * tcopprot.h-- + * prototypes for postgres.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tcopprot.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + * 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 index 0000000000..e12d18b91e --- /dev/null +++ b/src/backend/tcop/utility.c @@ -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: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..8a3035c0c4 --- /dev/null +++ b/src/backend/tcop/utility.h @@ -0,0 +1,18 @@ +/*------------------------------------------------------------------------- + * + * utility.h-- + * prototypes for utility.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: utility.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..365f4647d6 --- /dev/null +++ b/src/backend/tioga/Arr_TgRecipe.h @@ -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 index 0000000000..57086e6db6 --- /dev/null +++ b/src/backend/tioga/Makefile.inc @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the Tioga module +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/tioga/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..3ed45c6656 --- /dev/null +++ b/src/backend/tioga/Varray.c @@ -0,0 +1,48 @@ +/* ************************************************************************ + * + * Varray.c -- + * + * routines to provide a generic set of functions to handle variable sized + * arrays. originally by Jiang Wu + * ************************************************************************/ + +#include +#include +#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 index 0000000000..f6d01c23db --- /dev/null +++ b/src/backend/tioga/Varray.h @@ -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 index 0000000000..ea52a71510 --- /dev/null +++ b/src/backend/tioga/tgRecipe.c @@ -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: /cvsroot/pgsql/src/backend/tioga/Attic/tgRecipe.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#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;inum;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;inum;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;ioutNodes->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;iinNodes->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;ielements, &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;iallNodes, &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;ielements, &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;iallNodes->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; + jinNodes->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 index 0000000000..c6ee78e506 --- /dev/null +++ b/src/backend/tioga/tgRecipe.h @@ -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: tgRecipe.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..f6af3aa80d --- /dev/null +++ b/src/backend/utils/Gen_fmgrtab.sh @@ -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: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ +# +# 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 <> $HFILE +cat >> $HFILE < $TABCFILE < +#else +# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi) +# include +# define MAXINT INT_MAX +# else +# include /* for MAXINT */ +# endif /* PORTNAME_BSD44_derived || PORTNAME_bsdi */ +#endif /* WIN32 */ + +#include "utils/fmgrtab.h" + +FuNkYfMgRtAbStUfF +awk '{ print "extern char *" $2 "();"; }' $RAWFILE >> $TABCFILE +cat >> $TABCFILE <> $TABCFILE +cat >> $TABCFILE < 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 +#include +#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= 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;iaclitem = (AclItem*)palloc(sizeof(AclItem)); + /* the grantee string is "G ", "U ", 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 index 0000000000..2e780fb03b --- /dev/null +++ b/src/backend/utils/adt/arrayfuncs.c @@ -0,0 +1,1375 @@ +/*------------------------------------------------------------------------- + * + * arrayfuncs.c-- + * Special functions for arrays. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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: + * - total number of bytes + * - number of dimensions of the array + * - bit mask of flags + * - size of each array axis + * - lower boundary of each dimension + * - 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: -chunk DEFAULT/ -invert/-native [-noreorg ]"); + } + } + + 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 index 0000000000..9a16b950f2 --- /dev/null +++ b/src/backend/utils/adt/arrayutils.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/arrayutils.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..d0f3d34b52 --- /dev/null +++ b/src/backend/utils/adt/bool.c @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------- + * + * bool.c-- + * Functions for the built-in type "bool". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/bool.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..0d9c8f4fb5 --- /dev/null +++ b/src/backend/utils/adt/char.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/char.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#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 index 0000000000..ca0cb2647e --- /dev/null +++ b/src/backend/utils/adt/chunk.c @@ -0,0 +1,587 @@ +/*------------------------------------------------------------------------- + * + * chunk.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/chunk.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..d9eaf1227e --- /dev/null +++ b/src/backend/utils/adt/date.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + * 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 +#include +#include +#include +#include +#include + +#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: + * `[' {` '} `'' `'' {` '} `'' `'' {` '} `]' + * + * OR `Undefined Range' (see also INVALID_INTERVAL_STR) + * + * where 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 index 0000000000..b6207be263 --- /dev/null +++ b/src/backend/utils/adt/datetimes.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/Attic/datetimes.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#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->yearyear); + if (date1->month!=date2->month) + return (date1->monthmonth); + return (date1->dayday); +} + +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->yearyear) ? -1 : 1); + if (date1->month!=date2->month) + return ((date1->monthmonth) ? -1 : 1); + if (date1->day!=date2->day) + return ((date1->dayday) ? -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->hrhr); + if (time1->min!=time2->min) + return (time1->minmin); + return (time1->secsec); +} + +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->hrhr) ? -1 : 1); + if (time1->min!=time2->min) + return ((time1->minmin) ? -1 : 1); + if (time1->sec!=time2->sec) + return ((time1->secsec) ? -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 index 0000000000..e982fc7607 --- /dev/null +++ b/src/backend/utils/adt/datum.c @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------- + * + * datum.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/datum.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 +#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 index 0000000000..bc162427d2 --- /dev/null +++ b/src/backend/utils/adt/dt.c @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------- + * + * dt.c-- + * Functions for the built-in type "dt". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..21389597eb --- /dev/null +++ b/src/backend/utils/adt/filename.c @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------- + * + * filename.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/filename.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#ifndef WIN32 +#include +#endif /* WIN32 */ + +#include +#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 index 0000000000..ef962e7136 --- /dev/null +++ b/src/backend/utils/adt/float.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* for sprintf() */ +#include +#include +#include +#include + +#include /* faked on sunos4 */ + +#include + +#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 */ + +#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: + * {} [+|-] {digit} [.{digit}] [] + * where is a space, digit is 0-9, + * 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: + * {} [+|-] {digit} [.{digit}] [] + * where is a space, digit is 0-9, + * 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 +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 +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 +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 +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 index 0000000000..8262591a05 --- /dev/null +++ b/src/backend/utils/adt/geo-ops.c @@ -0,0 +1,1947 @@ +/*------------------------------------------------------------------------- + * + * geo-ops.c-- + * 2D geometric operations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/geo-ops.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include /* faked on sunos */ +#include /* for sprintf proto, etc. */ +#include + +#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; inpts + 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; inpts; 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; inpts; 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 index 0000000000..a816e738f5 --- /dev/null +++ b/src/backend/utils/adt/geo-selfuncs.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/Attic/geo-selfuncs.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + * 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 index 0000000000..cbf4a0cba1 --- /dev/null +++ b/src/backend/utils/adt/int.c @@ -0,0 +1,343 @@ +/*------------------------------------------------------------------------- + * + * int.c-- + * Functions for the built-in integer types. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 index 0000000000..e33e66ee62 --- /dev/null +++ b/src/backend/utils/adt/like.c @@ -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 (7/95). + * + * + *------------------------------------------------------------------------- + */ +#include +#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 . +** Special thanks to Lars Mathiesen 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. +** +** [SQL92 lets you specify the escape character by saying +** LIKE ESCAPE . 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 index 0000000000..7ba7cc98f7 --- /dev/null +++ b/src/backend/utils/adt/misc.c @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------- + * + * misc.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/misc.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..fa66ff5fa4 --- /dev/null +++ b/src/backend/utils/adt/nabstime.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#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 index 0000000000..1031b5bb49 --- /dev/null +++ b/src/backend/utils/adt/name.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/name.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..47c28c4eda --- /dev/null +++ b/src/backend/utils/adt/not_in.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * + * 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 +#include +#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 index 0000000000..d5e07b5f26 --- /dev/null +++ b/src/backend/utils/adt/numutils.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/numutils.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#include +#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: + ** {} [+|-] {} {} [.{digit}] {} [] + ** where is "e" or "E" followed by an integer, is a + ** space character, 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 index 0000000000..d049db8b7f --- /dev/null +++ b/src/backend/utils/adt/oid.c @@ -0,0 +1,127 @@ +/*------------------------------------------------------------------------- + * + * oid.c-- + * Functions for the built-in type Oid. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/oid.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4d92821e8d --- /dev/null +++ b/src/backend/utils/adt/oidint2.c @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------- + * + * oidint2.c-- + * Functions for the built-in type "oidint2". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/oidint2.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..d0844411a7 --- /dev/null +++ b/src/backend/utils/adt/oidint4.c @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------- + * + * oidint4.c-- + * Functions for the built-in type "oidint4". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/oidint4.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* 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 index 0000000000..c4f42674c5 --- /dev/null +++ b/src/backend/utils/adt/oidname.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/Attic/oidname.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..4b5a008903 --- /dev/null +++ b/src/backend/utils/adt/regexp.c @@ -0,0 +1,343 @@ +/*------------------------------------------------------------------------- + * + * regexp.c-- + * regular expression handling code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regexp.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + * 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 +#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 +#include + +#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 index 0000000000..071573fdcc --- /dev/null +++ b/src/backend/utils/adt/regproc.c @@ -0,0 +1,159 @@ +/*------------------------------------------------------------------------- + * + * regproc.c-- + * Functions for the built-in type "RegProcedure". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..8ee93b768d --- /dev/null +++ b/src/backend/utils/adt/selfuncs.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..9ad8d45782 --- /dev/null +++ b/src/backend/utils/adt/sets.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* 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" 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 index 0000000000..6690b74c02 --- /dev/null +++ b/src/backend/utils/adt/tid.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + * NOTES + * input routine largely stolen from boxin(). + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#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 index 0000000000..341ad94f00 --- /dev/null +++ b/src/backend/utils/adt/varchar.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* for sprintf() */ +#include +#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 +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 +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 index 0000000000..1ef6f19113 --- /dev/null +++ b/src/backend/utils/adt/varlena.c @@ -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: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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<=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< /* 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 index 0000000000..c817d7b69b --- /dev/null +++ b/src/backend/utils/bit.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * bit.h-- + * Standard bit array definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: bit.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2379c59adb --- /dev/null +++ b/src/backend/utils/builtins.h @@ -0,0 +1,433 @@ +/*------------------------------------------------------------------------- + * + * builtins.h-- + * Declarations for operations on built-in types. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: builtins.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + * 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 index 0000000000..c6a688e5e8 --- /dev/null +++ b/src/backend/utils/cache/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/cache +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..93ea9be828 --- /dev/null +++ b/src/backend/utils/cache/catcache.c @@ -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: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * Notes: + * XXX This needs to use exception.h to handle recovery when + * an abort occurs during DisableCache. + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..070f457c28 --- /dev/null +++ b/src/backend/utils/cache/fcache.c @@ -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: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..7bd214d085 --- /dev/null +++ b/src/backend/utils/cache/inval.c @@ -0,0 +1,612 @@ +/*------------------------------------------------------------------------- + * + * inval.c-- + * POSTGRES cache invalidation dispatcher code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * 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 index 0000000000..b7fc5e2620 --- /dev/null +++ b/src/backend/utils/cache/lsyscache.c @@ -0,0 +1,484 @@ +/*------------------------------------------------------------------------- + * + * lsyscache.c-- + * Routines to access information within system caches + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * 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 +#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 index 0000000000..33eabad1a8 --- /dev/null +++ b/src/backend/utils/cache/rel.c @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------- + * + * rel.c-- + * POSTGRES relation descriptor code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/rel.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* #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 index 0000000000..4e3f28491b --- /dev/null +++ b/src/backend/utils/cache/relcache.c @@ -0,0 +1,1795 @@ +/*------------------------------------------------------------------------- + * + * relcache.c-- + * POSTGRES relation descriptor cache code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* for sprintf() */ +#include +#include +#include + +#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 index 0000000000..36b46d9b99 --- /dev/null +++ b/src/backend/utils/cache/syscache.c @@ -0,0 +1,630 @@ +/*------------------------------------------------------------------------- + * + * syscache.c-- + * System cache management routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * 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 index 0000000000..daecf2ff30 --- /dev/null +++ b/src/backend/utils/catcache.h @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------- + * + * catcache.h-- + * Low-level catalog cache definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: catcache.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..09a2851806 --- /dev/null +++ b/src/backend/utils/datum.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * datum.h-- + * POSTGRES abstract data type datum representation definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: datum.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..c0532bdcba --- /dev/null +++ b/src/backend/utils/dynamic_loader.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- + * + * dynamic_loader.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: dynamic_loader.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef DYNAMIC_LOADER_H +#define DYNAMIC_LOADER_H + +#ifdef MIN +#undef MIN +#undef MAX +#endif /* MIN */ + +#ifdef WIN32 +#define MAXPATHLEN 250 +#endif + +#include /* for MAXPATHLEN */ +#include /* for dev_t, ino_t, etc. */ +#ifdef WIN32 +#include +#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 index 0000000000..bf858fc7ad --- /dev/null +++ b/src/backend/utils/elog.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * elog.h-- + * POSTGRES error logging definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: elog.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..2c3d469669 --- /dev/null +++ b/src/backend/utils/error/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/error +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..ac2de1631d --- /dev/null +++ b/src/backend/utils/error/assert.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * assert.c-- + * Assert code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/assert.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * NOTE + * This should eventually work with elog(), dlog(), etc. + * + *------------------------------------------------------------------------- + */ +#include + +#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 index 0000000000..d5bef1ab1b --- /dev/null +++ b/src/backend/utils/error/elog.c @@ -0,0 +1,237 @@ +/*------------------------------------------------------------------------- + * + * elog.c-- + * error logger + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#ifndef O_RDONLY +#include +#endif /* O_RDONLY */ +#include +#include +#include + +#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 index 0000000000..35c5db9118 --- /dev/null +++ b/src/backend/utils/error/exc.c @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------- + * + * exc.c-- + * POSTGRES exception handling code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/exc.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * NOTE + * XXX this code needs improvement--check for state violations and + * XXX reset after handling an exception. + * + *------------------------------------------------------------------------- + */ +#include /* XXX use own I/O routines */ +#include +#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 index 0000000000..d1b14361ea --- /dev/null +++ b/src/backend/utils/error/excabort.c @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * excabort.c-- + * Default exception abort code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/excabort.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4d87c16344 --- /dev/null +++ b/src/backend/utils/error/excid.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * excid.c-- + * POSTGRES known exception identifier code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/excid.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..99a5d15af5 --- /dev/null +++ b/src/backend/utils/error/format.c @@ -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: /cvsroot/pgsql/src/backend/utils/error/Attic/format.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#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 index 0000000000..487ecca837 --- /dev/null +++ b/src/backend/utils/exc.h @@ -0,0 +1,101 @@ +/*------------------------------------------------------------------------- + * + * exc.h-- + * POSTGRES exception handling definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: exc.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef EXC_H +#define EXC_H + +#include "c.h" /* for Exception, etc. */ +#include + +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 index 0000000000..a0d5edc6f9 --- /dev/null +++ b/src/backend/utils/excid.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * excid.h-- + * POSTGRES known exception identifier definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: excid.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..a7a83452ba --- /dev/null +++ b/src/backend/utils/fcache.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * fcache.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fcache.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..3f149aee30 --- /dev/null +++ b/src/backend/utils/fcache2.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * fcache2.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fcache2.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1961554431 --- /dev/null +++ b/src/backend/utils/fmgr/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/fmgr +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/fmgr/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..e835d9451d --- /dev/null +++ b/src/backend/utils/fmgr/dfmgr.c @@ -0,0 +1,269 @@ +/*------------------------------------------------------------------------- + * + * dfmgr.c-- + * Dynamic function manager code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include + +#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 index 0000000000..6ae276944e --- /dev/null +++ b/src/backend/utils/fmgr/fmgr.c @@ -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: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..e700f2c82c --- /dev/null +++ b/src/backend/utils/fmgrtab.h @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * fmgrtab.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fmgrtab.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..914f0d995d --- /dev/null +++ b/src/backend/utils/geo-decls.h @@ -0,0 +1,248 @@ +/*------------------------------------------------------------------------- + * + * geo-decls.h-- + * Declarations for various 2D constructs. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: geo-decls.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + * 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 +#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 index 0000000000..3f9710488e --- /dev/null +++ b/src/backend/utils/hash/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/hash +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/hash/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..66477f5567 --- /dev/null +++ b/src/backend/utils/hash/dynahash.c @@ -0,0 +1,868 @@ +/*------------------------------------------------------------------------- + * + * dynahash.c-- + * dynamic hashing + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * + * 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 ejp@ausmelb.oz, 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 ejp@ausmelb.oz. WARNING: relies on pre-processor + * concatenation property, in probably unnecessary code 'optimisation'. + * + * Modified margo@postgres.berkeley.edu February 1990 + * added multiple table interface + * Modified by sullivan@postgres.berkeley.edu April 1990 + * changed ctl structure for shared memory + */ +# include +# include +# include +# 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 index 0000000000..2a25b8a59e --- /dev/null +++ b/src/backend/utils/hash/hashfn.c @@ -0,0 +1,156 @@ +/*------------------------------------------------------------------------- + * + * hashfn.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/hash/hashfn.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..d12664e468 --- /dev/null +++ b/src/backend/utils/hsearch.h @@ -0,0 +1,141 @@ +/*------------------------------------------------------------------------- + * + * hsearch.h-- + * for hashing in the new buffer manager + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: hsearch.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d773037f36 --- /dev/null +++ b/src/backend/utils/init/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/init +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..995ab9d956 --- /dev/null +++ b/src/backend/utils/init/enbl.c @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * enbl.c-- + * POSTGRES module enable and disable support code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/enbl.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4a82105490 --- /dev/null +++ b/src/backend/utils/init/findbe.c @@ -0,0 +1,251 @@ +/*------------------------------------------------------------------------- + * + * findbe.c -- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/findbe.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#ifndef WIN32 +#include +#else +#include +#endif /* WIN32 */ +#include +#include +#include +#include + +#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 index 0000000000..ad8a75a90a --- /dev/null +++ b/src/backend/utils/init/globals.c @@ -0,0 +1,108 @@ +/*------------------------------------------------------------------------- + * + * globals.c-- + * global variable declarations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + * NOTES + * Globals used all over the place should be declared here and not + * in other modules. + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..8e93ff51f5 --- /dev/null +++ b/src/backend/utils/init/magic.c @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------- + * + * magic.c-- + * magic number management routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/magic.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000000..6f19210048 --- /dev/null +++ b/src/backend/utils/init/miscinit.c @@ -0,0 +1,378 @@ +/*------------------------------------------------------------------------- + * + * miscinit.c-- + * miscellanious initialization support stuff + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include /* for MAXPATHLEN */ +#include +#include +#include +#include +#ifndef WIN32 +#include /* for getgrgid */ +#include /* 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 index 0000000000..eab6c76cdc --- /dev/null +++ b/src/backend/utils/init/postinit.c @@ -0,0 +1,648 @@ +/*------------------------------------------------------------------------- + * + * postinit.c-- + * postgres initialization utilities + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * 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 +#include +#include +#include +#include +#include + +#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 index 0000000000..72a664395a --- /dev/null +++ b/src/backend/utils/inval.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * inval.h-- + * POSTGRES cache invalidation dispatcher definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: inval.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..095da056e1 --- /dev/null +++ b/src/backend/utils/lselect.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * lselect.h-- + * definitions for the replacement selection algorithm. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lselect.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..27f18570da --- /dev/null +++ b/src/backend/utils/lsyscache.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * lsyscache.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lsyscache.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..d10019166e --- /dev/null +++ b/src/backend/utils/mcxt.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * mcxt.h-- + * POSTGRES memory context definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: mcxt.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..9f94cfafab --- /dev/null +++ b/src/backend/utils/memutils.h @@ -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: memutils.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + * 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 . + * 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: /cvsroot/pgsql/src/backend/utils/Attic/memutils.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + */ + +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 */ +/* use because it's ANSI */ +#include + +/* + * 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 index 0000000000..6a4a67a81d --- /dev/null +++ b/src/backend/utils/mmgr/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/mmgr +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/mmgr/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..bbb7cd66f2 --- /dev/null +++ b/src/backend/utils/mmgr/aset.c @@ -0,0 +1,381 @@ +/*------------------------------------------------------------------------- + * + * aset.c-- + * Allocation set definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * NOTE + * XXX This is a preliminary implementation which lacks fail-fast + * XXX validity checking of arguments. + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..d9095af11f --- /dev/null +++ b/src/backend/utils/mmgr/mcxt.c @@ -0,0 +1,510 @@ +/*------------------------------------------------------------------------- + * + * mcxt.c-- + * POSTGRES memory context code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include /* 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 index 0000000000..478fe1516a --- /dev/null +++ b/src/backend/utils/mmgr/oset.c @@ -0,0 +1,173 @@ +/*------------------------------------------------------------------------- + * + * oset.c-- + * Fixed format ordered set definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/Attic/oset.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * 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 index 0000000000..725cf171c8 --- /dev/null +++ b/src/backend/utils/mmgr/palloc.c @@ -0,0 +1,117 @@ +/*------------------------------------------------------------------------- + * + * palloc.c-- + * POSTGRES memory allocator code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/Attic/palloc.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..b43e85a079 --- /dev/null +++ b/src/backend/utils/mmgr/portalmem.c @@ -0,0 +1,980 @@ +/*------------------------------------------------------------------------- + * + * portalmem.c-- + * backend portal memory context management stuff + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 /* for sprintf() */ +#include /* 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 index 0000000000..9ad62d0f0c --- /dev/null +++ b/src/backend/utils/module.h @@ -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: module.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..68857656e9 --- /dev/null +++ b/src/backend/utils/nabstime.h @@ -0,0 +1,165 @@ +/*------------------------------------------------------------------------- + * + * nabstime.h-- + * Definitions for the "new" abstime code. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nabstime.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef NABSTIME_H +#define NABSTIME_H + +#include +#include +#if !defined(PORTNAME_irix5) +#include +#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 +/*#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 index 0000000000..18c1f2ac55 --- /dev/null +++ b/src/backend/utils/oidcompos.h @@ -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: oidcompos.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..f27f5f598a --- /dev/null +++ b/src/backend/utils/palloc.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * palloc.h-- + * POSTGRES memory allocator definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: palloc.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4e3e42c914 --- /dev/null +++ b/src/backend/utils/portal.h @@ -0,0 +1,97 @@ +/*------------------------------------------------------------------------- + * + * portal.h-- + * POSTGRES portal definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: portal.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * 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 "" + +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 index 0000000000..f699753814 --- /dev/null +++ b/src/backend/utils/psort.h @@ -0,0 +1,86 @@ +/*------------------------------------------------------------------------- + * + * psort.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: psort.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 +#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 index 0000000000..d1d5a78dba --- /dev/null +++ b/src/backend/utils/rel.h @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------- + * + * rel.h-- + * POSTGRES relation descriptor definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rel.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..1e3d9881e7 --- /dev/null +++ b/src/backend/utils/rel2.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * rel2.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rel2.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..61e71ff4ed --- /dev/null +++ b/src/backend/utils/relcache.h @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------- + * + * relcache.h-- + * Relation descriptor cache definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: relcache.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef RELCACHE_H +#define RELCACHE_H + +#include + +#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 index 0000000000..5fc1ec1a41 --- /dev/null +++ b/src/backend/utils/sets.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * sets.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: sets.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..4775407aca --- /dev/null +++ b/src/backend/utils/sort/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/sort +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..1f92e48d83 --- /dev/null +++ b/src/backend/utils/sort/lselect.c @@ -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: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#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 index 0000000000..8e7187b434 --- /dev/null +++ b/src/backend/utils/sort/psort.c @@ -0,0 +1,617 @@ +/*------------------------------------------------------------------------- + * + * psort.c-- + * Polyphase merge sort. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + * 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 +#include + +#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 index 0000000000..beea596940 --- /dev/null +++ b/src/backend/utils/syscache.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------- + * + * syscache.h-- + * System catalog cache definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: syscache.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..5e5d842e34 --- /dev/null +++ b/src/backend/utils/time/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/time +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/time/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= tqual.c diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c new file mode 100644 index 0000000000..dd115b6988 --- /dev/null +++ b/src/backend/utils/time/tqual.c @@ -0,0 +1,815 @@ +/*------------------------------------------------------------------------- + * + * tqual.c-- + * POSTGRES time qualification code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +/* #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 index 0000000000..294fb18a3e --- /dev/null +++ b/src/backend/utils/tqual.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * tqual.h-- + * POSTGRES time qualification definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tqual.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + * 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 index 0000000000..67ed0b86ad --- /dev/null +++ b/src/bin/Makefile @@ -0,0 +1,30 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for src/bin (utility programs) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/Makefile,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# +# 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 index 0000000000..dc6ed375b6 --- /dev/null +++ b/src/bin/Makefile.global @@ -0,0 +1,33 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# global configurations for Makefiles in src/bin +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/Attic/Makefile.global,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..43d78487c4 --- /dev/null +++ b/src/bin/cleardbdir/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/cleardbdir +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/cleardbdir/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..d9c03ab75c --- /dev/null +++ b/src/bin/cleardbdir/cleardbdir.sh @@ -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: /cvsroot/pgsql/src/bin/cleardbdir/Attic/cleardbdir.sh,v 1.1.1.1 1996/07/09 06:22:11 scrappy Exp $ +# +#------------------------------------------------------------------------- + +[ -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 index 0000000000..db5a63484f --- /dev/null +++ b/src/bin/createdb/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/createdb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/createdb/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..2d2116d469 --- /dev/null +++ b/src/bin/createdb/createdb.sh @@ -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: /cvsroot/pgsql/src/bin/createdb/Attic/createdb.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# ---------------- +# 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 index 0000000000..7c0c3e13ee --- /dev/null +++ b/src/bin/createuser/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/createuser +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/createuser/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..4178f94596 --- /dev/null +++ b/src/bin/createuser/createuser.sh @@ -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: /cvsroot/pgsql/src/bin/createuser/Attic/createuser.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +# 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 index 0000000000..4dadef6eb8 --- /dev/null +++ b/src/bin/destroydb/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/destroydb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/destroydb/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..564d503a1d --- /dev/null +++ b/src/bin/destroydb/destroydb.sh @@ -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: /cvsroot/pgsql/src/bin/destroydb/Attic/destroydb.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# ---------------- +# 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 index 0000000000..7e5d5014f7 --- /dev/null +++ b/src/bin/destroyuser/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/destroyuser +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/destroyuser/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..e600748f14 --- /dev/null +++ b/src/bin/destroyuser/destroyuser.sh @@ -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: /cvsroot/pgsql/src/bin/destroyuser/Attic/destroyuser.sh,v 1.1.1.1 1996/07/09 06:22:12 scrappy Exp $ +# +# 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 index 0000000000..a63236aaff --- /dev/null +++ b/src/bin/initdb/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/initdb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/initdb/Makefile,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..8e9044981f --- /dev/null +++ b/src/bin/initdb/initdb.sh @@ -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: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +# ---------------- +# 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 index 0000000000..5f7993b9ba --- /dev/null +++ b/src/bin/ipcclean/Makefile @@ -0,0 +1,21 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/initdb +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/ipcclean/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..d3ea2fc576 --- /dev/null +++ b/src/bin/ipcclean/ipcclean.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# +# $Header: /cvsroot/pgsql/src/bin/ipcclean/Attic/ipcclean.sh,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +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 index 0000000000..53e6f0d514 --- /dev/null +++ b/src/bin/monitor/Makefile @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/monitor +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/monitor/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..f9bfa92237 --- /dev/null +++ b/src/bin/monitor/monitor.c @@ -0,0 +1,1058 @@ +/*------------------------------------------------------------------------- + * + * monitor.c-- + * POSTGRES Terminal Monitor + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/monitor/Attic/monitor.c,v 1.1.1.1 1996/07/09 06:22:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include "libpq/pqsignal.h" /* substitute for */ +#include +#include +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifndef WIN32 +#include +#endif +#ifdef PORTNAME_sparc_solaris +#include /* for MAXHOSTNAMELEN on some */ +#endif +#include +/* #include */ +#include + +#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 + +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 index 0000000000..286f96ea29 --- /dev/null +++ b/src/bin/pg4_dump/Makefile @@ -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 + +CFLAGS+= -I${.CURDIR}/../../backend/tmp + +PROG= pg4_dump + +SRCS= pg4_dump.c common.c + +.include diff --git a/src/bin/pg4_dump/README b/src/bin/pg4_dump/README new file mode 100644 index 0000000000..4ac5451709 --- /dev/null +++ b/src/bin/pg4_dump/README @@ -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 + postgres95@postgres.berkeley.edu + + + diff --git a/src/bin/pg4_dump/common.c b/src/bin/pg4_dump/common.c new file mode 100644 index 0000000000..f485c152c6 --- /dev/null +++ b/src/bin/pg4_dump/common.c @@ -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 +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include /* 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 0) { + result = (char**)malloc(sizeof(char*) * numParents); + j = 0; + for (i=0;i= 0; i--) { + tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables, + inhinfo, numInherits, + tblinfo[i].oid, + &tblinfo[i].numParents); + for (k=0;k 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 index 0000000000..6e6ee6fa57 --- /dev/null +++ b/src/bin/pg4_dump/pg4_dump.c @@ -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 +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include /* 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 '%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 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 '%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 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 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 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;k0) ? ", " : "", + 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 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 index 0000000000..0708f671be --- /dev/null +++ b/src/bin/pg4_dump/pg_dump.h @@ -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 index 0000000000..5512f249fa --- /dev/null +++ b/src/bin/pg_dump/Makefile @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/pg_dump +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pg_dump/Makefile,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..17c433c898 --- /dev/null +++ b/src/bin/pg_dump/README @@ -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 + postgres95@postgres95.vnet.net + + + diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c new file mode 100644 index 0000000000..96ea7f7295 --- /dev/null +++ b/src/bin/pg_dump/common.c @@ -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: /cvsroot/pgsql/src/bin/pg_dump/common.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +#include +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include /* 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 0) { + result = (char**)malloc(sizeof(char*) * numParents); + j = 0; + for (i=0;i= 0; i--) { + tblinfo[i].parentRels = findParentsByOid(tblinfo, numTables, + inhinfo, numInherits, + tblinfo[i].oid, + &tblinfo[i].numParents); + for (k=0;k 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 index 0000000000..9ef8f8f35e --- /dev/null +++ b/src/bin/pg_dump/pg_dump.c @@ -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: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +#include +#include +#include +#include /* for MAXHOSTNAMELEN on most */ +#ifdef PORTNAME_sparc_solaris +#include /* 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 '%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 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 '%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 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 0) ? ", " : "", + tblinfo[i].attnames[j], + tblinfo[i].typnames[j]); + actual_atts++; + } + } + + strcat(q,")"); + + if (numParents > 0) { + sprintf(q, "%s inherits ( ",q); + for (k=0;k0) ? ", " : "", + 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;iconn, 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 index 0000000000..e77960afe4 --- /dev/null +++ b/src/bin/pg_dump/pg_dump.h @@ -0,0 +1,195 @@ +/*------------------------------------------------------------------------- + * + * pg_dump.h + * header file for the pg_dump utility + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: pg_dump.h,v 1.1.1.1 1996/07/09 06:22:14 scrappy 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(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 index 0000000000..db056f89f2 --- /dev/null +++ b/src/bin/pg_id/Makefile @@ -0,0 +1,23 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/pg_id +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pg_id/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..675326acbc --- /dev/null +++ b/src/bin/pg_id/pg_id.c @@ -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: /cvsroot/pgsql/src/bin/pg_id/Attic/pg_id.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include + +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 index 0000000000..9e46bd27b0 --- /dev/null +++ b/src/bin/pg_version/Makefile @@ -0,0 +1,26 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/pg_version +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/pg_version/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..6844692be7 --- /dev/null +++ b/src/bin/pg_version/pg_version.c @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * pg_version.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/pg_version/Attic/pg_version.c,v 1.1.1.1 1996/07/09 06:22:14 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +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 index 0000000000..a99ab39f08 --- /dev/null +++ b/src/bin/pgtclsh/Makefile @@ -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: /cvsroot/pgsql/src/bin/pgtclsh/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..bbd89e012f --- /dev/null +++ b/src/bin/pgtclsh/README @@ -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 jolly@cs.berkeley.edu. diff --git a/src/bin/pgtclsh/pgtclAppInit.c b/src/bin/pgtclsh/pgtclAppInit.c new file mode 100644 index 0000000000..cc38ca34a7 --- /dev/null +++ b/src/bin/pgtclsh/pgtclAppInit.c @@ -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 + +/* + *---------------------------------------------------------------------- + * + * 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. */ +} + +/* + *---------------------------------------------------------------------- + * + * 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 index 0000000000..dff87a484f --- /dev/null +++ b/src/bin/pgtclsh/pgtclUtils.tcl @@ -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 index 0000000000..33763458b5 --- /dev/null +++ b/src/bin/pgtclsh/pgtkAppInit.c @@ -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 + +/* + *---------------------------------------------------------------------- + * + * 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. */ +} + +/* + *---------------------------------------------------------------------- + * + * 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 index 0000000000..62c9564fea --- /dev/null +++ b/src/bin/pgtclsh/updateStats.tcl @@ -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 index 0000000000..8e6ab5bb4f --- /dev/null +++ b/src/bin/psql/Makefile @@ -0,0 +1,65 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for bin/psql +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/bin/psql/Makefile,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 , +# 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 index 0000000000..d5dbfcea6f --- /dev/null +++ b/src/bin/psql/psql.c @@ -0,0 +1,1230 @@ +/*------------------------------------------------------------------------- + * + * psql.c-- + * an interactive front-end to postgres95 + * + * Copyright (c) 1996, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include +#include + +#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 +#include +#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 [] -- list tables in database or columns in
\n"); + fprintf(stderr,"\t \\d * -- list tables in database and columns in all tables\n"); + fprintf(stderr,"\t \\e [] -- edit the current query buffer or \n"); + fprintf(stderr,"\t \\f -- change field separator\n"); + fprintf(stderr,"\t \\g -- query to backend\n"); + fprintf(stderr,"\t \\h -- 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 -- read queries from filename\n"); + fprintf(stderr,"\t \\l -- list all databases\n"); + fprintf(stderr,"\t \\o [] -- send query results file named or stdout\n"); + fprintf(stderr,"\t \\p -- print the current query buffer\n"); + fprintf(stderr,"\t \\q -- quit\n"); + fprintf(stderr,"\t \\s [] -- save or print history\n"); + fprintf(stderr,"\t \\t -- toggle output field headers (defaults to on)\n"); + fprintf(stderr,"\t \\! [] -- 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 where 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; iqueryFout = 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 index 0000000000..e0d5077bc3 --- /dev/null +++ b/src/bin/psql/psqlHelp.h @@ -0,0 +1,168 @@ +/*------------------------------------------------------------------------- + * + * psqlHelp.h-- + * Help for query language syntax + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: psqlHelp.h,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +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 [*] add column ;\n\talter table [*] rename [column] to ;\n\talter table rename to "}, + { "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 on "}, + { "close", + "close an existing cursor (portal)", + "close ;"}, + { "commit", + "commit a transaction", + "commit [work]"}, + { "commit work", + "commit a transaction", + "commit [work]"}, + { "copy", + "copy data to and from a table", + "copy [binary] [nonulls] \n\t{to|from} {|stdin|stdout} [using delimiters ];"}, + { "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 [as] (basetype = , \n\t[sfunc1 = , stype1 = ]\n\t[sfunc2 = , stype2 = ]\n\t[,finalfunc = ]\n\t[,initcond1 = ][,initcond2 = ]);"}, + { "create database", + "create a database", + "create database "}, + { "create function", + "create a user-defined function", + "create function ([,...]) returns \n\tas ''|''\n\tlanguage 'c'|'sql'|'internal';"}, + { "create index", + "construct an index", + "create index on using (|(,...) );"}, + { "create operator", + "create a user-defined operator", + "create operator (\n\t[leftarg = ][,rightarg = ]\n\t,procedure = ,\n\t[,commutator = ][,negator = ]\n\t[,restrict = ][,hashes]\n\t[,join = ][,sort = ...]);"}, + { "create rule", + "define a new rule", + "create rule as on\n\t[select|update|delete|insert]\n\tto [where ]\n\tdo [instead] [|nothing| []];"}, + { "create table", + "create a new table", + "create table ( ,... )\n\t[inherits (,...\n\tarchive=\n\tstore=\n\tarch_store=];"}, + { "create type", + "create a new user-defined base data type", + "create type (\n\tinternallength = ( | variable),\n\t[externallength = (|variable),]\n\tinput=, output = \n\t[,element = ][,delimiter=][,default=\'\']\n\t[,send = ][,receive = ][,passedbyvalue]);"}, + { "create view", + "create a view", + "create view as select [as ][,... [as ]] [from ] [where ];"}, + { "declare", + "set up a cursor (portal)", + "declare [binary] cursor for\n\tselect [distinct]\n\t [as ],... [as ]\n\t[from ] [where ]\n\t[order by [using ],... [using ]];"}, + { "delete", + "delete tuples", + "delete from [where ];"}, + { "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 ;"}, + { "drop database", + "remove a database", + "drop database "}, + { "drop function", + "remove a user-defined function", + "drop function ([,....]);"}, + { "drop index", + "remove an existing index", + "drop index ;"}, + { "drop operator", + "remove a user-defined operator", + "drop operator ([|none],[|none]);"}, + { "drop rule", + "remove a rule", + "drop rule ;"}, + { "drop table", + "remove a table", + "drop table [,...;"}, + { "drop view", + "remove a view", + "drop view "}, + { "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}] "}, + { "extend index", + "extend a partial index", + "extend index [where ];"}, + { "fetch", + "retrieve tuples from a cursor (portal)", + "fetch [forward|backward] [|all] [in ];"}, + { "grant", + "grant access control to a user or group", + "grant on [,...] to \n[public | group | ]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"}, + { "insert", + "insert tuples", + "insert into [(...)]\n\t[values (...); |\n\tselect ,... [from ] [where ];"}, + { "listen", + "listen for notification on a relation", + "listen "}, + { "load", + "dynamically load a module", + "load ;"}, + { "notify", + "signal all frontends and backends listening on a relation", + "notify "}, + { "purge", + "purge historical data", + "purge [before ] [after ];"}, + { "revoke", + "revoke access control from a user or group", + "revoke on [,...] from \n[public | group | ]\n\t privilege is {ALL | SELECT | INSERT | UPDATE | DELETE | RULE}"}, + { "rollback", + "abort a transaction", + "rollback [work]"}, + { "select", + "retrieve tuples", + "select [distinct on ] [as ], ... [as ]\n\t[into table ] [from ]\n\t[where ]\n\t[order by \n\t\t[using ],.. [[using ] | ASC | DESC]];" }, + { "update", + "update tuples", + "update set =,...= [from ] [where ];"}, + { "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 index 0000000000..2640d3ce09 --- /dev/null +++ b/src/bin/psql/rlstubs.c @@ -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: /cvsroot/pgsql/src/bin/psql/Attic/rlstubs.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include + +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 index 0000000000..0a9043c633 --- /dev/null +++ b/src/bin/psql/stringutils.c @@ -0,0 +1,104 @@ +/*------------------------------------------------------------------------- + * + * stringutils.c-- + * simple string manipulation routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.1.1.1 1996/07/09 06:22:15 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#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 index 0000000000..d8564a02d0 --- /dev/null +++ b/src/bin/psql/stringutils.h @@ -0,0 +1,51 @@ +/*------------------------------------------------------------------------- + * + * stringutils.h-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: stringutils.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 +#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 index 0000000000..73e218b384 --- /dev/null +++ b/src/interfaces/libpgtcl/Makefile @@ -0,0 +1,38 @@ +#------------------------------------------------------------------------- +# +# Makefile +# Makefile for libpgtcl library +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..d2e2d59c79 --- /dev/null +++ b/src/interfaces/libpgtcl/README @@ -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 index 0000000000..923bf594d7 --- /dev/null +++ b/src/interfaces/libpgtcl/libpgtcl.h @@ -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: libpgtcl.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..449107339f --- /dev/null +++ b/src/interfaces/libpgtcl/pgtcl.c @@ -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: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtcl.c,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..e6c2c53911 --- /dev/null +++ b/src/interfaces/libpgtcl/pgtclCmds.c @@ -0,0 +1,812 @@ +/*------------------------------------------------------------------------- + * + * pgtclCmds.c-- + * C functions which implement pg_* tcl commands + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclCmds.c,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + + +#include +#include +#include +#include +#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)) { + 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 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 index 0000000000..244471ebe1 --- /dev/null +++ b/src/interfaces/libpgtcl/pgtclCmds.h @@ -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: pgtclCmds.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..00dffe7a88 --- /dev/null +++ b/src/interfaces/libpgtcl/pgtclId.c @@ -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: /cvsroot/pgsql/src/interfaces/libpgtcl/Attic/pgtclId.c,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#include +#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 index 0000000000..af9839ceb1 --- /dev/null +++ b/src/interfaces/libpgtcl/pgtclId.h @@ -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: pgtclId.h,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +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 index 0000000000..e1d58847ee --- /dev/null +++ b/src/interfaces/libpq++/Makefile @@ -0,0 +1,54 @@ +#------------------------------------------------------------------------- +# +# Makefile +# Makefile for libpq++ library +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/interfaces/libpq++/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..cb5d0aeddb --- /dev/null +++ b/src/interfaces/libpq++/README @@ -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 +(wwanders@sci.kun.nl) + +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 +jolly@cs.berkeley.edu + +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 index 0000000000..5e51d91587 --- /dev/null +++ b/src/interfaces/libpq++/examples/Makefile @@ -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 index 0000000000..76f3ea8071 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq0.cc @@ -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: /cvsroot/pgsql/src/interfaces/libpq++/examples/Attic/testlibpq0.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#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 index 0000000000..1d71f79558 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq1.cc @@ -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 +#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 index 0000000000..8eafea00b7 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq2.cc @@ -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 +#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 index 0000000000..f9c7410932 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq2.sql @@ -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 index 0000000000..1146dffac9 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq3.cc @@ -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 +#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 index 0000000000..f024c0b071 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq3.sql @@ -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 index 0000000000..9d5ca3ec76 --- /dev/null +++ b/src/interfaces/libpq++/examples/testlibpq4.cc @@ -0,0 +1,69 @@ +/* + * testlibpq4.cc + * Test the C++ version of LIBPQ, the POSTGRES frontend library. + * tests the copy in features + * + */ +#include +#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 index 0000000000..be3459794f --- /dev/null +++ b/src/interfaces/libpq++/examples/testlo.cc @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * lotest.cc-- + * test using large objects with libpq + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/interfaces/libpq++/examples/Attic/testlo.cc,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 index 0000000000..9b3e173900 --- /dev/null +++ b/src/interfaces/libpq++/libpq++.H @@ -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: libpq++.H,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#ifndef LIBPQXX_H +#define LIBPQXX_H + +#include +#include + +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 index 0000000000..ebca7ab7bb --- /dev/null +++ b/src/interfaces/libpq++/man/libpq++.3 @@ -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: libpq++.3,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $ +.\" +.\" ------------------------------------------------------------------ +.\" .(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 index 0000000000..777a12e8e7 --- /dev/null +++ b/src/interfaces/libpq++/pgconnection.cc @@ -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: /cvsroot/pgsql/src/interfaces/libpq++/Attic/pgconnection.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..aab4f213eb --- /dev/null +++ b/src/interfaces/libpq++/pgenv.cc @@ -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: /cvsroot/pgsql/src/interfaces/libpq++/Attic/pgenv.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#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 index 0000000000..20c7ad5e22 --- /dev/null +++ b/src/interfaces/libpq++/pglobject.cc @@ -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: /cvsroot/pgsql/src/interfaces/libpq++/Attic/pglobject.cc,v 1.1.1.1 1996/07/09 06:22:18 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..dc6318340e --- /dev/null +++ b/src/interfaces/libpq/Makefile @@ -0,0 +1,98 @@ +#------------------------------------------------------------------------- +# +# Makefile +# Makefile for libpq library +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/interfaces/libpq/Makefile,v 1.1.1.1 1996/07/09 06:22:16 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..e581950bf2 --- /dev/null +++ b/src/interfaces/libpq/README @@ -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 index 0000000000..cce02b64fc --- /dev/null +++ b/src/interfaces/libpq/fe-auth.c @@ -0,0 +1,544 @@ +/*------------------------------------------------------------------------- + * + * fe-auth.c-- + * The front-end (client) authorization routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +/* + * 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 +#include +#include /* for MAX{HOSTNAME,PATH}LEN, NOFILE */ +#include +#include +#include +#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: @ + */ + 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. Aoki@CS.BERKELEY.EDU + * 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: @ + */ + 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 + * / + * 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 index 0000000000..d5b8ecd8f7 --- /dev/null +++ b/src/interfaces/libpq/fe-auth.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * fe-auth.h + * + * Definitions for network authentication routines + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fe-auth.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#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 index 0000000000..97a909e105 --- /dev/null +++ b/src/interfaces/libpq/fe-connect.c @@ -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: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000000..78854ed73f --- /dev/null +++ b/src/interfaces/libpq/fe-exec.c @@ -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: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#include "postgres.h" +#include "libpq/pqcomm.h" +#include "libpq-fe.h" +#include + +/* 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;intups;i++) { + for (j=0;jnumAttributes;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;inumAttributes;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 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;ierrorMessage, + "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 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 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;inumAttributes;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 index 0000000000..c0c60d4a78 --- /dev/null +++ b/src/interfaces/libpq/fe-lobj.c @@ -0,0 +1,381 @@ +/*------------------------------------------------------------------------- + * + * fe-lobj.c-- + * Front-end large object interface + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include +#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 index 0000000000..c8b703b9d3 --- /dev/null +++ b/src/interfaces/libpq/fe-misc.c @@ -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: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#include +#include + +/* 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 index 0000000000..af3413b437 --- /dev/null +++ b/src/interfaces/libpq/libpq-fe.h @@ -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: libpq-fe.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#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 index 0000000000..22a83beb09 --- /dev/null +++ b/src/interfaces/libpq/pg_hba @@ -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. +# +#
+# +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 index 0000000000..638c494eda --- /dev/null +++ b/src/interfaces/libpq/pqsignal.c @@ -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: /cvsroot/pgsql/src/interfaces/libpq/pqsignal.c,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + * 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 index 0000000000..77d01b0a8e --- /dev/null +++ b/src/interfaces/libpq/pqsignal.h @@ -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: pqsignal.h,v 1.1.1.1 1996/07/09 06:22:17 scrappy Exp $ + * + * NOTES + * This shouldn't be in libpq, but the monitor and some other + * things need it... + * + *------------------------------------------------------------------------- + */ +#ifndef PQSIGNAL_H +#define PQSIGNAL_H + +#include + +#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 index 0000000000..122dd913f7 --- /dev/null +++ b/src/mk/port/postgres.mk.BSD44_derived @@ -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: postgres.mk.BSD44_derived,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $ +# +#------------------------------------------------------------------------- +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)/$( $(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 index 0000000000..c3d9071e3d --- /dev/null +++ b/src/mk/port/postgres.mk.alpha @@ -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: postgres.mk.alpha,v 1.1.1.1 1996/07/09 06:22:20 scrappy Exp $ +# +#------------------------------------------------------------------------- +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) $(. +# +# 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)/$( $(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 index 0000000000..4cd22439e0 --- /dev/null +++ b/src/mk/postgres.subdir.mk @@ -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: /cvsroot/pgsql/src/mk/Attic/postgres.subdir.mk,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $ +# +#------------------------------------------------------------------------- + +.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 index 0000000000..8b8c84c87f --- /dev/null +++ b/src/mk/postgres.user.mk @@ -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: /cvsroot/pgsql/src/mk/Attic/postgres.user.mk,v 1.1.1.1 1996/07/09 06:22:19 scrappy Exp $ +# +#------------------------------------------------------------------------- + +-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 index 0000000000..ed0bfd12cf --- /dev/null +++ b/src/test/Makefile @@ -0,0 +1,18 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for test suites +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/test/Makefile,v 1.1.1.1 1996/07/09 06:22:20 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBDIR= bench regress + +include ../mk/postgres.subdir.mk + + diff --git a/src/test/bench/Makefile b/src/test/bench/Makefile new file mode 100644 index 0000000000..65e612b73d --- /dev/null +++ b/src/test/bench/Makefile @@ -0,0 +1,62 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for the Wisconsin Benchmark +# +# Copyright (c) 1994-5, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/test/bench/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:21 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..7142802f92 --- /dev/null +++ b/src/test/bench/WISC-README @@ -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 index 0000000000..57563d8d4a --- /dev/null +++ b/src/test/bench/create.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# $Header: /cvsroot/pgsql/src/test/bench/Attic/create.sh,v 1.1.1.1 1996/07/09 06:22:21 scrappy Exp $ +# +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 index 0000000000..986ff3cf6c --- /dev/null +++ b/src/test/bench/create.source @@ -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 index 0000000000..4f0ba03ce6 --- /dev/null +++ b/src/test/bench/perquery @@ -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 index 0000000000..0ed3a7b1e5 --- /dev/null +++ b/src/test/bench/query01 @@ -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 index 0000000000..2d253ac1ef --- /dev/null +++ b/src/test/bench/query02 @@ -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 index 0000000000..0ed3a7b1e5 --- /dev/null +++ b/src/test/bench/query03 @@ -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 index 0000000000..2d253ac1ef --- /dev/null +++ b/src/test/bench/query04 @@ -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 index 0000000000..0ed3a7b1e5 --- /dev/null +++ b/src/test/bench/query05 @@ -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 index 0000000000..209c1321e6 --- /dev/null +++ b/src/test/bench/query06 @@ -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 index 0000000000..ae8fccb3c0 --- /dev/null +++ b/src/test/bench/query07 @@ -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 index 0000000000..531064829b --- /dev/null +++ b/src/test/bench/query08 @@ -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 index 0000000000..66d33ca1ad --- /dev/null +++ b/src/test/bench/query09 @@ -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 index 0000000000..de372aa647 --- /dev/null +++ b/src/test/bench/query10 @@ -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 index 0000000000..abbce829a8 --- /dev/null +++ b/src/test/bench/query11 @@ -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 index 0000000000..d1b4611fee --- /dev/null +++ b/src/test/bench/query12 @@ -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 index 0000000000..de372aa647 --- /dev/null +++ b/src/test/bench/query13 @@ -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 index 0000000000..abbce829a8 --- /dev/null +++ b/src/test/bench/query14 @@ -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 index 0000000000..be5fb5b45f --- /dev/null +++ b/src/test/bench/query15 @@ -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 index 0000000000..6e36649030 --- /dev/null +++ b/src/test/bench/query16 @@ -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 index 0000000000..e3b501ba3f --- /dev/null +++ b/src/test/bench/query17 @@ -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 index 0000000000..92fe5f79b2 --- /dev/null +++ b/src/test/bench/query18 @@ -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 index 0000000000..1ed160d7ab --- /dev/null +++ b/src/test/bench/query19 @@ -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 index 0000000000..e544ea63f4 --- /dev/null +++ b/src/test/bench/query20 @@ -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 index 0000000000..e69de29bb2 diff --git a/src/test/bench/query22 b/src/test/bench/query22 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/test/bench/query23 b/src/test/bench/query23 new file mode 100644 index 0000000000..e544ea63f4 --- /dev/null +++ b/src/test/bench/query23 @@ -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 index 0000000000..e69de29bb2 diff --git a/src/test/bench/query25 b/src/test/bench/query25 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/test/bench/query26 b/src/test/bench/query26 new file mode 100644 index 0000000000..715c4c3744 --- /dev/null +++ b/src/test/bench/query26 @@ -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 index 0000000000..c837735dcd --- /dev/null +++ b/src/test/bench/query27 @@ -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 index 0000000000..c3d01c92cf --- /dev/null +++ b/src/test/bench/query28 @@ -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 index 0000000000..3600685c6a --- /dev/null +++ b/src/test/bench/query29 @@ -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 index 0000000000..e8b37e6771 --- /dev/null +++ b/src/test/bench/query30 @@ -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 index 0000000000..cbdb85d3da --- /dev/null +++ b/src/test/bench/query31 @@ -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 index 0000000000..3b4159c93b --- /dev/null +++ b/src/test/bench/query32 @@ -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 index 0000000000..0d5afa389e --- /dev/null +++ b/src/test/bench/runwisc.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# $Header: /cvsroot/pgsql/src/test/bench/Attic/runwisc.sh,v 1.1.1.1 1996/07/09 06:22:23 scrappy Exp $ +# +# 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 index 0000000000..b684449436 --- /dev/null +++ b/src/test/bench/wholebench.sh @@ -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 index 0000000000..fb79062bc2 --- /dev/null +++ b/src/test/examples/Makefile @@ -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 index 0000000000..f3b9964416 --- /dev/null +++ b/src/test/examples/testlibpq.c @@ -0,0 +1,118 @@ +/* + * testlibpq.c + * Test the C version of LIBPQ, the POSTGRES frontend library. + * + * + */ +#include +#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 index 0000000000..801feec9bc --- /dev/null +++ b/src/test/examples/testlibpq2.c @@ -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 +#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 index 0000000000..f9c7410932 --- /dev/null +++ b/src/test/examples/testlibpq2.sql @@ -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 index 0000000000..bab22b3b1b --- /dev/null +++ b/src/test/examples/testlibpq3.c @@ -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 +#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 index 0000000000..f024c0b071 --- /dev/null +++ b/src/test/examples/testlibpq3.sql @@ -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 index 0000000000..5e04097119 --- /dev/null +++ b/src/test/examples/testlibpq4.c @@ -0,0 +1,127 @@ +/* + * testlibpq4.c + * this test programs shows to use LIBPQ to make multiple backend + * connections + * + * + */ +#include +#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 index 0000000000..ac650a9dc5 --- /dev/null +++ b/src/test/examples/testlo.c @@ -0,0 +1,232 @@ +/*------------------------------------------------------------------------- + * + * testlo.c-- + * test using large objects with libpq + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/test/examples/testlo.c,v 1.1.1.1 1996/07/09 06:22:23 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 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 index 0000000000..3756a158e3 --- /dev/null +++ b/src/test/examples/testlo2.c @@ -0,0 +1,233 @@ +/*------------------------------------------------------------------------- + * + * lotest.c-- + * test using large objects with libpq + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/test/examples/Attic/testlo2.c,v 1.1.1.1 1996/07/09 06:22:23 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#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 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 index 0000000000..852ffc5767 --- /dev/null +++ b/src/test/regress/Makefile @@ -0,0 +1,62 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for regress (the regression test) +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/test/regress/Makefile,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..f3fb711dc7 --- /dev/null +++ b/src/test/regress/create.source @@ -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 index 0000000000..c35dd575b0 --- /dev/null +++ b/src/test/regress/data/dept.data @@ -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 index 0000000000..4ff1457e90 --- /dev/null +++ b/src/test/regress/data/desc.data @@ -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 index 0000000000..5fc17ffa84 --- /dev/null +++ b/src/test/regress/data/emp.data @@ -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 index 0000000000..97e97095a0 --- /dev/null +++ b/src/test/regress/data/hash.data @@ -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 index 0000000000..1605bbec8d --- /dev/null +++ b/src/test/regress/data/onek.data @@ -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 index 0000000000..57a8fa6038 --- /dev/null +++ b/src/test/regress/data/person.data @@ -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 index 0000000000..f4365f2b6a --- /dev/null +++ b/src/test/regress/data/real_city.data @@ -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 index 0000000000..a8d36d02ca --- /dev/null +++ b/src/test/regress/data/rect.data @@ -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 index 0000000000..929d7490a5 --- /dev/null +++ b/src/test/regress/data/streets.data @@ -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 index 0000000000..315cefed4e --- /dev/null +++ b/src/test/regress/data/stud_emp.data @@ -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 index 0000000000..f7e29e460e --- /dev/null +++ b/src/test/regress/data/student.data @@ -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 index 0000000000..c9064c9c03 --- /dev/null +++ b/src/test/regress/data/tenk.data @@ -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 index 0000000000..3db4b0daea --- /dev/null +++ b/src/test/regress/destroy.source @@ -0,0 +1,285 @@ +-- +-- destroy.source +-- +-- $Header: /cvsroot/pgsql/src/test/regress/Attic/destroy.source,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $ +-- + +-- +-- 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 index 0000000000..a93f7f00c5 --- /dev/null +++ b/src/test/regress/errors.source @@ -0,0 +1,275 @@ +-- +-- errors.source +-- +-- $Header: /cvsroot/pgsql/src/test/regress/Attic/errors.source,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $ + + +-- 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 index 0000000000..48e084b1de --- /dev/null +++ b/src/test/regress/queries.source @@ -0,0 +1,2614 @@ +-- +-- queries.source +-- +-- $Header: /cvsroot/pgsql/src/test/regress/Attic/queries.source,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $ +-- +-- 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 index 0000000000..71d400f93d --- /dev/null +++ b/src/test/regress/regress.c @@ -0,0 +1,271 @@ +/* + * $Header: /cvsroot/pgsql/src/test/regress/regress.c,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $ + */ + +#include /* faked on sunos */ +#include + +#include "utils/geo-decls.h" /* includes */ +#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; inpts; 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 index 0000000000..fb17ab7a72 --- /dev/null +++ b/src/test/regress/regress.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# $Header: /cvsroot/pgsql/src/test/regress/Attic/regress.sh,v 1.1.1.1 1996/07/09 06:22:24 scrappy Exp $ +# +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 index 0000000000..1caf4c79ab --- /dev/null +++ b/src/test/regress/sample.regress.out @@ -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 index 0000000000..816457441e --- /dev/null +++ b/src/test/regress/security.source @@ -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 index 0000000000..3be4047c4b --- /dev/null +++ b/src/test/suite/README @@ -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 index 0000000000..0185cd1d38 --- /dev/null +++ b/src/test/suite/agg.sql @@ -0,0 +1,76 @@ +--------------------------------------------------------------------------- +-- +-- agg.sql- +-- test aggregates +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: agg.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..e86c98f5e3 --- /dev/null +++ b/src/test/suite/date.sql @@ -0,0 +1,30 @@ +--------------------------------------------------------------------------- +-- +-- date.sql- +-- test DATE adt +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: date.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..060a0fbcb8 --- /dev/null +++ b/src/test/suite/float.sql @@ -0,0 +1,113 @@ +--------------------------------------------------------------------------- +-- +-- float.sql- +-- test float4, float8 adt +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: float.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- +-- 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 index 0000000000..491b9cc5ba --- /dev/null +++ b/src/test/suite/group.sql @@ -0,0 +1,100 @@ +--------------------------------------------------------------------------- +-- +-- group.sql- +-- test GROUP BY (with aggregates) +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: group.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..36600ce1f8 --- /dev/null +++ b/src/test/suite/group_err.sql @@ -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: group_err.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..4b7c35726e --- /dev/null +++ b/src/test/suite/inh.sql @@ -0,0 +1,73 @@ +--------------------------------------------------------------------------- +-- +-- inh.sql- +-- checks inheritance +-- +-- +-- Copyright (c) 1994, Regents of the University of California +-- +-- $Id: inh.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..288b21ebc1 --- /dev/null +++ b/src/test/suite/join.sql @@ -0,0 +1,40 @@ +--------------------------------------------------------------------------- +-- +-- joins.sql- +-- test joins +-- +-- +-- Copyright (c) 1994, Regents of the University of California +-- +-- $Id: join.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..78f7f33c6d --- /dev/null +++ b/src/test/suite/oper.sql @@ -0,0 +1,27 @@ +--------------------------------------------------------------------------- +-- +-- oper.sql- +-- test operators +-- +-- +-- Copyright (c) 1994, Regents of the University of California +-- +-- $Id: oper.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- 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 index 0000000000..d9832c8bc0 --- /dev/null +++ b/src/test/suite/parse.sql @@ -0,0 +1,45 @@ +--------------------------------------------------------------------------- +-- +-- parse.sql- +-- checks the parser +-- +-- +-- Copyright (c) 1994, Regents of the University of California +-- +-- $Id: parse.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..0040bebb9b --- /dev/null +++ b/src/test/suite/quote.sql @@ -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 index 0000000000..a3b41ee485 --- /dev/null +++ b/src/test/suite/results/agg.sql.out @@ -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 index 0000000000..ffd785fc52 --- /dev/null +++ b/src/test/suite/results/date.sql.out @@ -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 index 0000000000..49bce672c9 --- /dev/null +++ b/src/test/suite/results/float.sql.out @@ -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 index 0000000000..5730a6e441 --- /dev/null +++ b/src/test/suite/results/group.sql.out @@ -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 index 0000000000..ea0f15cd23 --- /dev/null +++ b/src/test/suite/results/group_err.sql.out @@ -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 index 0000000000..658028c0bc --- /dev/null +++ b/src/test/suite/results/inh.sql.out @@ -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 index 0000000000..423aa3dd3b --- /dev/null +++ b/src/test/suite/results/join.sql.out @@ -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 index 0000000000..d7b76bea83 --- /dev/null +++ b/src/test/suite/results/oper.sql.out @@ -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 index 0000000000..0a97f3055f --- /dev/null +++ b/src/test/suite/results/parse.sql.out @@ -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 index 0000000000..cba95800cd --- /dev/null +++ b/src/test/suite/results/quote.sql.out @@ -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 index 0000000000..b39e7af81c --- /dev/null +++ b/src/test/suite/results/rules.sql.out @@ -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 index 0000000000..ba4c75de98 --- /dev/null +++ b/src/test/suite/results/select.sql.out @@ -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 index 0000000000..322ce59cf6 --- /dev/null +++ b/src/test/suite/results/sort.sql.out @@ -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 index 0000000000..76aee7945f --- /dev/null +++ b/src/test/suite/results/sqlcompat.sql.out @@ -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 index 0000000000..9cc3a27060 --- /dev/null +++ b/src/test/suite/results/time.sql.out @@ -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 index 0000000000..535ef8f0ca --- /dev/null +++ b/src/test/suite/results/varchar.sql.out @@ -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 index 0000000000..42b45b2cd3 --- /dev/null +++ b/src/test/suite/results/views.sql.out @@ -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 index 0000000000..c537d50843 --- /dev/null +++ b/src/test/suite/rules.sql @@ -0,0 +1,29 @@ +--------------------------------------------------------------------------- +-- +-- rules.sql- +-- test rules +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: rules.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- 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 index 0000000000..d657e89497 --- /dev/null +++ b/src/test/suite/runall @@ -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 index 0000000000..5ede1dc159 --- /dev/null +++ b/src/test/suite/select.sql @@ -0,0 +1,31 @@ +--------------------------------------------------------------------------- +-- +-- select.sql- +-- test select +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: select.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- 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 index 0000000000..c5a334eeac --- /dev/null +++ b/src/test/suite/sort.sql @@ -0,0 +1,57 @@ +--------------------------------------------------------------------------- +-- +-- sort.sql- +-- test sorting +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: sort.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..456f220aa7 --- /dev/null +++ b/src/test/suite/sqlcompat.sql @@ -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: sqlcompat.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- 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 index 0000000000..aeaeaf1dd4 --- /dev/null +++ b/src/test/suite/time.sql @@ -0,0 +1,30 @@ +--------------------------------------------------------------------------- +-- +-- time.sql- +-- test TIME adt +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: time.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +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 index 0000000000..0e83172208 --- /dev/null +++ b/src/test/suite/varchar.sql @@ -0,0 +1,64 @@ +--------------------------------------------------------------------------- +-- +-- varchar.sql- +-- test CHAR() and VARCHAR() adts +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: varchar.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- 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 index 0000000000..987df324b7 --- /dev/null +++ b/src/test/suite/views.sql @@ -0,0 +1,77 @@ +--------------------------------------------------------------------------- +-- +-- views.sql- +-- test views queries +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: views.sql,v 1.1.1.1 1996/07/09 06:22:30 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- 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 index 0000000000..70275cc3b8 --- /dev/null +++ b/src/tools/mkldexport/Makefile @@ -0,0 +1,20 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for tools/mkldexport +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/tools/mkldexport/Attic/Makefile,v 1.1.1.1 1996/07/09 06:22:32 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..83415d7fb3 --- /dev/null +++ b/src/tools/mkldexport/README @@ -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 index 0000000000..cd07a453eb --- /dev/null +++ b/src/tools/mkldexport/mkldexport.sh @@ -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 index 0000000000..0fe289c8d2 --- /dev/null +++ b/src/tutorial/C-code/beard.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * beard.c-- + * sample routines to use large objects + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/tutorial/C-code/Attic/beard.c,v 1.1.1.1 1996/07/09 06:22:34 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +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 index 0000000000..bebdd511d4 --- /dev/null +++ b/src/tutorial/C-code/complex.c @@ -0,0 +1,150 @@ +#include +/* 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_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 index 0000000000..f91b4d6205 --- /dev/null +++ b/src/tutorial/C-code/funcs.c @@ -0,0 +1,56 @@ +#include +#include +#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 index 0000000000..5a29b1468d --- /dev/null +++ b/src/tutorial/Makefile @@ -0,0 +1,39 @@ +#------------------------------------------------------------------------- +# +# Makefile-- +# Makefile for tutorial/C-code +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/tutorial/Makefile,v 1.1.1.1 1996/07/09 06:22:33 scrappy Exp $ +# +#------------------------------------------------------------------------- + +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 index 0000000000..b35f7b2b07 --- /dev/null +++ b/src/tutorial/README @@ -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 psql command. + diff --git a/src/tutorial/advanced.source b/src/tutorial/advanced.source new file mode 100644 index 0000000000..6e4c7f1e9b --- /dev/null +++ b/src/tutorial/advanced.source @@ -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: advanced.source,v 1.1.1.1 1996/07/09 06:22:34 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +----------------------------- +-- 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 index 0000000000..fcda8b3b8c --- /dev/null +++ b/src/tutorial/basics.source @@ -0,0 +1,188 @@ +--------------------------------------------------------------------------- +-- +-- basics.sql- +-- Tutorial on the basics (table creation and data manipulation) +-- +-- +-- Copyright (c) 1994, Andrew Yu, University of California +-- +-- $Id: basics.source,v 1.1.1.1 1996/07/09 06:22:34 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +----------------------------- +-- 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 index 0000000000..af8bd26cc2 --- /dev/null +++ b/src/tutorial/complex.source @@ -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: complex.source,v 1.1.1.1 1996/07/09 06:22:34 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +----------------------------- +-- 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 index 0000000000..00f256ea85 --- /dev/null +++ b/src/tutorial/funcs.source @@ -0,0 +1,158 @@ +--------------------------------------------------------------------------- +-- +-- funcs.sql- +-- Tutorial on using functions in POSTGRES. +-- +-- +-- Copyright (c) 1994-5, Regents of the University of California +-- +-- $Id: funcs.source,v 1.1.1.1 1996/07/09 06:22:34 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +----------------------------- +-- 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 index 0000000000..90ed0e4ec5 --- /dev/null +++ b/src/tutorial/syscat.source @@ -0,0 +1,151 @@ +--------------------------------------------------------------------------- +-- +-- syscat.sql- +-- sample queries to the system catalogs +-- +-- +-- Copyright (c) 1994, Regents of the University of California +-- +-- $Id: syscat.source,v 1.1.1.1 1996/07/09 06:22:34 scrappy Exp $ +-- +--------------------------------------------------------------------------- + +-- +-- 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; + +