diff options
author | CVS to git conversion script <webmaster@postgresql.org> | 2002-06-20 20:29:55 +0000 |
---|---|---|
committer | CVS to git conversion script <webmaster@postgresql.org> | 2002-06-20 20:29:55 +0000 |
commit | 05dedace84ef8c93fea5309c3ac55f889011429b (patch) | |
tree | 4c7f513afbbc03060039a165d1770e11be711773 /src/backend/executor | |
parent | d84fe82230c593f3dc5d7f427849b99d1efa8a0a (diff) |
This commit was manufactured by cvs2git to create branch 'ecpg_big_bison'.
Sprout from master 2002-06-20 20:29:54 UTC Bruce Momjian <bruce@momjian.us> 'Update copyright to 2002.'
Delete:
COPYRIGHT
GNUmakefile.in
HISTORY
INSTALL
Makefile
README
aclocal.m4
config/ac_func_accept_argtypes.m4
config/c-compiler.m4
config/c-library.m4
config/config.guess
config/config.sub
config/cxx.m4
config/docbook.m4
config/general.m4
config/install-sh
config/java.m4
config/libtool.m4
config/missing
config/mkinstalldirs
config/perl.m4
config/prep_buildtree
config/programs.m4
config/python.m4
config/tcl.m4
configure
configure.in
contrib/Makefile
contrib/README
contrib/array/Makefile
contrib/array/README.array_iterator
contrib/array/array_iterator.c
contrib/array/array_iterator.h
contrib/array/array_iterator.sql.in
contrib/btree_gist/Makefile
contrib/btree_gist/README.btree_gist
contrib/btree_gist/btree_gist.c
contrib/btree_gist/btree_gist.sql.in
contrib/btree_gist/data/test_btree.data
contrib/btree_gist/data/test_btree_ts.data
contrib/btree_gist/expected/btree_gist.out
contrib/btree_gist/sql/btree_gist.sql
contrib/chkpass/Makefile
contrib/chkpass/README.chkpass
contrib/chkpass/chkpass.c
contrib/chkpass/chkpass.sql.in
contrib/contrib-global.mk
contrib/cube/Makefile
contrib/cube/README.cube
contrib/cube/buffer.c
contrib/cube/buffer.h
contrib/cube/cube.c
contrib/cube/cube.sql.in
contrib/cube/cubedata.h
contrib/cube/cubeparse.y
contrib/cube/cubescan.l
contrib/cube/data/test_cube.data
contrib/cube/expected/cube.out
contrib/cube/sql/cube.sql
contrib/dbase/Makefile
contrib/dbase/README.dbf2pg
contrib/dbase/dbf.c
contrib/dbase/dbf.h
contrib/dbase/dbf2pg.1
contrib/dbase/dbf2pg.c
contrib/dbase/endian.c
contrib/dblink/Makefile
contrib/dblink/README.dblink
contrib/dblink/dblink.c
contrib/dblink/dblink.h
contrib/dblink/dblink.sql.in
contrib/dbsize/Makefile
contrib/dbsize/README.dbsize
contrib/dbsize/dbsize.c
contrib/dbsize/dbsize.sql.in
contrib/earthdistance/Makefile
contrib/earthdistance/README.earthdistance
contrib/earthdistance/earthdistance.c
contrib/earthdistance/earthdistance.sql.in
contrib/findoidjoins/Makefile
contrib/findoidjoins/README.findoidjoins
contrib/findoidjoins/findoidjoins.c
contrib/findoidjoins/make_oidjoins_check
contrib/fulltextindex/Makefile
contrib/fulltextindex/README.fti
contrib/fulltextindex/TODO
contrib/fulltextindex/fti.c
contrib/fulltextindex/fti.pl
contrib/fulltextindex/fti.sql.in
contrib/fulltextindex/timings.sh
contrib/fuzzystrmatch/Makefile
contrib/fuzzystrmatch/README.fuzzystrmatch
contrib/fuzzystrmatch/README.soundex
contrib/fuzzystrmatch/fuzzystrmatch.c
contrib/fuzzystrmatch/fuzzystrmatch.h
contrib/fuzzystrmatch/fuzzystrmatch.sql.in
contrib/intagg/Makefile
contrib/intagg/README.int_aggregate
contrib/intagg/int_aggregate.c
contrib/intagg/int_aggregate.sql.in
contrib/intarray/Makefile
contrib/intarray/README.intarray
contrib/intarray/_int.c
contrib/intarray/_int.sql.in
contrib/intarray/bench/bench.pl
contrib/intarray/bench/create_test.pl
contrib/intarray/data/test__int.data
contrib/intarray/expected/_int.out
contrib/intarray/sql/_int.sql
contrib/ipc_check/README
contrib/ipc_check/ipc_check.pl
contrib/isbn_issn/Makefile
contrib/isbn_issn/README.isbn_issn
contrib/isbn_issn/isbn_issn.c
contrib/isbn_issn/isbn_issn.sql.in
contrib/lo/Makefile
contrib/lo/README.lo
contrib/lo/lo.c
contrib/lo/lo.sql.in
contrib/lo/lo_drop.sql
contrib/lo/lo_test.sql
contrib/mSQL-interface/Makefile
contrib/mSQL-interface/README.mpgsql
contrib/mSQL-interface/mpgsql.c
contrib/mac/README.mac
contrib/mac/createoui
contrib/mac/dropoui
contrib/mac/ouiparse.awk
contrib/mac/updateoui
contrib/miscutil/Makefile
contrib/miscutil/README.misc_utils
contrib/miscutil/misc_utils.c
contrib/miscutil/misc_utils.h
contrib/miscutil/misc_utils.sql.in
contrib/mysql/README
contrib/mysql/my2pg.pl
contrib/mysql/mysql2pgsql
contrib/noupdate/Makefile
contrib/noupdate/README.noup
contrib/noupdate/noup.c
contrib/noupdate/noup.sql.in
contrib/oid2name/Makefile
contrib/oid2name/README.oid2name
contrib/oid2name/oid2name.c
contrib/oracle/CHANGES
contrib/oracle/Ora2Pg.pm
contrib/oracle/README.ora2pg
contrib/oracle/TODO
contrib/oracle/ora2pg.html
contrib/oracle/ora2pg.pl
contrib/pg_controldata/Makefile
contrib/pg_controldata/README.pg_controldata
contrib/pg_controldata/pg_controldata.c
contrib/pg_dumplo/Makefile
contrib/pg_dumplo/README.pg_dumplo
contrib/pg_dumplo/lo_export.c
contrib/pg_dumplo/lo_import.c
contrib/pg_dumplo/main.c
contrib/pg_dumplo/pg_dumplo.h
contrib/pg_dumplo/utils.c
contrib/pg_logger/Makefile
contrib/pg_logger/README.pg_logger
contrib/pg_logger/pg_logger.c
contrib/pg_resetxlog/Makefile
contrib/pg_resetxlog/README.pg_resetxlog
contrib/pg_resetxlog/pg_resetxlog.c
contrib/pg_upgrade/README
contrib/pg_upgrade/pg_upgrade
contrib/pg_upgrade/pg_upgrade.1
contrib/pgbench/Makefile
contrib/pgbench/README.pgbench
contrib/pgbench/README.pgbench_jis
contrib/pgbench/pgbench.c
contrib/pgcrypto/API
contrib/pgcrypto/Makefile
contrib/pgcrypto/README.pgcrypto
contrib/pgcrypto/blf.c
contrib/pgcrypto/blf.h
contrib/pgcrypto/crypt-blowfish.c
contrib/pgcrypto/crypt-des.c
contrib/pgcrypto/crypt-gensalt.c
contrib/pgcrypto/crypt-md5.c
contrib/pgcrypto/expected/blowfish.out
contrib/pgcrypto/expected/crypt-blowfish.out
contrib/pgcrypto/expected/crypt-des.out
contrib/pgcrypto/expected/crypt-md5.out
contrib/pgcrypto/expected/crypt-xdes.out
contrib/pgcrypto/expected/hmac-md5.out
contrib/pgcrypto/expected/hmac-sha1.out
contrib/pgcrypto/expected/init.out
contrib/pgcrypto/expected/md5.out
contrib/pgcrypto/expected/rijndael.out
contrib/pgcrypto/expected/sha1.out
contrib/pgcrypto/internal.c
contrib/pgcrypto/md5.c
contrib/pgcrypto/md5.h
contrib/pgcrypto/mhash.c
contrib/pgcrypto/misc.c
contrib/pgcrypto/openssl.c
contrib/pgcrypto/pgcrypto.c
contrib/pgcrypto/pgcrypto.h
contrib/pgcrypto/pgcrypto.sql.in
contrib/pgcrypto/px-crypt.c
contrib/pgcrypto/px-crypt.h
contrib/pgcrypto/px-hmac.c
contrib/pgcrypto/px.c
contrib/pgcrypto/px.h
contrib/pgcrypto/random.c
contrib/pgcrypto/rijndael.c
contrib/pgcrypto/rijndael.h
contrib/pgcrypto/rijndael.tbl
contrib/pgcrypto/sha1.c
contrib/pgcrypto/sha1.h
contrib/pgcrypto/sql/blowfish.sql
contrib/pgcrypto/sql/crypt-blowfish.sql
contrib/pgcrypto/sql/crypt-des.sql
contrib/pgcrypto/sql/crypt-md5.sql
contrib/pgcrypto/sql/crypt-xdes.sql
contrib/pgcrypto/sql/hmac-md5.sql
contrib/pgcrypto/sql/hmac-sha1.sql
contrib/pgcrypto/sql/init.sql
contrib/pgcrypto/sql/md5.sql
contrib/pgcrypto/sql/rijndael.sql
contrib/pgcrypto/sql/sha1.sql
contrib/pgstattuple/Makefile
contrib/pgstattuple/README.pgstattuple
contrib/pgstattuple/README.pgstattuple.euc_jp
contrib/pgstattuple/pgstattuple.c
contrib/pgstattuple/pgstattuple.sql.in
contrib/retep/CHANGELOG
contrib/retep/Implementation
contrib/retep/Makefile
contrib/retep/README
contrib/retep/build.xml
contrib/retep/data/cds.dtd
contrib/retep/data/cds.xml
contrib/retep/retep.jpx
contrib/retep/uk/org/retep/dtu/DCollection.java
contrib/retep/uk/org/retep/dtu/DConstants.java
contrib/retep/uk/org/retep/dtu/DElement.java
contrib/retep/uk/org/retep/dtu/DEnvironment.java
contrib/retep/uk/org/retep/dtu/DModule.java
contrib/retep/uk/org/retep/dtu/DModuleXML.java
contrib/retep/uk/org/retep/dtu/DNode.java
contrib/retep/uk/org/retep/dtu/DProcessor.java
contrib/retep/uk/org/retep/dtu/DTransform.java
contrib/retep/uk/org/retep/tools/Tool.java
contrib/retep/uk/org/retep/tools.properties
contrib/retep/uk/org/retep/util/ExceptionDialog.java
contrib/retep/uk/org/retep/util/Globals.java
contrib/retep/uk/org/retep/util/Logger.java
contrib/retep/uk/org/retep/util/Main.java
contrib/retep/uk/org/retep/util/StandaloneApp.java
contrib/retep/uk/org/retep/util/hba/Editor.java
contrib/retep/uk/org/retep/util/hba/Main.java
contrib/retep/uk/org/retep/util/hba/Record.java
contrib/retep/uk/org/retep/util/misc/IPAddress.java
contrib/retep/uk/org/retep/util/misc/PropertiesIO.java
contrib/retep/uk/org/retep/util/misc/WStringTokenizer.java
contrib/retep/uk/org/retep/util/models/HBATableModel.java
contrib/retep/uk/org/retep/util/models/PropertiesTableModel.java
contrib/retep/uk/org/retep/util/proped/Main.java
contrib/retep/uk/org/retep/util/proped/PropertyEditor.java
contrib/retep/uk/org/retep/xml/core/XMLFactory.java
contrib/retep/uk/org/retep/xml/core/XMLFactoryException.java
contrib/retep/uk/org/retep/xml/jdbc/XMLDatabase.java
contrib/retep/uk/org/retep/xml/jdbc/XMLResultSet.java
contrib/retep/uk/org/retep/xml/parser/TagHandler.java
contrib/retep/uk/org/retep/xml/parser/TagListener.java
contrib/retep/uk/org/retep/xml/test/XMLExport.java
contrib/rserv/ApplySnapshot.in
contrib/rserv/CleanLog.in
contrib/rserv/GetSyncID.in
contrib/rserv/InitRservTest.in
contrib/rserv/Makefile
contrib/rserv/MasterAddTable.in
contrib/rserv/MasterInit.in
contrib/rserv/MasterSync.in
contrib/rserv/PrepareSnapshot.in
contrib/rserv/README.rserv
contrib/rserv/RServ.pm
contrib/rserv/Replicate.in
contrib/rserv/RservTest.in
contrib/rserv/SlaveAddTable.in
contrib/rserv/SlaveInit.in
contrib/rserv/master.sql.in
contrib/rserv/regress.sh
contrib/rserv/rserv.c
contrib/rserv/slave.sql.in
contrib/rtree_gist/Makefile
contrib/rtree_gist/README.rtree_gist
contrib/rtree_gist/bench/bench.pl
contrib/rtree_gist/bench/create_test.pl
contrib/rtree_gist/data/test_box.data
contrib/rtree_gist/expected/rtree_gist.out
contrib/rtree_gist/rtree_gist.c
contrib/rtree_gist/rtree_gist.sql.in
contrib/rtree_gist/sql/rtree_gist.sql
contrib/seg/Makefile
contrib/seg/README.seg
contrib/seg/buffer.c
contrib/seg/buffer.h
contrib/seg/data/test_seg.data
contrib/seg/expected/seg.out
contrib/seg/seg-validate.pl
contrib/seg/seg.c
contrib/seg/seg.sql.in
contrib/seg/segdata.h
contrib/seg/segparse.y
contrib/seg/segscan.l
contrib/seg/sort-segments.pl
contrib/seg/sql/seg.sql
contrib/spi/Makefile
contrib/spi/README.spi
contrib/spi/autoinc.c
contrib/spi/autoinc.example
contrib/spi/autoinc.sql.in
contrib/spi/insert_username.c
contrib/spi/insert_username.example
contrib/spi/insert_username.sql.in
contrib/spi/moddatetime.c
contrib/spi/moddatetime.example
contrib/spi/moddatetime.sql.in
contrib/spi/preprocessor/README.MAX
contrib/spi/preprocessor/example.sql
contrib/spi/preprocessor/step1.c
contrib/spi/preprocessor/step1.e
contrib/spi/preprocessor/step2.pl
contrib/spi/refint.c
contrib/spi/refint.example
contrib/spi/refint.sql.in
contrib/spi/timetravel.c
contrib/spi/timetravel.example
contrib/spi/timetravel.sql.in
contrib/start-scripts/freebsd
contrib/start-scripts/linux
contrib/string/Makefile
contrib/string/README.string_io
contrib/string/string_io.c
contrib/string/string_io.h
contrib/string/string_io.sql.in
contrib/tips/Makefile
contrib/tips/README.apachelog
contrib/tools/add-emacs-variables
contrib/tools/find-sources
contrib/tools/make-tags
contrib/tsearch/Makefile
contrib/tsearch/README.tsearch
contrib/tsearch/crc32.c
contrib/tsearch/crc32.h
contrib/tsearch/data/test_tsearch.data
contrib/tsearch/deflex.h
contrib/tsearch/dict/porter_english.dct
contrib/tsearch/dict/russian_stemming.dct
contrib/tsearch/dict.h
contrib/tsearch/expected/tsearch.out
contrib/tsearch/gistidx.c
contrib/tsearch/gistidx.h
contrib/tsearch/makedict/makedict.pl
contrib/tsearch/morph.c
contrib/tsearch/morph.h
contrib/tsearch/parser.h
contrib/tsearch/parser.l
contrib/tsearch/query.c
contrib/tsearch/query.h
contrib/tsearch/rewrite.c
contrib/tsearch/rewrite.h
contrib/tsearch/sql/tsearch.sql
contrib/tsearch/tsearch.sql.in
contrib/tsearch/txtidx.c
contrib/tsearch/txtidx.h
contrib/userlock/Makefile
contrib/userlock/README.user_locks
contrib/userlock/user_locks.c
contrib/userlock/user_locks.h
contrib/userlock/user_locks.sql.in
contrib/vacuumlo/Makefile
contrib/vacuumlo/README.vacuumlo
contrib/vacuumlo/vacuumlo.c
contrib/xml/Makefile
contrib/xml/README
contrib/xml/TODO
contrib/xml/pgxml.c
contrib/xml/pgxml.h
contrib/xml/pgxml.source
contrib/xml/pgxml_dom.c
contrib/xml/pgxml_dom.source
doc/FAQ
doc/FAQ_AIX
doc/FAQ_DEV
doc/FAQ_HPUX
doc/FAQ_IRIX
doc/FAQ_MSWIN
doc/FAQ_QNX4
doc/FAQ_SCO
doc/FAQ_Solaris
doc/FAQ_german
doc/FAQ_japanese
doc/FAQ_polish
doc/FAQ_russian
doc/KNOWN_BUGS
doc/MISSING_FEATURES
doc/Makefile
doc/README.mb.big5
doc/README.mb.jp
doc/TODO
doc/TODO.detail/README
doc/TODO.detail/atttypmod
doc/TODO.detail/crossdb
doc/TODO.detail/cursor
doc/TODO.detail/drop
doc/TODO.detail/exists
doc/TODO.detail/foreign
doc/TODO.detail/fsync
doc/TODO.detail/inheritance
doc/TODO.detail/java
doc/TODO.detail/mmap
doc/TODO.detail/namedatalen
doc/TODO.detail/optimizer
doc/TODO.detail/performance
doc/TODO.detail/persistent
doc/TODO.detail/pool
doc/TODO.detail/prepare
doc/TODO.detail/privileges
doc/TODO.detail/replication
doc/TODO.detail/schema
doc/TODO.detail/tablespaces
doc/TODO.detail/thread
doc/TODO.detail/transactions
doc/TODO.detail/typeconv
doc/TODO.detail/update
doc/TODO.detail/vacuum
doc/TODO.detail/view
doc/TODO.detail/win32
doc/TODO.detail/yacc
doc/bug.template
doc/src/FAQ/FAQ.html
doc/src/FAQ/FAQ_DEV.html
doc/src/FAQ/FAQ_german.html
doc/src/FAQ/FAQ_japanese.html
doc/src/FAQ/FAQ_polish.html
doc/src/FAQ/FAQ_russian.html
doc/src/Makefile
doc/src/graphics/catalogs.ag
doc/src/graphics/catalogs.cgm
doc/src/graphics/catalogs.gif
doc/src/graphics/catalogs.ps
doc/src/graphics/clientserver.ag
doc/src/graphics/clientserver.gif
doc/src/graphics/connections.ag
doc/src/graphics/connections.gif
doc/src/sgml/Makefile
doc/src/sgml/admin.sgml
doc/src/sgml/advanced.sgml
doc/src/sgml/arch-dev.sgml
doc/src/sgml/arch-pg.sgml
doc/src/sgml/array.sgml
doc/src/sgml/backup.sgml
doc/src/sgml/biblio.sgml
doc/src/sgml/bki.sgml
doc/src/sgml/book-decl.sgml
doc/src/sgml/catalogs.sgml
doc/src/sgml/charset.sgml
doc/src/sgml/client-auth.sgml
doc/src/sgml/compiler.sgml
doc/src/sgml/contacts.sgml
doc/src/sgml/cvs.sgml
doc/src/sgml/datatype.sgml
doc/src/sgml/datetime.sgml
doc/src/sgml/developer.sgml
doc/src/sgml/dfunc.sgml
doc/src/sgml/diskusage.sgml
doc/src/sgml/docguide.sgml
doc/src/sgml/ecpg.sgml
doc/src/sgml/extend.sgml
doc/src/sgml/features.sgml
doc/src/sgml/filelist.sgml
doc/src/sgml/fixrtf
doc/src/sgml/func-ref.sgml
doc/src/sgml/func.sgml
doc/src/sgml/geqo.sgml
doc/src/sgml/gist.sgml
doc/src/sgml/history.sgml
doc/src/sgml/indexcost.sgml
doc/src/sgml/indices.sgml
doc/src/sgml/info.sgml
doc/src/sgml/inherit.sgml
doc/src/sgml/install-win32.sgml
doc/src/sgml/installation.sgml
doc/src/sgml/intro.sgml
doc/src/sgml/jdbc.sgml
doc/src/sgml/keywords.sgml
doc/src/sgml/legal.sgml
doc/src/sgml/libpgeasy.sgml
doc/src/sgml/libpgtcl.sgml
doc/src/sgml/libpq++.sgml
doc/src/sgml/libpq.sgml
doc/src/sgml/lobj.sgml
doc/src/sgml/maintenance.sgml
doc/src/sgml/manage-ag.sgml
doc/src/sgml/manage.sgml
doc/src/sgml/monitoring.sgml
doc/src/sgml/mvcc.sgml
doc/src/sgml/nls.sgml
doc/src/sgml/notation.sgml
doc/src/sgml/odbc.sgml
doc/src/sgml/page.sgml
doc/src/sgml/perform.sgml
doc/src/sgml/plperl.sgml
doc/src/sgml/plpython.sgml
doc/src/sgml/plsql.sgml
doc/src/sgml/pltcl.sgml
doc/src/sgml/postgres.sgml
doc/src/sgml/problems.sgml
doc/src/sgml/programmer.sgml
doc/src/sgml/protocol.sgml
doc/src/sgml/pygresql.sgml
doc/src/sgml/queries.sgml
doc/src/sgml/query.sgml
doc/src/sgml/recovery.sgml
doc/src/sgml/ref/abort.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_database.sgml
doc/src/sgml/ref/alter_group.sgml
doc/src/sgml/ref/alter_table.sgml
doc/src/sgml/ref/alter_trigger.sgml
doc/src/sgml/ref/alter_user.sgml
doc/src/sgml/ref/analyze.sgml
doc/src/sgml/ref/begin.sgml
doc/src/sgml/ref/checkpoint.sgml
doc/src/sgml/ref/close.sgml
doc/src/sgml/ref/cluster.sgml
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/commit.sgml
doc/src/sgml/ref/copy.sgml
doc/src/sgml/ref/create_aggregate.sgml
doc/src/sgml/ref/create_constraint.sgml
doc/src/sgml/ref/create_database.sgml
doc/src/sgml/ref/create_domain.sgml
doc/src/sgml/ref/create_function.sgml
doc/src/sgml/ref/create_group.sgml
doc/src/sgml/ref/create_index.sgml
doc/src/sgml/ref/create_language.sgml
doc/src/sgml/ref/create_operator.sgml
doc/src/sgml/ref/create_rule.sgml
doc/src/sgml/ref/create_schema.sgml
doc/src/sgml/ref/create_sequence.sgml
doc/src/sgml/ref/create_table.sgml
doc/src/sgml/ref/create_table_as.sgml
doc/src/sgml/ref/create_trigger.sgml
doc/src/sgml/ref/create_type.sgml
doc/src/sgml/ref/create_user.sgml
doc/src/sgml/ref/create_view.sgml
doc/src/sgml/ref/createdb.sgml
doc/src/sgml/ref/createlang.sgml
doc/src/sgml/ref/createuser.sgml
doc/src/sgml/ref/current_date.sgml
doc/src/sgml/ref/current_time.sgml
doc/src/sgml/ref/current_timestamp.sgml
doc/src/sgml/ref/current_user.sgml
doc/src/sgml/ref/declare.sgml
doc/src/sgml/ref/delete.sgml
doc/src/sgml/ref/drop_aggregate.sgml
doc/src/sgml/ref/drop_database.sgml
doc/src/sgml/ref/drop_domain.sgml
doc/src/sgml/ref/drop_function.sgml
doc/src/sgml/ref/drop_group.sgml
doc/src/sgml/ref/drop_index.sgml
doc/src/sgml/ref/drop_language.sgml
doc/src/sgml/ref/drop_operator.sgml
doc/src/sgml/ref/drop_rule.sgml
doc/src/sgml/ref/drop_sequence.sgml
doc/src/sgml/ref/drop_table.sgml
doc/src/sgml/ref/drop_trigger.sgml
doc/src/sgml/ref/drop_type.sgml
doc/src/sgml/ref/drop_user.sgml
doc/src/sgml/ref/drop_view.sgml
doc/src/sgml/ref/dropdb.sgml
doc/src/sgml/ref/droplang.sgml
doc/src/sgml/ref/dropuser.sgml
doc/src/sgml/ref/ecpg-ref.sgml
doc/src/sgml/ref/end.sgml
doc/src/sgml/ref/explain.sgml
doc/src/sgml/ref/fetch.sgml
doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/initdb.sgml
doc/src/sgml/ref/initlocation.sgml
doc/src/sgml/ref/insert.sgml
doc/src/sgml/ref/ipcclean.sgml
doc/src/sgml/ref/listen.sgml
doc/src/sgml/ref/load.sgml
doc/src/sgml/ref/lock.sgml
doc/src/sgml/ref/move.sgml
doc/src/sgml/ref/notify.sgml
doc/src/sgml/ref/pg_config-ref.sgml
doc/src/sgml/ref/pg_ctl-ref.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
doc/src/sgml/ref/pg_restore.sgml
doc/src/sgml/ref/pgaccess-ref.sgml
doc/src/sgml/ref/pgtclsh.sgml
doc/src/sgml/ref/pgtksh.sgml
doc/src/sgml/ref/postgres-ref.sgml
doc/src/sgml/ref/postmaster.sgml
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/ref/reindex.sgml
doc/src/sgml/ref/reset.sgml
doc/src/sgml/ref/revoke.sgml
doc/src/sgml/ref/rollback.sgml
doc/src/sgml/ref/select.sgml
doc/src/sgml/ref/select_into.sgml
doc/src/sgml/ref/set.sgml
doc/src/sgml/ref/set_constraints.sgml
doc/src/sgml/ref/set_session_auth.sgml
doc/src/sgml/ref/set_transaction.sgml
doc/src/sgml/ref/show.sgml
doc/src/sgml/ref/truncate.sgml
doc/src/sgml/ref/unlisten.sgml
doc/src/sgml/ref/update.sgml
doc/src/sgml/ref/vacuum.sgml
doc/src/sgml/ref/vacuumdb.sgml
doc/src/sgml/refentry.sgml
doc/src/sgml/reference.ced
doc/src/sgml/reference.sgml
doc/src/sgml/regress.sgml
doc/src/sgml/release.sgml
doc/src/sgml/rules.sgml
doc/src/sgml/runtime.sgml
doc/src/sgml/sources.sgml
doc/src/sgml/spi.sgml
doc/src/sgml/sql.sgml
doc/src/sgml/standalone-install.sgml
doc/src/sgml/start.sgml
doc/src/sgml/stylesheet.css
doc/src/sgml/stylesheet.dsl
doc/src/sgml/syntax.sgml
doc/src/sgml/trigger.sgml
doc/src/sgml/tutorial.sgml
doc/src/sgml/typeconv.sgml
doc/src/sgml/user-manag.sgml
doc/src/sgml/user.sgml
doc/src/sgml/version.sgml
doc/src/sgml/wal.sgml
doc/src/sgml/xaggr.sgml
doc/src/sgml/xfunc.sgml
doc/src/sgml/xindex.sgml
doc/src/sgml/xoper.sgml
doc/src/sgml/xplang.sgml
doc/src/sgml/xtypes.sgml
doc/src/sgml/y2k.sgml
register.txt
src/DEVELOPERS
src/Makefile
src/Makefile.global.in
src/Makefile.shlib
src/backend/Makefile
src/backend/access/Makefile
src/backend/access/common/Makefile
src/backend/access/common/heaptuple.c
src/backend/access/common/indextuple.c
src/backend/access/common/indexvalid.c
src/backend/access/common/printtup.c
src/backend/access/common/scankey.c
src/backend/access/common/tupdesc.c
src/backend/access/gist/Makefile
src/backend/access/gist/gist.c
src/backend/access/gist/gistget.c
src/backend/access/gist/gistscan.c
src/backend/access/gist/giststrat.c
src/backend/access/hash/Makefile
src/backend/access/hash/hash.c
src/backend/access/hash/hashfunc.c
src/backend/access/hash/hashinsert.c
src/backend/access/hash/hashovfl.c
src/backend/access/hash/hashpage.c
src/backend/access/hash/hashscan.c
src/backend/access/hash/hashsearch.c
src/backend/access/hash/hashstrat.c
src/backend/access/hash/hashutil.c
src/backend/access/heap/Makefile
src/backend/access/heap/heapam.c
src/backend/access/heap/hio.c
src/backend/access/heap/tuptoaster.c
src/backend/access/index/Makefile
src/backend/access/index/genam.c
src/backend/access/index/indexam.c
src/backend/access/index/istrat.c
src/backend/access/nbtree/Makefile
src/backend/access/nbtree/README
src/backend/access/nbtree/nbtcompare.c
src/backend/access/nbtree/nbtinsert.c
src/backend/access/nbtree/nbtpage.c
src/backend/access/nbtree/nbtree.c
src/backend/access/nbtree/nbtsearch.c
src/backend/access/nbtree/nbtsort.c
src/backend/access/nbtree/nbtstrat.c
src/backend/access/nbtree/nbtutils.c
src/backend/access/rtree/Makefile
src/backend/access/rtree/rtget.c
src/backend/access/rtree/rtproc.c
src/backend/access/rtree/rtree.c
src/backend/access/rtree/rtscan.c
src/backend/access/rtree/rtstrat.c
src/backend/access/transam/Makefile
src/backend/access/transam/clog.c
src/backend/access/transam/rmgr.c
src/backend/access/transam/transam.c
src/backend/access/transam/varsup.c
src/backend/access/transam/xact.c
src/backend/access/transam/xid.c
src/backend/access/transam/xlog.c
src/backend/access/transam/xlogutils.c
src/backend/bootstrap/.cvsignore
src/backend/bootstrap/Makefile
src/backend/bootstrap/bootparse.y
src/backend/bootstrap/bootscanner.l
src/backend/bootstrap/bootstrap.c
src/backend/catalog/Makefile
src/backend/catalog/README
src/backend/catalog/aclchk.c
src/backend/catalog/catalog.c
src/backend/catalog/genbki.sh
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/indexing.c
src/backend/catalog/namespace.c
src/backend/catalog/pg_aggregate.c
src/backend/catalog/pg_largeobject.c
src/backend/catalog/pg_namespace.c
src/backend/catalog/pg_operator.c
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_type.c
src/backend/commands/Makefile
src/backend/commands/_deadcode/recipe.c
src/backend/commands/_deadcode/recipe.h
src/backend/commands/_deadcode/version.c
src/backend/commands/aggregatecmds.c
src/backend/commands/analyze.c
src/backend/commands/async.c
src/backend/commands/cluster.c
src/backend/commands/comment.c
src/backend/commands/copy.c
src/backend/commands/dbcommands.c
src/backend/commands/define.c
src/backend/commands/explain.c
src/backend/commands/functioncmds.c
src/backend/commands/indexcmds.c
src/backend/commands/lockcmds.c
src/backend/commands/operatorcmds.c
src/backend/commands/portalcmds.c
src/backend/commands/proclang.c
src/backend/commands/schemacmds.c
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/commands/typecmds.c
src/backend/commands/user.c
src/backend/commands/vacuum.c
src/backend/commands/vacuumlazy.c
src/backend/commands/variable.c
src/backend/commands/view.c
src/backend/executor/Makefile
src/backend/executor/README
src/backend/executor/_deadcode/nodeTee.c
src/backend/executor/execAmi.c
src/backend/executor/execJunk.c
src/backend/executor/execMain.c
src/backend/executor/execProcnode.c
src/backend/executor/execQual.c
src/backend/executor/execScan.c
src/backend/executor/execTuples.c
src/backend/executor/execUtils.c
src/backend/executor/functions.c
src/backend/executor/instrument.c
src/backend/executor/nodeAgg.c
src/backend/executor/nodeAppend.c
src/backend/executor/nodeFunctionscan.c
src/backend/executor/nodeGroup.c
src/backend/executor/nodeHash.c
src/backend/executor/nodeHashjoin.c
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeLimit.c
src/backend/executor/nodeMaterial.c
src/backend/executor/nodeMergejoin.c
src/backend/executor/nodeNestloop.c
src/backend/executor/nodeResult.c
src/backend/executor/nodeSeqscan.c
src/backend/executor/nodeSetOp.c
src/backend/executor/nodeSort.c
src/backend/executor/nodeSubplan.c
src/backend/executor/nodeSubqueryscan.c
src/backend/executor/nodeTidscan.c
src/backend/executor/nodeUnique.c
src/backend/executor/spi.c
src/backend/lib/Makefile
src/backend/lib/bit.c
src/backend/lib/dllist.c
src/backend/lib/lispsort.c
src/backend/lib/stringinfo.c
src/backend/libpq/Makefile
src/backend/libpq/README.SSL
src/backend/libpq/auth.c
src/backend/libpq/be-fsstubs.c
src/backend/libpq/be-secure.c
src/backend/libpq/crypt.c
src/backend/libpq/hba.c
src/backend/libpq/md5.c
src/backend/libpq/pg_hba.conf.sample
src/backend/libpq/pg_ident.conf.sample
src/backend/libpq/pqcomm.c
src/backend/libpq/pqformat.c
src/backend/libpq/pqsignal.c
src/backend/main/Makefile
src/backend/main/main.c
src/backend/nodes/Makefile
src/backend/nodes/README
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/list.c
src/backend/nodes/makefuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/nodes.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/nodes/read.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/Makefile
src/backend/optimizer/README
src/backend/optimizer/geqo/Makefile
src/backend/optimizer/geqo/geqo_copy.c
src/backend/optimizer/geqo/geqo_cx.c
src/backend/optimizer/geqo/geqo_erx.c
src/backend/optimizer/geqo/geqo_eval.c
src/backend/optimizer/geqo/geqo_main.c
src/backend/optimizer/geqo/geqo_misc.c
src/backend/optimizer/geqo/geqo_mutation.c
src/backend/optimizer/geqo/geqo_ox1.c
src/backend/optimizer/geqo/geqo_ox2.c
src/backend/optimizer/geqo/geqo_pmx.c
src/backend/optimizer/geqo/geqo_pool.c
src/backend/optimizer/geqo/geqo_px.c
src/backend/optimizer/geqo/geqo_recombination.c
src/backend/optimizer/geqo/geqo_selection.c
src/backend/optimizer/path/Makefile
src/backend/optimizer/path/_deadcode/predmig.c
src/backend/optimizer/path/_deadcode/xfunc.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/path/joinrels.c
src/backend/optimizer/path/orindxpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/path/tidpath.c
src/backend/optimizer/plan/Makefile
src/backend/optimizer/plan/README
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/plan/planmain.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/prep/Makefile
src/backend/optimizer/prep/_deadcode/prepkeyset.c
src/backend/optimizer/prep/prepqual.c
src/backend/optimizer/prep/preptlist.c
src/backend/optimizer/prep/prepunion.c
src/backend/optimizer/util/Makefile
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/joininfo.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/restrictinfo.c
src/backend/optimizer/util/tlist.c
src/backend/optimizer/util/var.c
src/backend/parser/.cvsignore
src/backend/parser/Makefile
src/backend/parser/README
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/parse_agg.c
src/backend/parser/parse_clause.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/parser/parse_node.c
src/backend/parser/parse_oper.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/parser/parse_type.c
src/backend/parser/parser.c
src/backend/parser/scan.l
src/backend/parser/scansup.c
src/backend/po/Makefile
src/backend/po/cs.po
src/backend/po/de.po
src/backend/po/hu.po
src/backend/po/nls.mk
src/backend/po/ru.po
src/backend/po/zh_CN.po
src/backend/po/zh_TW.po
src/backend/port/Makefile
src/backend/port/aix/mkldexport.sh
src/backend/port/beos/Makefile
src/backend/port/beos/sem.c
src/backend/port/beos/shm.c
src/backend/port/beos/support.c
src/backend/port/darwin/Makefile
src/backend/port/darwin/README
src/backend/port/darwin/system.c
src/backend/port/dynloader/README.dlfcn.aix
src/backend/port/dynloader/aix.c
src/backend/port/dynloader/aix.h
src/backend/port/dynloader/beos.c
src/backend/port/dynloader/beos.h
src/backend/port/dynloader/bsdi.c
src/backend/port/dynloader/bsdi.h
src/backend/port/dynloader/darwin.c
src/backend/port/dynloader/darwin.h
src/backend/port/dynloader/dgux.c
src/backend/port/dynloader/dgux.h
src/backend/port/dynloader/freebsd.c
src/backend/port/dynloader/freebsd.h
src/backend/port/dynloader/hpux.c
src/backend/port/dynloader/hpux.h
src/backend/port/dynloader/irix5.c
src/backend/port/dynloader/irix5.h
src/backend/port/dynloader/linux.c
src/backend/port/dynloader/linux.h
src/backend/port/dynloader/netbsd.c
src/backend/port/dynloader/netbsd.h
src/backend/port/dynloader/nextstep.c
src/backend/port/dynloader/nextstep.h
src/backend/port/dynloader/openbsd.c
src/backend/port/dynloader/openbsd.h
src/backend/port/dynloader/osf.c
src/backend/port/dynloader/osf.h
src/backend/port/dynloader/qnx4.c
src/backend/port/dynloader/qnx4.h
src/backend/port/dynloader/sco.c
src/backend/port/dynloader/sco.h
src/backend/port/dynloader/solaris.c
src/backend/port/dynloader/solaris.h
src/backend/port/dynloader/sunos4.c
src/backend/port/dynloader/sunos4.h
src/backend/port/dynloader/svr4.c
src/backend/port/dynloader/svr4.h
src/backend/port/dynloader/ultrix4.c
src/backend/port/dynloader/ultrix4.h
src/backend/port/dynloader/univel.c
src/backend/port/dynloader/univel.h
src/backend/port/dynloader/unixware.c
src/backend/port/dynloader/unixware.h
src/backend/port/dynloader/win.c
src/backend/port/dynloader/win.h
src/backend/port/gethostname.c
src/backend/port/getrusage.c
src/backend/port/hpux/tas.c.template
src/backend/port/inet_aton.c
src/backend/port/inet_aton.h
src/backend/port/ipc_test.c
src/backend/port/isinf.c
src/backend/port/memcmp.c
src/backend/port/nextstep/Makefile
src/backend/port/nextstep/port.c
src/backend/port/posix_sema.c
src/backend/port/qnx4/Makefile
src/backend/port/qnx4/ipc.h
src/backend/port/qnx4/isnan.c
src/backend/port/qnx4/rint.c
src/backend/port/qnx4/sem.c
src/backend/port/qnx4/sem.h
src/backend/port/qnx4/shm.c
src/backend/port/qnx4/shm.h
src/backend/port/qnx4/tstrint.c
src/backend/port/qnx4/tstsem.c
src/backend/port/qnx4/tstshm.c
src/backend/port/random.c
src/backend/port/snprintf.c
src/backend/port/srandom.c
src/backend/port/strcasecmp.c
src/backend/port/strerror.c
src/backend/port/strtol.c
src/backend/port/strtoul.c
src/backend/port/sunos4/Makefile
src/backend/port/sunos4/float.h
src/backend/port/sysv_sema.c
src/backend/port/sysv_shmem.c
src/backend/port/tas/dummy.s
src/backend/port/tas/hpux.s
src/backend/port/tas/solaris_i386.s
src/backend/port/tas/solaris_sparc.s
src/backend/postmaster/Makefile
src/backend/postmaster/pgstat.c
src/backend/postmaster/postmaster.c
src/backend/regex/COPYRIGHT
src/backend/regex/Makefile
src/backend/regex/WHATSNEW
src/backend/regex/engine.c
src/backend/regex/re_format.7
src/backend/regex/regcomp.c
src/backend/regex/regerror.c
src/backend/regex/regex.3
src/backend/regex/regexec.c
src/backend/regex/regfree.c
src/backend/regex/retest.c
src/backend/rewrite/Makefile
src/backend/rewrite/rewriteDefine.c
src/backend/rewrite/rewriteHandler.c
src/backend/rewrite/rewriteManip.c
src/backend/rewrite/rewriteRemove.c
src/backend/rewrite/rewriteSupport.c
src/backend/storage/Makefile
src/backend/storage/buffer/Makefile
src/backend/storage/buffer/README
src/backend/storage/buffer/buf_init.c
src/backend/storage/buffer/buf_table.c
src/backend/storage/buffer/bufmgr.c
src/backend/storage/buffer/freelist.c
src/backend/storage/buffer/localbuf.c
src/backend/storage/file/Makefile
src/backend/storage/file/buffile.c
src/backend/storage/file/fd.c
src/backend/storage/freespace/Makefile
src/backend/storage/freespace/freespace.c
src/backend/storage/ipc/Makefile
src/backend/storage/ipc/README
src/backend/storage/ipc/ipc.c
src/backend/storage/ipc/ipci.c
src/backend/storage/ipc/pmsignal.c
src/backend/storage/ipc/shmem.c
src/backend/storage/ipc/shmqueue.c
src/backend/storage/ipc/sinval.c
src/backend/storage/ipc/sinvaladt.c
src/backend/storage/large_object/Makefile
src/backend/storage/large_object/inv_api.c
src/backend/storage/lmgr/Makefile
src/backend/storage/lmgr/README
src/backend/storage/lmgr/deadlock.c
src/backend/storage/lmgr/lmgr.c
src/backend/storage/lmgr/lock.c
src/backend/storage/lmgr/lwlock.c
src/backend/storage/lmgr/proc.c
src/backend/storage/lmgr/s_lock.c
src/backend/storage/lmgr/spin.c
src/backend/storage/page/Makefile
src/backend/storage/page/bufpage.c
src/backend/storage/page/itemptr.c
src/backend/storage/smgr/Makefile
src/backend/storage/smgr/README
src/backend/storage/smgr/md.c
src/backend/storage/smgr/mm.c
src/backend/storage/smgr/smgr.c
src/backend/storage/smgr/smgrtype.c
src/backend/tcop/Makefile
src/backend/tcop/dest.c
src/backend/tcop/fastpath.c
src/backend/tcop/postgres.c
src/backend/tcop/pquery.c
src/backend/tcop/utility.c
src/backend/tioga/Arr_TgRecipe.h
src/backend/tioga/Makefile
src/backend/tioga/Varray.c
src/backend/tioga/Varray.h
src/backend/tioga/tgRecipe.c
src/backend/tioga/tgRecipe.h
src/backend/utils/.cvsignore
src/backend/utils/Gen_fmgrtab.sh
src/backend/utils/Makefile
src/backend/utils/adt/Makefile
src/backend/utils/adt/acl.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/arrayutils.c
src/backend/utils/adt/ascii.c
src/backend/utils/adt/bool.c
src/backend/utils/adt/cash.c
src/backend/utils/adt/char.c
src/backend/utils/adt/date.c
src/backend/utils/adt/datetime.c
src/backend/utils/adt/datum.c
src/backend/utils/adt/encode.c
src/backend/utils/adt/float.c
src/backend/utils/adt/format_type.c
src/backend/utils/adt/formatting.c
src/backend/utils/adt/geo_ops.c
src/backend/utils/adt/geo_selfuncs.c
src/backend/utils/adt/inet_net_ntop.c
src/backend/utils/adt/inet_net_pton.c
src/backend/utils/adt/int.c
src/backend/utils/adt/int8.c
src/backend/utils/adt/like.c
src/backend/utils/adt/like_match.c
src/backend/utils/adt/mac.c
src/backend/utils/adt/misc.c
src/backend/utils/adt/nabstime.c
src/backend/utils/adt/name.c
src/backend/utils/adt/network.c
src/backend/utils/adt/not_in.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/numutils.c
src/backend/utils/adt/oid.c
src/backend/utils/adt/oracle_compat.c
src/backend/utils/adt/pg_locale.c
src/backend/utils/adt/pg_lzcompress.c
src/backend/utils/adt/pgstatfuncs.c
src/backend/utils/adt/quote.c
src/backend/utils/adt/regexp.c
src/backend/utils/adt/regproc.c
src/backend/utils/adt/ri_triggers.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/sets.c
src/backend/utils/adt/tid.c
src/backend/utils/adt/timestamp.c
src/backend/utils/adt/varbit.c
src/backend/utils/adt/varchar.c
src/backend/utils/adt/varlena.c
src/backend/utils/adt/version.c
src/backend/utils/cache/Makefile
src/backend/utils/cache/catcache.c
src/backend/utils/cache/fcache.c
src/backend/utils/cache/inval.c
src/backend/utils/cache/lsyscache.c
src/backend/utils/cache/relcache.c
src/backend/utils/cache/syscache.c
src/backend/utils/error/Makefile
src/backend/utils/error/assert.c
src/backend/utils/error/elog.c
src/backend/utils/error/exc.c
src/backend/utils/error/excabort.c
src/backend/utils/error/excid.c
src/backend/utils/error/format.c
src/backend/utils/fmgr/Makefile
src/backend/utils/fmgr/README
src/backend/utils/fmgr/dfmgr.c
src/backend/utils/fmgr/fmgr.c
src/backend/utils/hash/Makefile
src/backend/utils/hash/dynahash.c
src/backend/utils/hash/hashfn.c
src/backend/utils/hash/pg_crc.c
src/backend/utils/init/Makefile
src/backend/utils/init/findbe.c
src/backend/utils/init/globals.c
src/backend/utils/init/miscinit.c
src/backend/utils/init/postinit.c
src/backend/utils/mb/Makefile
src/backend/utils/mb/README
src/backend/utils/mb/Unicode/ISO10646-GB18030.TXT
src/backend/utils/mb/Unicode/Makefile
src/backend/utils/mb/Unicode/UCS_to_8859.pl
src/backend/utils/mb/Unicode/UCS_to_BIG5.pl
src/backend/utils/mb/Unicode/UCS_to_EUC_CN.pl
src/backend/utils/mb/Unicode/UCS_to_EUC_JP.pl
src/backend/utils/mb/Unicode/UCS_to_EUC_KR.pl
src/backend/utils/mb/Unicode/UCS_to_EUC_TW.pl
src/backend/utils/mb/Unicode/UCS_to_GB18030.pl
src/backend/utils/mb/Unicode/UCS_to_GBK.pl
src/backend/utils/mb/Unicode/UCS_to_JOHAB.pl
src/backend/utils/mb/Unicode/UCS_to_SJIS.pl
src/backend/utils/mb/Unicode/UCS_to_UHC.pl
src/backend/utils/mb/Unicode/UCS_to_WIN874.pl
src/backend/utils/mb/Unicode/UCS_to_WINX.pl
src/backend/utils/mb/Unicode/UCS_to_cyrillic.pl
src/backend/utils/mb/Unicode/alt_to_utf8.map
src/backend/utils/mb/Unicode/big5_to_utf8.map
src/backend/utils/mb/Unicode/euc_cn_to_utf8.map
src/backend/utils/mb/Unicode/euc_jp_to_utf8.map
src/backend/utils/mb/Unicode/euc_kr_to_utf8.map
src/backend/utils/mb/Unicode/euc_tw_to_utf8.map
src/backend/utils/mb/Unicode/gb18030_to_utf8.map
src/backend/utils/mb/Unicode/gbk_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_10_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_13_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_14_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_15_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_16_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_2_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_3_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_4_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_5_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_6_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_7_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_8_to_utf8.map
src/backend/utils/mb/Unicode/iso8859_9_to_utf8.map
src/backend/utils/mb/Unicode/johab_to_utf8.map
src/backend/utils/mb/Unicode/koi8r_to_utf8.map
src/backend/utils/mb/Unicode/sjis_to_utf8.map
src/backend/utils/mb/Unicode/tcvn_to_utf8.map
src/backend/utils/mb/Unicode/ucs2utf.pl
src/backend/utils/mb/Unicode/uhc_to_utf8.map
src/backend/utils/mb/Unicode/utf8_to_alt.map
src/backend/utils/mb/Unicode/utf8_to_big5.map
src/backend/utils/mb/Unicode/utf8_to_euc_cn.map
src/backend/utils/mb/Unicode/utf8_to_euc_jp.map
src/backend/utils/mb/Unicode/utf8_to_euc_kr.map
src/backend/utils/mb/Unicode/utf8_to_euc_tw.map
src/backend/utils/mb/Unicode/utf8_to_gb18030.map
src/backend/utils/mb/Unicode/utf8_to_gbk.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_10.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_13.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_14.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_15.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_16.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_2.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_3.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_4.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_5.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_6.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_7.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_8.map
src/backend/utils/mb/Unicode/utf8_to_iso8859_9.map
src/backend/utils/mb/Unicode/utf8_to_johab.map
src/backend/utils/mb/Unicode/utf8_to_koi8r.map
src/backend/utils/mb/Unicode/utf8_to_sjis.map
src/backend/utils/mb/Unicode/utf8_to_tcvn.map
src/backend/utils/mb/Unicode/utf8_to_uhc.map
src/backend/utils/mb/Unicode/utf8_to_win1250.map
src/backend/utils/mb/Unicode/utf8_to_win1251.map
src/backend/utils/mb/Unicode/utf8_to_win1256.map
src/backend/utils/mb/Unicode/utf8_to_win874.map
src/backend/utils/mb/Unicode/win1250_to_utf8.map
src/backend/utils/mb/Unicode/win1251_to_utf8.map
src/backend/utils/mb/Unicode/win1256_to_utf8.map
src/backend/utils/mb/Unicode/win874_to_utf8.map
src/backend/utils/mb/alt.c
src/backend/utils/mb/big5.c
src/backend/utils/mb/conv.c
src/backend/utils/mb/encnames.c
src/backend/utils/mb/iso.c
src/backend/utils/mb/mbutils.c
src/backend/utils/mb/sjis.map
src/backend/utils/mb/wchar.c
src/backend/utils/mb/win.c
src/backend/utils/mb/win1251.c
src/backend/utils/mb/wstrcmp.c
src/backend/utils/mb/wstrncmp.c
src/backend/utils/misc/.cvsignore
src/backend/utils/misc/Makefile
src/backend/utils/misc/README
src/backend/utils/misc/database.c
src/backend/utils/misc/guc-file.l
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/backend/utils/misc/ps_status.c
src/backend/utils/misc/superuser.c
src/backend/utils/mmgr/Makefile
src/backend/utils/mmgr/README
src/backend/utils/mmgr/aset.c
src/backend/utils/mmgr/mcxt.c
src/backend/utils/mmgr/portalmem.c
src/backend/utils/sort/Makefile
src/backend/utils/sort/logtape.c
src/backend/utils/sort/tuplesort.c
src/backend/utils/sort/tuplestore.c
src/backend/utils/time/Makefile
src/backend/utils/time/tqual.c
src/bin/Makefile
src/bin/initdb/Makefile
src/bin/initdb/initdb.sh
src/bin/initlocation/Makefile
src/bin/initlocation/initlocation.sh
src/bin/ipcclean/Makefile
src/bin/ipcclean/ipcclean.sh
src/bin/pg_config/Makefile
src/bin/pg_config/pg_config.sh
src/bin/pg_ctl/Makefile
src/bin/pg_ctl/pg_ctl.sh
src/bin/pg_dump/Makefile
src/bin/pg_dump/README
src/bin/pg_dump/common.c
src/bin/pg_dump/cs.po
src/bin/pg_dump/de.po
src/bin/pg_dump/nls.mk
src/bin/pg_dump/pg_backup.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_backup_archiver.h
src/bin/pg_dump/pg_backup_custom.c
src/bin/pg_dump/pg_backup_db.c
src/bin/pg_dump/pg_backup_db.h
src/bin/pg_dump/pg_backup_files.c
src/bin/pg_dump/pg_backup_null.c
src/bin/pg_dump/pg_backup_tar.c
src/bin/pg_dump/pg_backup_tar.h
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dumpall.sh
src/bin/pg_dump/pg_restore.c
src/bin/pg_dump/ru.po
src/bin/pg_dump/sv.po
src/bin/pg_dump/zh_CN.po
src/bin/pg_dump/zh_TW.po
src/bin/pg_encoding/Makefile
src/bin/pg_encoding/pg_encoding.c
src/bin/pg_id/Makefile
src/bin/pg_id/pg_id.c
src/bin/pgaccess/Makefile
src/bin/pgaccess/README
src/bin/pgaccess/copyright.html
src/bin/pgaccess/demo/formdemo.sql
src/bin/pgaccess/doc/html/a_right.gif
src/bin/pgaccess/doc/html/addindex.gif
src/bin/pgaccess/doc/html/api.html
src/bin/pgaccess/doc/html/ball.gif
src/bin/pgaccess/doc/html/contents.html
src/bin/pgaccess/doc/html/copyright.html
src/bin/pgaccess/doc/html/documentation.html
src/bin/pgaccess/doc/html/download.html
src/bin/pgaccess/doc/html/faq.html
src/bin/pgaccess/doc/html/features.html
src/bin/pgaccess/doc/html/formdemo.sql
src/bin/pgaccess/doc/html/forms.gif
src/bin/pgaccess/doc/html/forms.html
src/bin/pgaccess/doc/html/function.gif
src/bin/pgaccess/doc/html/help.gif
src/bin/pgaccess/doc/html/index.html
src/bin/pgaccess/doc/html/irix.html
src/bin/pgaccess/doc/html/linux1.gif
src/bin/pgaccess/doc/html/maillist.html
src/bin/pgaccess/doc/html/main.html
src/bin/pgaccess/doc/html/mainwindow.gif
src/bin/pgaccess/doc/html/newtable.gif
src/bin/pgaccess/doc/html/newuser.gif
src/bin/pgaccess/doc/html/old_index.html
src/bin/pgaccess/doc/html/permissions.gif
src/bin/pgaccess/doc/html/pg93patch.html
src/bin/pgaccess/doc/html/pga-rad.html
src/bin/pgaccess/doc/html/qbtclet.html
src/bin/pgaccess/doc/html/qbtclet.tcl
src/bin/pgaccess/doc/html/screenshots.html
src/bin/pgaccess/doc/html/specialchars.html
src/bin/pgaccess/doc/html/todo.html
src/bin/pgaccess/doc/html/tutorial/addref.jpg
src/bin/pgaccess/doc/html/tutorial/altern_q.jpg
src/bin/pgaccess/doc/html/tutorial/altern_v.jpg
src/bin/pgaccess/doc/html/tutorial/copyright.html
src/bin/pgaccess/doc/html/tutorial/index.html
src/bin/pgaccess/doc/html/tutorial/intro.html
src/bin/pgaccess/doc/html/tutorial/irix.html
src/bin/pgaccess/doc/html/tutorial/newref.txt
src/bin/pgaccess/doc/html/tutorial/newtable.jpg
src/bin/pgaccess/doc/html/tutorial/newtable.tga
src/bin/pgaccess/doc/html/tutorial/problems.html
src/bin/pgaccess/doc/html/tutorial/screen1.jpg
src/bin/pgaccess/doc/html/tutorial/sel_tbl.jpg
src/bin/pgaccess/doc/html/tutorial/start.html
src/bin/pgaccess/doc/html/tutorial/tut.html
src/bin/pgaccess/doc/html/tutorial/tut_edit.html
src/bin/pgaccess/doc/html/tutorial/tut_new.html
src/bin/pgaccess/doc/html/tutorial/tut_sel1.html
src/bin/pgaccess/doc/html/tutorial/tut_user.html
src/bin/pgaccess/doc/html/vdesigner.gif
src/bin/pgaccess/doc/html/whatsnew.html
src/bin/pgaccess/doc/html/win32.html
src/bin/pgaccess/images/icon_button.gif
src/bin/pgaccess/images/icon_checkbutton.gif
src/bin/pgaccess/images/icon_entry.gif
src/bin/pgaccess/images/icon_frame.gif
src/bin/pgaccess/images/icon_label.gif
src/bin/pgaccess/images/icon_listbox.gif
src/bin/pgaccess/images/icon_query.gif
src/bin/pgaccess/images/icon_radiobutton.gif
src/bin/pgaccess/images/icon_text.gif
src/bin/pgaccess/lib/database.tcl
src/bin/pgaccess/lib/forms.tcl
src/bin/pgaccess/lib/functions.tcl
src/bin/pgaccess/lib/help/abort.hlp
src/bin/pgaccess/lib/help/add_records.hlp
src/bin/pgaccess/lib/help/alter_table.hlp
src/bin/pgaccess/lib/help/alter_user.hlp
src/bin/pgaccess/lib/help/author.hlp
src/bin/pgaccess/lib/help/begin.hlp
src/bin/pgaccess/lib/help/close.hlp
src/bin/pgaccess/lib/help/cluster.hlp
src/bin/pgaccess/lib/help/commit.hlp
src/bin/pgaccess/lib/help/copy.hlp
src/bin/pgaccess/lib/help/copyrights.hlp
src/bin/pgaccess/lib/help/create_aggregate.hlp
src/bin/pgaccess/lib/help/create_database.hlp
src/bin/pgaccess/lib/help/create_function.hlp
src/bin/pgaccess/lib/help/create_index.hlp
src/bin/pgaccess/lib/help/create_language.hlp
src/bin/pgaccess/lib/help/create_operator.hlp
src/bin/pgaccess/lib/help/create_rule.hlp
src/bin/pgaccess/lib/help/create_sequence.hlp
src/bin/pgaccess/lib/help/create_table.hlp
src/bin/pgaccess/lib/help/create_table_as.hlp
src/bin/pgaccess/lib/help/create_trigger.hlp
src/bin/pgaccess/lib/help/create_type.hlp
src/bin/pgaccess/lib/help/create_user.hlp
src/bin/pgaccess/lib/help/create_view.hlp
src/bin/pgaccess/lib/help/data_types.hlp
src/bin/pgaccess/lib/help/datefunc.hlp
src/bin/pgaccess/lib/help/declare.hlp
src/bin/pgaccess/lib/help/delete.hlp
src/bin/pgaccess/lib/help/drop_aggregate.hlp
src/bin/pgaccess/lib/help/drop_database.hlp
src/bin/pgaccess/lib/help/drop_function.hlp
src/bin/pgaccess/lib/help/drop_index.hlp
src/bin/pgaccess/lib/help/drop_language.hlp
src/bin/pgaccess/lib/help/drop_operator.hlp
src/bin/pgaccess/lib/help/drop_rule.hlp
src/bin/pgaccess/lib/help/drop_sequence.hlp
src/bin/pgaccess/lib/help/drop_table.hlp
src/bin/pgaccess/lib/help/drop_trigger.hlp
src/bin/pgaccess/lib/help/drop_type.hlp
src/bin/pgaccess/lib/help/drop_user.hlp
src/bin/pgaccess/lib/help/drop_view.hlp
src/bin/pgaccess/lib/help/explain.hlp
src/bin/pgaccess/lib/help/fetch.hlp
src/bin/pgaccess/lib/help/form_design.hlp
src/bin/pgaccess/lib/help/forms.hlp
src/bin/pgaccess/lib/help/functions.hlp
src/bin/pgaccess/lib/help/geomfunc.hlp
src/bin/pgaccess/lib/help/grant.hlp
src/bin/pgaccess/lib/help/history.hlp
src/bin/pgaccess/lib/help/index.hlp
src/bin/pgaccess/lib/help/inheritance.hlp
src/bin/pgaccess/lib/help/insert.hlp
src/bin/pgaccess/lib/help/ipv4func.hlp
src/bin/pgaccess/lib/help/isolation.hlp
src/bin/pgaccess/lib/help/keywords.hlp
src/bin/pgaccess/lib/help/listen.hlp
src/bin/pgaccess/lib/help/load.hlp
src/bin/pgaccess/lib/help/lock.hlp
src/bin/pgaccess/lib/help/mathfunc.hlp
src/bin/pgaccess/lib/help/move.hlp
src/bin/pgaccess/lib/help/mvcc.hlp
src/bin/pgaccess/lib/help/new_query.hlp
src/bin/pgaccess/lib/help/new_table.hlp
src/bin/pgaccess/lib/help/notify.hlp
src/bin/pgaccess/lib/help/open_query.hlp
src/bin/pgaccess/lib/help/open_table.hlp
src/bin/pgaccess/lib/help/pgfunctions.hlp
src/bin/pgaccess/lib/help/postgresql.hlp
src/bin/pgaccess/lib/help/queries.hlp
src/bin/pgaccess/lib/help/reports.hlp
src/bin/pgaccess/lib/help/reset.hlp
src/bin/pgaccess/lib/help/revoke.hlp
src/bin/pgaccess/lib/help/rollback.hlp
src/bin/pgaccess/lib/help/schema.hlp
src/bin/pgaccess/lib/help/scripts.hlp
src/bin/pgaccess/lib/help/select.hlp
src/bin/pgaccess/lib/help/select_into.hlp
src/bin/pgaccess/lib/help/sequences.hlp
src/bin/pgaccess/lib/help/set.hlp
src/bin/pgaccess/lib/help/show.hlp
src/bin/pgaccess/lib/help/sql_guide.hlp
src/bin/pgaccess/lib/help/sqlfunc.hlp
src/bin/pgaccess/lib/help/stringfunc.hlp
src/bin/pgaccess/lib/help/tables.hlp
src/bin/pgaccess/lib/help/unlisten.hlp
src/bin/pgaccess/lib/help/update.hlp
src/bin/pgaccess/lib/help/users.hlp
src/bin/pgaccess/lib/help/vacuum.hlp
src/bin/pgaccess/lib/help/view_table_structure.hlp
src/bin/pgaccess/lib/help/views.hlp
src/bin/pgaccess/lib/help/visual_designer.hlp
src/bin/pgaccess/lib/help/y2k.hlp
src/bin/pgaccess/lib/help.tcl
src/bin/pgaccess/lib/languages/chinese_big5
src/bin/pgaccess/lib/languages/chinese_gb
src/bin/pgaccess/lib/languages/czech
src/bin/pgaccess/lib/languages/deutsch
src/bin/pgaccess/lib/languages/euskara
src/bin/pgaccess/lib/languages/francais
src/bin/pgaccess/lib/languages/italiano
src/bin/pgaccess/lib/languages/japanese
src/bin/pgaccess/lib/languages/magyar
src/bin/pgaccess/lib/languages/nederlands
src/bin/pgaccess/lib/languages/portugues
src/bin/pgaccess/lib/languages/romana
src/bin/pgaccess/lib/languages/russian.koi8r
src/bin/pgaccess/lib/languages/russian_win
src/bin/pgaccess/lib/languages/spanish
src/bin/pgaccess/lib/mainlib.tcl
src/bin/pgaccess/lib/preferences.tcl
src/bin/pgaccess/lib/queries.tcl
src/bin/pgaccess/lib/reports.tcl
src/bin/pgaccess/lib/schema.tcl
src/bin/pgaccess/lib/scripts.tcl
src/bin/pgaccess/lib/sequences.tcl
src/bin/pgaccess/lib/tables.tcl
src/bin/pgaccess/lib/users.tcl
src/bin/pgaccess/lib/views.tcl
src/bin/pgaccess/lib/visualqb.tcl
src/bin/pgaccess/main.tcl
src/bin/pgaccess/pgaccess.sh
src/bin/pgtclsh/Makefile
src/bin/pgtclsh/README
src/bin/pgtclsh/pgtclAppInit.c
src/bin/pgtclsh/pgtclUtils.tcl
src/bin/pgtclsh/pgtkAppInit.c
src/bin/pgtclsh/updateStats.tcl
src/bin/psql/.cvsignore
src/bin/psql/Makefile
src/bin/psql/command.c
src/bin/psql/command.h
src/bin/psql/common.c
src/bin/psql/common.h
src/bin/psql/copy.c
src/bin/psql/copy.h
src/bin/psql/create_help.pl
src/bin/psql/cs.po
src/bin/psql/de.po
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/fr.po
src/bin/psql/help.c
src/bin/psql/help.h
src/bin/psql/input.c
src/bin/psql/input.h
src/bin/psql/large_obj.c
src/bin/psql/large_obj.h
src/bin/psql/mainloop.c
src/bin/psql/mainloop.h
src/bin/psql/mbprint.c
src/bin/psql/mbprint.h
src/bin/psql/nls.mk
src/bin/psql/print.c
src/bin/psql/print.h
src/bin/psql/prompt.c
src/bin/psql/prompt.h
src/bin/psql/ru.po
src/bin/psql/settings.h
src/bin/psql/startup.c
src/bin/psql/stringutils.c
src/bin/psql/stringutils.h
src/bin/psql/sv.po
src/bin/psql/tab-complete.c
src/bin/psql/tab-complete.h
src/bin/psql/variables.c
src/bin/psql/variables.h
src/bin/psql/win32.mak
src/bin/psql/zh_CN.po
src/bin/psql/zh_TW.po
src/bin/scripts/Makefile
src/bin/scripts/createdb
src/bin/scripts/createlang.sh
src/bin/scripts/createuser
src/bin/scripts/dropdb
src/bin/scripts/droplang
src/bin/scripts/dropuser
src/bin/scripts/vacuumdb
src/corba/CosQuery.idl
src/corba/CosQueryCollection.idl
src/corba/pgsql.idl
src/corba/pgsql_int.idl
src/corba/server.cc
src/data/charset.conf
src/data/isocz-wincz.tab
src/data/koi-alt.tab
src/data/koi-iso.tab
src/data/koi-koi.tab
src/data/koi-mac.tab
src/data/koi-win.tab
src/include/Makefile
src/include/access/attnum.h
src/include/access/clog.h
src/include/access/genam.h
src/include/access/gist.h
src/include/access/gistscan.h
src/include/access/hash.h
src/include/access/heapam.h
src/include/access/hio.h
src/include/access/htup.h
src/include/access/ibit.h
src/include/access/iqual.h
src/include/access/istrat.h
src/include/access/itup.h
src/include/access/nbtree.h
src/include/access/printtup.h
src/include/access/relscan.h
src/include/access/rmgr.h
src/include/access/rtree.h
src/include/access/rtscan.h
src/include/access/sdir.h
src/include/access/skey.h
src/include/access/strat.h
src/include/access/transam.h
src/include/access/tupdesc.h
src/include/access/tupmacs.h
src/include/access/tuptoaster.h
src/include/access/valid.h
src/include/access/xact.h
src/include/access/xlog.h
src/include/access/xlogdefs.h
src/include/access/xlogutils.h
src/include/bootstrap/bootstrap.h
src/include/c.h
src/include/catalog/catalog.h
src/include/catalog/catname.h
src/include/catalog/catversion.h
src/include/catalog/duplicate_oids
src/include/catalog/heap.h
src/include/catalog/index.h
src/include/catalog/indexing.h
src/include/catalog/namespace.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_am.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_attrdef.h
src/include/catalog/pg_attribute.h
src/include/catalog/pg_class.h
src/include/catalog/pg_control.h
src/include/catalog/pg_database.h
src/include/catalog/pg_description.h
src/include/catalog/pg_group.h
src/include/catalog/pg_index.h
src/include/catalog/pg_inherits.h
src/include/catalog/pg_language.h
src/include/catalog/pg_largeobject.h
src/include/catalog/pg_listener.h
src/include/catalog/pg_namespace.h
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/catalog/pg_relcheck.h
src/include/catalog/pg_rewrite.h
src/include/catalog/pg_shadow.h
src/include/catalog/pg_statistic.h
src/include/catalog/pg_trigger.h
src/include/catalog/pg_type.h
src/include/catalog/pg_version.h
src/include/catalog/unused_oids
src/include/commands/async.h
src/include/commands/cluster.h
src/include/commands/comment.h
src/include/commands/copy.h
src/include/commands/dbcommands.h
src/include/commands/defrem.h
src/include/commands/explain.h
src/include/commands/lockcmds.h
src/include/commands/portalcmds.h
src/include/commands/proclang.h
src/include/commands/schemacmds.h
src/include/commands/sequence.h
src/include/commands/tablecmds.h
src/include/commands/trigger.h
src/include/commands/user.h
src/include/commands/vacuum.h
src/include/commands/variable.h
src/include/commands/version.h
src/include/commands/view.h
src/include/executor/execdebug.h
src/include/executor/execdefs.h
src/include/executor/execdesc.h
src/include/executor/executor.h
src/include/executor/functions.h
src/include/executor/hashjoin.h
src/include/executor/instrument.h
src/include/executor/nodeAgg.h
src/include/executor/nodeAppend.h
src/include/executor/nodeFunctionscan.h
src/include/executor/nodeGroup.h
src/include/executor/nodeHash.h
src/include/executor/nodeHashjoin.h
src/include/executor/nodeIndexscan.h
src/include/executor/nodeLimit.h
src/include/executor/nodeMaterial.h
src/include/executor/nodeMergejoin.h
src/include/executor/nodeNestloop.h
src/include/executor/nodeResult.h
src/include/executor/nodeSeqscan.h
src/include/executor/nodeSetOp.h
src/include/executor/nodeSort.h
src/include/executor/nodeSubplan.h
src/include/executor/nodeSubqueryscan.h
src/include/executor/nodeTidscan.h
src/include/executor/nodeUnique.h
src/include/executor/spi.h
src/include/executor/spi_priv.h
src/include/executor/tuptable.h
src/include/fmgr.h
src/include/lib/dllist.h
src/include/lib/lispsort.h
src/include/lib/stringinfo.h
src/include/libpq/auth.h
src/include/libpq/be-fsstubs.h
src/include/libpq/crypt.h
src/include/libpq/hba.h
src/include/libpq/libpq-be.h
src/include/libpq/libpq-fs.h
src/include/libpq/libpq.h
src/include/libpq/password.h
src/include/libpq/pqcomm.h
src/include/libpq/pqformat.h
src/include/libpq/pqsignal.h
src/include/mb/pg_wchar.h
src/include/miscadmin.h
src/include/nodes/execnodes.h
src/include/nodes/makefuncs.h
src/include/nodes/memnodes.h
src/include/nodes/nodeFuncs.h
src/include/nodes/nodes.h
src/include/nodes/params.h
src/include/nodes/parsenodes.h
src/include/nodes/pg_list.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/print.h
src/include/nodes/readfuncs.h
src/include/nodes/relation.h
src/include/optimizer/_deadcode/xfunc.h
src/include/optimizer/clauses.h
src/include/optimizer/cost.h
src/include/optimizer/geqo.h
src/include/optimizer/geqo_copy.h
src/include/optimizer/geqo_gene.h
src/include/optimizer/geqo_misc.h
src/include/optimizer/geqo_mutation.h
src/include/optimizer/geqo_pool.h
src/include/optimizer/geqo_random.h
src/include/optimizer/geqo_recombination.h
src/include/optimizer/geqo_selection.h
src/include/optimizer/joininfo.h
src/include/optimizer/pathnode.h
src/include/optimizer/paths.h
src/include/optimizer/plancat.h
src/include/optimizer/planmain.h
src/include/optimizer/planner.h
src/include/optimizer/prep.h
src/include/optimizer/restrictinfo.h
src/include/optimizer/subselect.h
src/include/optimizer/tlist.h
src/include/optimizer/var.h
src/include/parser/analyze.h
src/include/parser/gramparse.h
src/include/parser/keywords.h
src/include/parser/parse_agg.h
src/include/parser/parse_clause.h
src/include/parser/parse_coerce.h
src/include/parser/parse_expr.h
src/include/parser/parse_func.h
src/include/parser/parse_node.h
src/include/parser/parse_oper.h
src/include/parser/parse_relation.h
src/include/parser/parse_target.h
src/include/parser/parse_type.h
src/include/parser/parser.h
src/include/parser/parsetree.h
src/include/parser/scansup.h
src/include/pg_config.h.in
src/include/pg_config.h.win32
src/include/pgstat.h
src/include/port/aix.h
src/include/port/beos.h
src/include/port/bsdi.h
src/include/port/darwin.h
src/include/port/dgux.h
src/include/port/freebsd.h
src/include/port/hpux.h
src/include/port/irix5.h
src/include/port/linux.h
src/include/port/netbsd.h
src/include/port/nextstep.h
src/include/port/openbsd.h
src/include/port/osf.h
src/include/port/qnx4.h
src/include/port/sco.h
src/include/port/solaris.h
src/include/port/sunos4.h
src/include/port/svr4.h
src/include/port/ultrix4.h
src/include/port/univel.h
src/include/port/unixware.h
src/include/port/win.h
src/include/port/win32.h
src/include/postgres.h
src/include/postgres_ext.h
src/include/postgres_fe.h
src/include/regex/cclass.h
src/include/regex/cname.h
src/include/regex/regex.h
src/include/regex/regex2.h
src/include/regex/utils.h
src/include/rewrite/prs2lock.h
src/include/rewrite/rewriteDefine.h
src/include/rewrite/rewriteHandler.h
src/include/rewrite/rewriteManip.h
src/include/rewrite/rewriteRemove.h
src/include/rewrite/rewriteSupport.h
src/include/rusagestub.h
src/include/storage/backendid.h
src/include/storage/block.h
src/include/storage/buf.h
src/include/storage/buf_internals.h
src/include/storage/buffile.h
src/include/storage/bufmgr.h
src/include/storage/bufpage.h
src/include/storage/fd.h
src/include/storage/freespace.h
src/include/storage/ipc.h
src/include/storage/item.h
src/include/storage/itemid.h
src/include/storage/itempos.h
src/include/storage/itemptr.h
src/include/storage/large_object.h
src/include/storage/lmgr.h
src/include/storage/lock.h
src/include/storage/lwlock.h
src/include/storage/off.h
src/include/storage/page.h
src/include/storage/pg_sema.h
src/include/storage/pg_shmem.h
src/include/storage/pmsignal.h
src/include/storage/pos.h
src/include/storage/proc.h
src/include/storage/relfilenode.h
src/include/storage/s_lock.h
src/include/storage/shmem.h
src/include/storage/sinval.h
src/include/storage/sinvaladt.h
src/include/storage/smgr.h
src/include/storage/spin.h
src/include/strdup.h
src/include/tcop/dest.h
src/include/tcop/fastpath.h
src/include/tcop/pquery.h
src/include/tcop/tcopdebug.h
src/include/tcop/tcopprot.h
src/include/tcop/utility.h
src/include/utils/acl.h
src/include/utils/array.h
src/include/utils/ascii.h
src/include/utils/bit.h
src/include/utils/builtins.h
src/include/utils/cash.h
src/include/utils/catcache.h
src/include/utils/date.h
src/include/utils/datetime.h
src/include/utils/datum.h
src/include/utils/dynahash.h
src/include/utils/dynamic_loader.h
src/include/utils/elog.h
src/include/utils/exc.h
src/include/utils/excid.h
src/include/utils/fcache.h
src/include/utils/fmgrtab.h
src/include/utils/formatting.h
src/include/utils/geo_decls.h
src/include/utils/guc.h
src/include/utils/hsearch.h
src/include/utils/inet.h
src/include/utils/int8.h
src/include/utils/inval.h
src/include/utils/logtape.h
src/include/utils/lsyscache.h
src/include/utils/memutils.h
src/include/utils/nabstime.h
src/include/utils/numeric.h
src/include/utils/palloc.h
src/include/utils/pg_crc.h
src/include/utils/pg_locale.h
src/include/utils/pg_lzcompress.h
src/include/utils/portal.h
src/include/utils/ps_status.h
src/include/utils/rel.h
src/include/utils/relcache.h
src/include/utils/selfuncs.h
src/include/utils/sets.h
src/include/utils/syscache.h
src/include/utils/timestamp.h
src/include/utils/tqual.h
src/include/utils/tuplesort.h
src/include/utils/tuplestore.h
src/include/utils/varbit.h
src/interfaces/Makefile
src/interfaces/cli/example1.c
src/interfaces/cli/example2.c
src/interfaces/cli/sqlcli.h
src/interfaces/jdbc/CHANGELOG
src/interfaces/jdbc/Implementation
src/interfaces/jdbc/Makefile
src/interfaces/jdbc/README
src/interfaces/jdbc/build.xml
src/interfaces/jdbc/example/ImageViewer.java
src/interfaces/jdbc/example/Unicode.java
src/interfaces/jdbc/example/basic.java
src/interfaces/jdbc/example/blobtest.java
src/interfaces/jdbc/example/corba/StockClient.java
src/interfaces/jdbc/example/corba/StockDB.java
src/interfaces/jdbc/example/corba/StockDispenserImpl.java
src/interfaces/jdbc/example/corba/StockItemImpl.java
src/interfaces/jdbc/example/corba/StockServer.java
src/interfaces/jdbc/example/corba/readme
src/interfaces/jdbc/example/corba/stock.idl
src/interfaces/jdbc/example/corba/stock.sql
src/interfaces/jdbc/example/datestyle.java
src/interfaces/jdbc/example/metadata.java
src/interfaces/jdbc/example/psql.java
src/interfaces/jdbc/example/threadsafe.java
src/interfaces/jdbc/jdbc.jpx
src/interfaces/jdbc/org/postgresql/Connection.java
src/interfaces/jdbc/org/postgresql/Driver.java.in
src/interfaces/jdbc/org/postgresql/Field.java
src/interfaces/jdbc/org/postgresql/PG_Stream.java
src/interfaces/jdbc/org/postgresql/PostgresqlDataSource.java
src/interfaces/jdbc/org/postgresql/ResultSet.java
src/interfaces/jdbc/org/postgresql/Statement.java
src/interfaces/jdbc/org/postgresql/core/BytePoolDim1.java
src/interfaces/jdbc/org/postgresql/core/BytePoolDim2.java
src/interfaces/jdbc/org/postgresql/core/Encoding.java
src/interfaces/jdbc/org/postgresql/core/MemoryPool.java
src/interfaces/jdbc/org/postgresql/core/ObjectPool.java
src/interfaces/jdbc/org/postgresql/core/QueryExecutor.java
src/interfaces/jdbc/org/postgresql/core/SimpleObjectPool.java
src/interfaces/jdbc/org/postgresql/core/StartupPacket.java
src/interfaces/jdbc/org/postgresql/errors.properties
src/interfaces/jdbc/org/postgresql/errors_de.properties
src/interfaces/jdbc/org/postgresql/errors_fr.properties
src/interfaces/jdbc/org/postgresql/errors_it.properties
src/interfaces/jdbc/org/postgresql/errors_nl.properties
src/interfaces/jdbc/org/postgresql/errors_zh_TW.properties
src/interfaces/jdbc/org/postgresql/fastpath/Fastpath.java
src/interfaces/jdbc/org/postgresql/fastpath/FastpathArg.java
src/interfaces/jdbc/org/postgresql/geometric/PGbox.java
src/interfaces/jdbc/org/postgresql/geometric/PGcircle.java
src/interfaces/jdbc/org/postgresql/geometric/PGline.java
src/interfaces/jdbc/org/postgresql/geometric/PGlseg.java
src/interfaces/jdbc/org/postgresql/geometric/PGpath.java
src/interfaces/jdbc/org/postgresql/geometric/PGpoint.java
src/interfaces/jdbc/org/postgresql/geometric/PGpolygon.java
src/interfaces/jdbc/org/postgresql/jdbc1/CallableStatement.java
src/interfaces/jdbc/org/postgresql/jdbc1/Connection.java
src/interfaces/jdbc/org/postgresql/jdbc1/DatabaseMetaData.java
src/interfaces/jdbc/org/postgresql/jdbc1/PreparedStatement.java
src/interfaces/jdbc/org/postgresql/jdbc1/ResultSet.java
src/interfaces/jdbc/org/postgresql/jdbc1/ResultSetMetaData.java
src/interfaces/jdbc/org/postgresql/jdbc1/Statement.java
src/interfaces/jdbc/org/postgresql/jdbc2/Array.java
src/interfaces/jdbc/org/postgresql/jdbc2/CallableStatement.java
src/interfaces/jdbc/org/postgresql/jdbc2/Connection.java
src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java
src/interfaces/jdbc/org/postgresql/jdbc2/PBatchUpdateException.java
src/interfaces/jdbc/org/postgresql/jdbc2/PreparedStatement.java
src/interfaces/jdbc/org/postgresql/jdbc2/ResultSet.java
src/interfaces/jdbc/org/postgresql/jdbc2/ResultSetMetaData.java
src/interfaces/jdbc/org/postgresql/jdbc2/Statement.java
src/interfaces/jdbc/org/postgresql/jdbc2/UpdateableResultSet.java
src/interfaces/jdbc/org/postgresql/largeobject/BlobInputStream.java
src/interfaces/jdbc/org/postgresql/largeobject/BlobOutputStream.java
src/interfaces/jdbc/org/postgresql/largeobject/LargeObject.java
src/interfaces/jdbc/org/postgresql/largeobject/LargeObjectManager.java
src/interfaces/jdbc/org/postgresql/largeobject/PGblob.java
src/interfaces/jdbc/org/postgresql/largeobject/PGclob.java
src/interfaces/jdbc/org/postgresql/test/JDBC2Tests.java
src/interfaces/jdbc/org/postgresql/test/README
src/interfaces/jdbc/org/postgresql/test/jdbc2/ANTTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/BatchExecuteTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/BlobTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/ConnectionTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/DateTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/DriverTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/EncodingTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/JBuilderTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/MiscTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/ResultSetTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/TimeTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/TimestampTest.java
src/interfaces/jdbc/org/postgresql/test/jdbc2/UpdateableResultTest.java
src/interfaces/jdbc/org/postgresql/util/MD5Digest.java
src/interfaces/jdbc/org/postgresql/util/MessageTranslator.java
src/interfaces/jdbc/org/postgresql/util/PGbytea.java
src/interfaces/jdbc/org/postgresql/util/PGmoney.java
src/interfaces/jdbc/org/postgresql/util/PGobject.java
src/interfaces/jdbc/org/postgresql/util/PGtokenizer.java
src/interfaces/jdbc/org/postgresql/util/PSQLException.java
src/interfaces/jdbc/org/postgresql/util/Serialize.java
src/interfaces/jdbc/org/postgresql/util/UnixCrypt.java
src/interfaces/jdbc/org/postgresql/xa/ClientConnection.java
src/interfaces/jdbc/org/postgresql/xa/TwoPhaseConnection.java
src/interfaces/jdbc/org/postgresql/xa/TxConnection.java
src/interfaces/jdbc/org/postgresql/xa/XAConnectionImpl.java
src/interfaces/jdbc/org/postgresql/xa/XADataSourceImpl.java
src/interfaces/jdbc/utils/CheckVersion.java
src/interfaces/jdbc/utils/buildDriver
src/interfaces/jdbc/utils/changelog.pl
src/interfaces/libpgeasy/Makefile
src/interfaces/libpgeasy/README
src/interfaces/libpgeasy/examples/Makefile
src/interfaces/libpgeasy/examples/pginsert.c
src/interfaces/libpgeasy/examples/pgmultiresult.c
src/interfaces/libpgeasy/examples/pgnulltest.c
src/interfaces/libpgeasy/examples/pgwordcount.c
src/interfaces/libpgeasy/halt.c
src/interfaces/libpgeasy/halt.h
src/interfaces/libpgeasy/libpgeasy.c
src/interfaces/libpgeasy/libpgeasy.h
src/interfaces/libpgtcl/Makefile
src/interfaces/libpgtcl/README
src/interfaces/libpgtcl/libpgtcl.def
src/interfaces/libpgtcl/libpgtcl.h
src/interfaces/libpgtcl/pgtcl.c
src/interfaces/libpgtcl/pgtclCmds.c
src/interfaces/libpgtcl/pgtclCmds.h
src/interfaces/libpgtcl/pgtclId.c
src/interfaces/libpgtcl/pgtclId.h
src/interfaces/libpgtcl/win32.mak
src/interfaces/libpq/Makefile
src/interfaces/libpq/README
src/interfaces/libpq/cs.po
src/interfaces/libpq/de.po
src/interfaces/libpq/fe-auth.c
src/interfaces/libpq/fe-auth.h
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/fe-exec.c
src/interfaces/libpq/fe-lobj.c
src/interfaces/libpq/fe-misc.c
src/interfaces/libpq/fe-print.c
src/interfaces/libpq/fe-secure.c
src/interfaces/libpq/fr.po
src/interfaces/libpq/libpq-fe.h
src/interfaces/libpq/libpq-int.h
src/interfaces/libpq/libpq.rc
src/interfaces/libpq/libpqdll.c
src/interfaces/libpq/libpqdll.def
src/interfaces/libpq/nls.mk
src/interfaces/libpq/pqexpbuffer.c
src/interfaces/libpq/pqexpbuffer.h
src/interfaces/libpq/pqsignal.c
src/interfaces/libpq/pqsignal.h
src/interfaces/libpq/ru.po
src/interfaces/libpq/sv.po
src/interfaces/libpq/win32.c
src/interfaces/libpq/win32.h
src/interfaces/libpq/win32.mak
src/interfaces/libpq/zh_CN.po
src/interfaces/libpq/zh_TW.po
src/interfaces/libpq++/CHANGES
src/interfaces/libpq++/Makefile
src/interfaces/libpq++/README
src/interfaces/libpq++/TODO
src/interfaces/libpq++/examples/Makefile
src/interfaces/libpq++/examples/testlibpq0.cc
src/interfaces/libpq++/examples/testlibpq1.cc
src/interfaces/libpq++/examples/testlibpq2.cc
src/interfaces/libpq++/examples/testlibpq2.sql
src/interfaces/libpq++/examples/testlibpq3.cc
src/interfaces/libpq++/examples/testlibpq3.sql
src/interfaces/libpq++/examples/testlibpq4.cc
src/interfaces/libpq++/examples/testlibpq4.sql
src/interfaces/libpq++/examples/testlibpq5.cc
src/interfaces/libpq++/examples/testlibpq5.sql
src/interfaces/libpq++/examples/testlibpq6.cc
src/interfaces/libpq++/examples/testlo.cc
src/interfaces/libpq++/libpq++.h
src/interfaces/libpq++/libpq++dll.rc
src/interfaces/libpq++/pgconnection.cc
src/interfaces/libpq++/pgconnection.h
src/interfaces/libpq++/pgcursordb.cc
src/interfaces/libpq++/pgcursordb.h
src/interfaces/libpq++/pgdatabase.cc
src/interfaces/libpq++/pgdatabase.h
src/interfaces/libpq++/pglobject.cc
src/interfaces/libpq++/pglobject.h
src/interfaces/libpq++/pgtransdb.cc
src/interfaces/libpq++/pgtransdb.h
src/interfaces/libpq++/win32.mak
src/interfaces/odbc/GNUmakefile
src/interfaces/odbc/bind.c
src/interfaces/odbc/bind.h
src/interfaces/odbc/columninfo.c
src/interfaces/odbc/columninfo.h
src/interfaces/odbc/connection.c
src/interfaces/odbc/connection.h
src/interfaces/odbc/convert.c
src/interfaces/odbc/convert.h
src/interfaces/odbc/descriptor.h
src/interfaces/odbc/dlg_specific.c
src/interfaces/odbc/dlg_specific.h
src/interfaces/odbc/dlg_wingui.c
src/interfaces/odbc/drvconn.c
src/interfaces/odbc/environ.c
src/interfaces/odbc/environ.h
src/interfaces/odbc/execute.c
src/interfaces/odbc/gpps.c
src/interfaces/odbc/gpps.h
src/interfaces/odbc/info.c
src/interfaces/odbc/info30.c
src/interfaces/odbc/iodbc.h
src/interfaces/odbc/isql.h
src/interfaces/odbc/isqlext.h
src/interfaces/odbc/license.txt
src/interfaces/odbc/lobj.c
src/interfaces/odbc/lobj.h
src/interfaces/odbc/md5.c
src/interfaces/odbc/md5.h
src/interfaces/odbc/misc.c
src/interfaces/odbc/misc.h
src/interfaces/odbc/multibyte.c
src/interfaces/odbc/multibyte.h
src/interfaces/odbc/notice.txt
src/interfaces/odbc/odbc.sql
src/interfaces/odbc/odbcapi.c
src/interfaces/odbc/odbcapi25w.c
src/interfaces/odbc/odbcapi30.c
src/interfaces/odbc/odbcapi30w.c
src/interfaces/odbc/odbcapiw.c
src/interfaces/odbc/odbcinst.ini
src/interfaces/odbc/options.c
src/interfaces/odbc/parse.c
src/interfaces/odbc/pgapi30.c
src/interfaces/odbc/pgapifunc.h
src/interfaces/odbc/pgtypes.c
src/interfaces/odbc/pgtypes.h
src/interfaces/odbc/psqlodbc.c
src/interfaces/odbc/psqlodbc.h
src/interfaces/odbc/psqlodbc.rc
src/interfaces/odbc/psqlodbc.reg
src/interfaces/odbc/psqlodbc30.reg
src/interfaces/odbc/psqlodbc30w.reg
src/interfaces/odbc/psqlodbc_api30.def
src/interfaces/odbc/psqlodbc_api30w.def
src/interfaces/odbc/psqlodbc_apiw.def
src/interfaces/odbc/psqlodbc_win32.def
src/interfaces/odbc/qresult.c
src/interfaces/odbc/qresult.h
src/interfaces/odbc/readme.txt
src/interfaces/odbc/resource.h
src/interfaces/odbc/results.c
src/interfaces/odbc/setup.c
src/interfaces/odbc/setup.rul
src/interfaces/odbc/socket.c
src/interfaces/odbc/socket.h
src/interfaces/odbc/statement.c
src/interfaces/odbc/statement.h
src/interfaces/odbc/tuple.c
src/interfaces/odbc/tuple.h
src/interfaces/odbc/tuplelist.c
src/interfaces/odbc/tuplelist.h
src/interfaces/odbc/version.h
src/interfaces/odbc/win32.mak
src/interfaces/odbc/win32_30.mak
src/interfaces/odbc/win32_30w.mak
src/interfaces/odbc/win32w.mak
src/interfaces/odbc/win_md5.c
src/interfaces/odbc/win_setup.h
src/interfaces/odbc/win_unicode.c
src/interfaces/perl5/Changes
src/interfaces/perl5/GNUmakefile
src/interfaces/perl5/MANIFEST
src/interfaces/perl5/Makefile.PL
src/interfaces/perl5/Pg.pm
src/interfaces/perl5/Pg.xs
src/interfaces/perl5/README
src/interfaces/perl5/examples/ApachePg.pl
src/interfaces/perl5/examples/example.newstyle
src/interfaces/perl5/examples/example.oldstyle
src/interfaces/perl5/ppport.h
src/interfaces/perl5/test.pl
src/interfaces/perl5/typemap
src/interfaces/python/Announce
src/interfaces/python/ChangeLog
src/interfaces/python/GNUmakefile
src/interfaces/python/PyGreSQL.spec
src/interfaces/python/README
src/interfaces/python/README.linux
src/interfaces/python/Setup.in.raw
src/interfaces/python/pg.py
src/interfaces/python/pgdb.py
src/interfaces/python/pgmodule.c
src/interfaces/python/setup.py
src/interfaces/python/tutorial/advanced.py
src/interfaces/python/tutorial/basics.py
src/interfaces/python/tutorial/func.py
src/interfaces/python/tutorial/syscat.py
src/interfaces/ssl/client.conf
src/interfaces/ssl/mkcert.sh
src/interfaces/ssl/pgkeygen.sh
src/interfaces/ssl/root.conf
src/interfaces/ssl/server.conf
src/makefiles/Makefile.aix
src/makefiles/Makefile.beos
src/makefiles/Makefile.bsdi
src/makefiles/Makefile.darwin
src/makefiles/Makefile.dgux
src/makefiles/Makefile.freebsd
src/makefiles/Makefile.hpux
src/makefiles/Makefile.irix5
src/makefiles/Makefile.linux
src/makefiles/Makefile.netbsd
src/makefiles/Makefile.openbsd
src/makefiles/Makefile.osf
src/makefiles/Makefile.qnx4
src/makefiles/Makefile.sco
src/makefiles/Makefile.solaris
src/makefiles/Makefile.sunos4
src/makefiles/Makefile.svr4
src/makefiles/Makefile.ultrix4
src/makefiles/Makefile.univel
src/makefiles/Makefile.unixware
src/makefiles/Makefile.win
src/nls-global.mk
src/pl/Makefile
src/pl/plperl/GNUmakefile
src/pl/plperl/README
src/pl/plperl/SPI.xs
src/pl/plperl/eloglvl.c
src/pl/plperl/eloglvl.h
src/pl/plperl/plperl.c
src/pl/plperl/ppport.h
src/pl/plpgsql/Makefile
src/pl/plpgsql/src/.cvsignore
src/pl/plpgsql/src/INSTALL
src/pl/plpgsql/src/Makefile
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_funcs.c
src/pl/plpgsql/src/pl_handler.c
src/pl/plpgsql/src/plpgsql.h
src/pl/plpgsql/src/scan.l
src/pl/plpgsql/test/README
src/pl/plpgsql/test/expected/tables.out
src/pl/plpgsql/test/expected/test.out
src/pl/plpgsql/test/expected/triggers.out
src/pl/plpgsql/test/expected/views.out
src/pl/plpgsql/test/runtest
src/pl/plpgsql/test/tables.sql
src/pl/plpgsql/test/test.sql
src/pl/plpgsql/test/triggers.sql
src/pl/plpgsql/test/views.sql
src/pl/plpython/Makefile
src/pl/plpython/README
src/pl/plpython/TODO
src/pl/plpython/error.expected
src/pl/plpython/feature.expected
src/pl/plpython/plpython.c
src/pl/plpython/plpython.h
src/pl/plpython/plpython_depopulate.sql
src/pl/plpython/plpython_deschema.sql
src/pl/plpython/plpython_drop.sql
src/pl/plpython/plpython_error.sql
src/pl/plpython/plpython_function.sql
src/pl/plpython/plpython_populate.sql
src/pl/plpython/plpython_schema.sql
src/pl/plpython/plpython_setof.sql
src/pl/plpython/plpython_test.sql
src/pl/plpython/test.sh
src/pl/tcl/Makefile
src/pl/tcl/license.terms
src/pl/tcl/modules/Makefile
src/pl/tcl/modules/README
src/pl/tcl/modules/pltcl_delmod.in
src/pl/tcl/modules/pltcl_listmod.in
src/pl/tcl/modules/pltcl_loadmod.in
src/pl/tcl/modules/unknown.pltcl
src/pl/tcl/pltcl.c
src/pl/tcl/test/README
src/pl/tcl/test/runtest
src/pl/tcl/test/test.expected
src/pl/tcl/test/test_queries.sql
src/pl/tcl/test/test_setup.sql
src/template/aix
src/template/beos
src/template/bsdi
src/template/darwin
src/template/dgux
src/template/freebsd
src/template/hpux
src/template/irix5
src/template/linux
src/template/netbsd
src/template/nextstep
src/template/openbsd
src/template/osf
src/template/qnx4
src/template/sco
src/template/solaris
src/template/sunos4
src/template/svr4
src/template/ultrix4
src/template/univel
src/template/unixware
src/template/win
src/test/Makefile
src/test/bench/Makefile
src/test/bench/WISC-README
src/test/bench/create.sh
src/test/bench/create.source
src/test/bench/perquery
src/test/bench/query01
src/test/bench/query02
src/test/bench/query03
src/test/bench/query04
src/test/bench/query05
src/test/bench/query06
src/test/bench/query07
src/test/bench/query08
src/test/bench/query09
src/test/bench/query10
src/test/bench/query11
src/test/bench/query12
src/test/bench/query13
src/test/bench/query14
src/test/bench/query15
src/test/bench/query16
src/test/bench/query17
src/test/bench/query18
src/test/bench/query19
src/test/bench/query20
src/test/bench/query21
src/test/bench/query22
src/test/bench/query23
src/test/bench/query24
src/test/bench/query25
src/test/bench/query26
src/test/bench/query27
src/test/bench/query28
src/test/bench/query29
src/test/bench/query30
src/test/bench/query31
src/test/bench/query32
src/test/bench/runwisc.sh
src/test/bench/wholebench.sh
src/test/examples/Makefile
src/test/examples/testlibpq.c
src/test/examples/testlibpq2.c
src/test/examples/testlibpq2.sql
src/test/examples/testlibpq3.c
src/test/examples/testlibpq3.sql
src/test/examples/testlibpq4.c
src/test/examples/testlo.c
src/test/locale/Makefile
src/test/locale/README
src/test/locale/de_DE.ISO8859-1/Makefile
src/test/locale/de_DE.ISO8859-1/README
src/test/locale/de_DE.ISO8859-1/expected/de-ctype.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-char.sql.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-select.sql.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-sort.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-text.sql.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-upper-char.sql.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-upper-text.sql.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-upper-varchar.sql.out
src/test/locale/de_DE.ISO8859-1/expected/test-de-varchar.sql.out
src/test/locale/de_DE.ISO8859-1/runall
src/test/locale/de_DE.ISO8859-1/test-de-select.sql.in
src/test/locale/de_DE.ISO8859-1/test-de-sort.in
src/test/locale/de_DE.ISO8859-1/test-de-upper.sql.in
src/test/locale/de_DE.ISO8859-1/test-de.sql.in
src/test/locale/gr_GR.ISO8859-7/Makefile
src/test/locale/gr_GR.ISO8859-7/README
src/test/locale/gr_GR.ISO8859-7/expected/gr-ctype.out
src/test/locale/gr_GR.ISO8859-7/expected/test-gr-char.sql.out
src/test/locale/gr_GR.ISO8859-7/expected/test-gr-select.sql.out
src/test/locale/gr_GR.ISO8859-7/expected/test-gr-sort.out
src/test/locale/gr_GR.ISO8859-7/expected/test-gr-text.sql.out
src/test/locale/gr_GR.ISO8859-7/expected/test-gr-varchar.sql.out
src/test/locale/gr_GR.ISO8859-7/runall
src/test/locale/gr_GR.ISO8859-7/test-gr-select.sql.in
src/test/locale/gr_GR.ISO8859-7/test-gr-sort.in
src/test/locale/gr_GR.ISO8859-7/test-gr.sql.in
src/test/locale/koi8-r/Makefile
src/test/locale/koi8-r/expected/koi8-ctype.out
src/test/locale/koi8-r/expected/test-koi8-char.sql.out
src/test/locale/koi8-r/expected/test-koi8-select.sql.out
src/test/locale/koi8-r/expected/test-koi8-sort.out
src/test/locale/koi8-r/expected/test-koi8-text.sql.out
src/test/locale/koi8-r/expected/test-koi8-varchar.sql.out
src/test/locale/koi8-r/runall
src/test/locale/koi8-r/test-koi8-select.sql.in
src/test/locale/koi8-r/test-koi8-sort.in
src/test/locale/koi8-r/test-koi8.sql.in
src/test/locale/koi8-to-win1251/Makefile
src/test/locale/koi8-to-win1251/README
src/test/locale/koi8-to-win1251/expected/test-koi8-char.sql.out
src/test/locale/koi8-to-win1251/expected/test-koi8-select.sql.out
src/test/locale/koi8-to-win1251/expected/test-koi8-sort.out
src/test/locale/koi8-to-win1251/expected/test-koi8-text.sql.out
src/test/locale/koi8-to-win1251/expected/test-koi8-varchar.sql.out
src/test/locale/koi8-to-win1251/runall
src/test/locale/koi8-to-win1251/test-koi8-select.sql.in
src/test/locale/koi8-to-win1251/test-koi8-sort.in
src/test/locale/koi8-to-win1251/test-koi8.sql.in
src/test/locale/sort-test.pl
src/test/locale/sort-test.py
src/test/locale/test-ctype.c
src/test/locale/test-pgsql-locale.c
src/test/mb/README
src/test/mb/expected/big5.out
src/test/mb/expected/euc_cn.out
src/test/mb/expected/euc_jp.out
src/test/mb/expected/euc_kr.out
src/test/mb/expected/euc_tw.out
src/test/mb/expected/mule_internal.out
src/test/mb/expected/sjis.out
src/test/mb/expected/unicode.out
src/test/mb/mbregress.sh
src/test/mb/sql/big5.sql
src/test/mb/sql/euc_cn.sql
src/test/mb/sql/euc_jp.sql
src/test/mb/sql/euc_kr.sql
src/test/mb/sql/euc_tw.sql
src/test/mb/sql/mule_internal.sql
src/test/mb/sql/sjis.sql
src/test/mb/sql/unicode.sql
src/test/performance/results/PgSQL.970926
src/test/performance/runtests.pl
src/test/performance/sqls/connection
src/test/performance/sqls/crtsimple
src/test/performance/sqls/crtsimpleidx
src/test/performance/sqls/drpsimple
src/test/performance/sqls/inssimple
src/test/performance/sqls/inssimple.data
src/test/performance/sqls/orbsimple
src/test/performance/sqls/slcsimple
src/test/performance/sqls/slcsimple.data
src/test/performance/sqls/vacuum
src/test/performance/start-pgsql.sh
src/test/regress/GNUmakefile
src/test/regress/Makefile
src/test/regress/README
src/test/regress/data/agg.data
src/test/regress/data/constrf.data
src/test/regress/data/constro.data
src/test/regress/data/dept.data
src/test/regress/data/desc.data
src/test/regress/data/emp.data
src/test/regress/data/hash.data
src/test/regress/data/onek.data
src/test/regress/data/person.data
src/test/regress/data/real_city.data
src/test/regress/data/rect.data
src/test/regress/data/streets.data
src/test/regress/data/stud_emp.data
src/test/regress/data/student.data
src/test/regress/data/tenk.data
src/test/regress/expected/abstime-solaris-1947.out
src/test/regress/expected/abstime.out
src/test/regress/expected/aggregates.out
src/test/regress/expected/alter_table.out
src/test/regress/expected/arrays.out
src/test/regress/expected/bit.out
src/test/regress/expected/boolean.out
src/test/regress/expected/box.out
src/test/regress/expected/btree_index.out
src/test/regress/expected/case.out
src/test/regress/expected/char.out
src/test/regress/expected/char_1.out
src/test/regress/expected/circle.out
src/test/regress/expected/comments.out
src/test/regress/expected/create_aggregate.out
src/test/regress/expected/create_index.out
src/test/regress/expected/create_misc.out
src/test/regress/expected/create_operator.out
src/test/regress/expected/create_table.out
src/test/regress/expected/create_type.out
src/test/regress/expected/create_view.out
src/test/regress/expected/date.out
src/test/regress/expected/domain.out
src/test/regress/expected/errors.out
src/test/regress/expected/euc_cn.out
src/test/regress/expected/euc_jp.out
src/test/regress/expected/euc_kr.out
src/test/regress/expected/euc_tw.out
src/test/regress/expected/float4-exp-three-digits.out
src/test/regress/expected/float4.out
src/test/regress/expected/float8-exp-three-digits.out
src/test/regress/expected/float8-fp-exception.out
src/test/regress/expected/float8-small-is-zero.out
src/test/regress/expected/float8.out
src/test/regress/expected/foreign_key.out
src/test/regress/expected/geometry-alpha-precision.out
src/test/regress/expected/geometry-bsdi-precision.out
src/test/regress/expected/geometry-i86-gnulibc.out
src/test/regress/expected/geometry-intel-beos.out
src/test/regress/expected/geometry-irix.out
src/test/regress/expected/geometry-positive-zeros-bsd.out
src/test/regress/expected/geometry-positive-zeros.out
src/test/regress/expected/geometry-powerpc-aix4.out
src/test/regress/expected/geometry-powerpc-darwin.out
src/test/regress/expected/geometry-powerpc-linux-gnulibc1.out
src/test/regress/expected/geometry-solaris-i386-pc.out
src/test/regress/expected/geometry-solaris-precision.out
src/test/regress/expected/geometry-uw7-cc.out
src/test/regress/expected/geometry-uw7-gcc.out
src/test/regress/expected/geometry.out
src/test/regress/expected/hash_index.out
src/test/regress/expected/horology-no-DST-before-1970.out
src/test/regress/expected/horology-solaris-1947.out
src/test/regress/expected/horology.out
src/test/regress/expected/inet.out
src/test/regress/expected/inherit.out
src/test/regress/expected/insert.out
src/test/regress/expected/int2.out
src/test/regress/expected/int4.out
src/test/regress/expected/int8-exp-three-digits.out
src/test/regress/expected/int8.out
src/test/regress/expected/interval.out
src/test/regress/expected/join.out
src/test/regress/expected/limit.out
src/test/regress/expected/lseg.out
src/test/regress/expected/mule_internal.out
src/test/regress/expected/name.out
src/test/regress/expected/numeric.out
src/test/regress/expected/numeric_big.out
src/test/regress/expected/numerology.out
src/test/regress/expected/oid.out
src/test/regress/expected/oidjoins.out
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/path.out
src/test/regress/expected/plpgsql.out
src/test/regress/expected/point.out
src/test/regress/expected/polygon.out
src/test/regress/expected/portals.out
src/test/regress/expected/portals_p2.out
src/test/regress/expected/privileges.out
src/test/regress/expected/random.out
src/test/regress/expected/reltime.out
src/test/regress/expected/rules.out
src/test/regress/expected/sanity_check.out
src/test/regress/expected/select.out
src/test/regress/expected/select_distinct.out
src/test/regress/expected/select_distinct_on.out
src/test/regress/expected/select_having.out
src/test/regress/expected/select_having_1.out
src/test/regress/expected/select_implicit.out
src/test/regress/expected/select_implicit_1.out
src/test/regress/expected/select_into.out
src/test/regress/expected/select_views.out
src/test/regress/expected/select_views_1.out
src/test/regress/expected/sql_ascii.out
src/test/regress/expected/strings.out
src/test/regress/expected/subselect.out
src/test/regress/expected/temp.out
src/test/regress/expected/text.out
src/test/regress/expected/time.out
src/test/regress/expected/timestamp.out
src/test/regress/expected/timestamptz.out
src/test/regress/expected/timetz.out
src/test/regress/expected/tinterval-solaris-1947.out
src/test/regress/expected/tinterval.out
src/test/regress/expected/transactions.out
src/test/regress/expected/triggers.out
src/test/regress/expected/type_sanity.out
src/test/regress/expected/union.out
src/test/regress/expected/varchar.out
src/test/regress/expected/varchar_1.out
src/test/regress/input/constraints.source
src/test/regress/input/copy.source
src/test/regress/input/create_function_1.source
src/test/regress/input/create_function_2.source
src/test/regress/input/misc.source
src/test/regress/output/constraints.source
src/test/regress/output/copy.source
src/test/regress/output/create_function_1.source
src/test/regress/output/create_function_2.source
src/test/regress/output/misc.source
src/test/regress/parallel_schedule
src/test/regress/pg_regress.sh
src/test/regress/regress.c
src/test/regress/regressplans.sh
src/test/regress/resultmap
src/test/regress/serial_schedule
src/test/regress/sql/abstime.sql
src/test/regress/sql/aggregates.sql
src/test/regress/sql/alter_table.sql
src/test/regress/sql/arrays.sql
src/test/regress/sql/bit.sql
src/test/regress/sql/boolean.sql
src/test/regress/sql/box.sql
src/test/regress/sql/btree_index.sql
src/test/regress/sql/case.sql
src/test/regress/sql/char.sql
src/test/regress/sql/circle.sql
src/test/regress/sql/comments.sql
src/test/regress/sql/create_aggregate.sql
src/test/regress/sql/create_index.sql
src/test/regress/sql/create_misc.sql
src/test/regress/sql/create_operator.sql
src/test/regress/sql/create_table.sql
src/test/regress/sql/create_type.sql
src/test/regress/sql/create_view.sql
src/test/regress/sql/date.sql
src/test/regress/sql/domain.sql
src/test/regress/sql/drop.sql
src/test/regress/sql/errors.sql
src/test/regress/sql/euc_cn.sql
src/test/regress/sql/euc_jp.sql
src/test/regress/sql/euc_kr.sql
src/test/regress/sql/euc_tw.sql
src/test/regress/sql/float4.sql
src/test/regress/sql/float8.sql
src/test/regress/sql/foreign_key.sql
src/test/regress/sql/geometry.sql
src/test/regress/sql/hash_index.sql
src/test/regress/sql/horology.sql
src/test/regress/sql/inet.sql
src/test/regress/sql/inherit.sql
src/test/regress/sql/insert.sql
src/test/regress/sql/int2.sql
src/test/regress/sql/int4.sql
src/test/regress/sql/int8.sql
src/test/regress/sql/interval.sql
src/test/regress/sql/join.sql
src/test/regress/sql/limit.sql
src/test/regress/sql/lseg.sql
src/test/regress/sql/mule_internal.sql
src/test/regress/sql/name.sql
src/test/regress/sql/numeric.sql
src/test/regress/sql/numeric_big.sql
src/test/regress/sql/numerology.sql
src/test/regress/sql/oid.sql
src/test/regress/sql/oidjoins.sql
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/path.sql
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/point.sql
src/test/regress/sql/polygon.sql
src/test/regress/sql/portals.sql
src/test/regress/sql/portals_p2.sql
src/test/regress/sql/privileges.sql
src/test/regress/sql/random.sql
src/test/regress/sql/reltime.sql
src/test/regress/sql/rules.sql
src/test/regress/sql/sanity_check.sql
src/test/regress/sql/select.sql
src/test/regress/sql/select_distinct.sql
src/test/regress/sql/select_distinct_on.sql
src/test/regress/sql/select_having.sql
src/test/regress/sql/select_implicit.sql
src/test/regress/sql/select_into.sql
src/test/regress/sql/select_views.sql
src/test/regress/sql/sql_ascii.sql
src/test/regress/sql/strings.sql
src/test/regress/sql/subselect.sql
src/test/regress/sql/temp.sql
src/test/regress/sql/text.sql
src/test/regress/sql/time.sql
src/test/regress/sql/timestamp.sql
src/test/regress/sql/timestamptz.sql
src/test/regress/sql/timetz.sql
src/test/regress/sql/tinterval.sql
src/test/regress/sql/transactions.sql
src/test/regress/sql/triggers.sql
src/test/regress/sql/type_sanity.sql
src/test/regress/sql/union.sql
src/test/regress/sql/varchar.sql
src/tools/RELEASE_CHANGES
src/tools/backend/README
src/tools/backend/backend_dirs.html
src/tools/backend/flow.fig
src/tools/backend/flow.gif
src/tools/backend/index.html
src/tools/ccsym
src/tools/copyright
src/tools/entab/Makefile
src/tools/entab/entab.c
src/tools/entab/entab.man
src/tools/entab/halt.c
src/tools/find_badmacros
src/tools/find_static
src/tools/find_typedef
src/tools/make_ctags
src/tools/make_diff/README
src/tools/make_diff/cporig
src/tools/make_diff/difforig
src/tools/make_diff/rmorig
src/tools/make_etags
src/tools/make_keywords
src/tools/make_mkid
src/tools/pgcvslog
src/tools/pginclude/README
src/tools/pginclude/pgcompinclude
src/tools/pginclude/pgdefine
src/tools/pginclude/pgfixinclude
src/tools/pginclude/pgrminclude
src/tools/pgindent/README
src/tools/pgindent/indent.bsd.patch
src/tools/pgindent/pgcppindent
src/tools/pgindent/pgindent
src/tools/pgindent/pgjindent
src/tutorial/Makefile
src/tutorial/README
src/tutorial/advanced.source
src/tutorial/basics.source
src/tutorial/beard.c
src/tutorial/complex.c
src/tutorial/complex.source
src/tutorial/funcs.c
src/tutorial/funcs.source
src/tutorial/funcs_new.c
src/tutorial/syscat.source
src/utils/Makefile
src/utils/README
src/utils/dllinit.c
src/utils/getopt.c
src/utils/strdup.c
src/win32.mak
Diffstat (limited to 'src/backend/executor')
33 files changed, 0 insertions, 20272 deletions
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile deleted file mode 100644 index b875259bc1a..00000000000 --- a/src/backend/executor/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -#------------------------------------------------------------------------- -# -# Makefile-- -# Makefile for executor -# -# IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/executor/Makefile,v 1.19 2002/05/12 23:43:02 tgl Exp $ -# -#------------------------------------------------------------------------- - -subdir = src/backend/executor -top_builddir = ../../.. -include $(top_builddir)/src/Makefile.global - -OBJS = execAmi.o execJunk.o execMain.o \ - execProcnode.o execQual.o execScan.o execTuples.o \ - execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o nodeHash.o \ - nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \ - nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \ - nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \ - nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o spi.o - -all: SUBSYS.o - -SUBSYS.o: $(OBJS) - $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS) - -depend dep: - $(CC) -MM $(CFLAGS) *.c >depend - -clean: - rm -f SUBSYS.o $(OBJS) - -ifeq (depend,$(wildcard depend)) -include depend -endif diff --git a/src/backend/executor/README b/src/backend/executor/README deleted file mode 100644 index 0a56c3fa6ae..00000000000 --- a/src/backend/executor/README +++ /dev/null @@ -1,99 +0,0 @@ -$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.1 2001/05/15 00:35:50 tgl Exp $ - -The Postgres Executor ---------------------- - -The executor processes a tree of "plan nodes". The plan tree is essentially -a demand-pull pipeline of tuple processing operations. Each node, when -called, will produce the next tuple in its output sequence, or NULL if no -more tuples are available. If the node is not a primitive relation-scanning -node, it will have child node(s) that it calls in turn to obtain input -tuples. - -Refinements on this basic model include: - -* Choice of scan direction (forwards or backwards). Caution: this is not -currently well-supported. It works for primitive scan nodes, but not very -well for joins, aggregates, etc. - -* Rescan command to reset a node and make it generate its output sequence -over again. - -* Parameters that can alter a node's results. After adjusting a parameter, -the rescan command must be applied to that node and all nodes above it. -There is a moderately intelligent scheme to avoid rescanning nodes -unnecessarily (for example, Sort does not rescan its input if no parameters -of the input have changed, since it can just reread its stored sorted data). - -The plan tree concept implements SELECT directly: it is only necessary to -deliver the top-level result tuples to the client, or insert them into -another table in the case of INSERT ... SELECT. (INSERT ... VALUES is -handled similarly, but the plan tree is just a Result node with no source -tables.) For UPDATE, the plan tree selects the tuples that need to be -updated (WHERE condition) and delivers a new calculated tuple value for each -such tuple, plus a "junk" (hidden) tuple CTID identifying the target tuple. -The executor's top level then uses this information to update the correct -tuple. DELETE is similar to UPDATE except that only a CTID need be -delivered by the plan tree. - -XXX a great deal more documentation needs to be written here... - - -EvalPlanQual (READ COMMITTED update checking) ---------------------------------------------- - -For simple SELECTs, the executor need only pay attention to tuples that are -valid according to the snapshot seen by the current transaction (ie, they -were inserted by a previously committed transaction, and not deleted by any -previously committed transaction). However, for UPDATE and DELETE it is not -cool to modify or delete a tuple that's been modified by an open or -concurrently-committed transaction. If we are running in SERIALIZABLE -isolation level then we just raise an error when this condition is seen to -occur. In READ COMMITTED isolation level, we must work a lot harder. - -The basic idea in READ COMMITTED mode is to take the modified tuple -committed by the concurrent transaction (after waiting for it to commit, -if need be) and re-evaluate the query qualifications to see if it would -still meet the quals. If so, we regenerate the updated tuple (if we are -doing an UPDATE) from the modified tuple, and finally update/delete the -modified tuple. SELECT FOR UPDATE behaves similarly, except that its action -is just to mark the modified tuple for update by the current transaction. - -To implement this checking, we actually re-run the entire query from scratch -for each modified tuple, but with the scan node that sourced the original -tuple set to return only the modified tuple, not the original tuple or any -of the rest of the relation. If this query returns a tuple, then the -modified tuple passes the quals (and the query output is the suitably -modified update tuple, if we're doing UPDATE). If no tuple is returned, -then the modified tuple fails the quals, so we ignore it and continue the -original query. (This is reasonably efficient for simple queries, but may -be horribly slow for joins. A better design would be nice; one thought for -future investigation is to treat the tuple substitution like a parameter, -so that we can avoid rescanning unrelated nodes.) - -Note a fundamental bogosity of this approach: if the relation containing -the original tuple is being used in a self-join, the other instance(s) of -the relation will be treated as still containing the original tuple, whereas -logical consistency would demand that the modified tuple appear in them too. -But we'd have to actually substitute the modified tuple for the original, -while still returning all the rest of the relation, to ensure consistent -answers. Implementing this correctly is a task for future work. - -In UPDATE/DELETE, only the target relation needs to be handled this way, -so only one special recheck query needs to execute at a time. In SELECT FOR -UPDATE, there may be multiple relations flagged FOR UPDATE, so it's possible -that while we are executing a recheck query for one modified tuple, we will -hit another modified tuple in another relation. In this case we "stack up" -recheck queries: a sub-recheck query is spawned in which both the first and -second modified tuples will be returned as the only components of their -relations. (In event of success, all these modified tuples will be marked -for update.) Again, this isn't necessarily quite the right thing ... but in -simple cases it works. Potentially, recheck queries could get nested to the -depth of the number of FOR UPDATE relations in the query. - -It should be noted also that UPDATE/DELETE expect at most one tuple to -result from the modified query, whereas in the FOR UPDATE case it's possible -for multiple tuples to result (since we could be dealing with a join in -which multiple tuples join to the modified tuple). We want FOR UPDATE to -mark all relevant tuples, so we pass all tuples output by all the stacked -recheck queries back to the executor toplevel for marking. diff --git a/src/backend/executor/_deadcode/nodeTee.c b/src/backend/executor/_deadcode/nodeTee.c deleted file mode 100644 index e02f8cb59f5..00000000000 --- a/src/backend/executor/_deadcode/nodeTee.c +++ /dev/null @@ -1,499 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeTee.c - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions 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 - * - * $Id: nodeTee.c,v 1.12 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include <sys/types.h> -#include <sys/file.h> -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/catalog.h" -#include "catalog/heap.h" -#include "executor/executor.h" -#include "executor/nodeTee.h" -#include "optimizer/internal.h" -#include "storage/bufmgr.h" -#include "storage/smgr.h" -#include "tcop/pquery.h" -#include "utils/relcache.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; - estate->es_snapshot = currentEstate->es_snapshot; - - /* - * 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); - - /* - * 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_with_catalog(teeState->tee_bufferRelname, - tupType, RELKIND_RELATION, false)); - } - else - { - sprintf(teeState->tee_bufferRelname, - "ttemp_%d", /* 'ttemp' for 'tee' temporary */ - newoid()); - bufferRel = heap_open( - heap_create_with_catalog(teeState->tee_bufferRelname, - tupType, RELKIND_RELATION, false)); - } - - 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 -* ---------------------------------------------------------------- -*/ -static void -initTeeScanDescs(Tee * node) -{ - TeeState *teeState; - Relation bufferRel; - ScanDirection dir; - Snapshot snapshot; - 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 */ - snapshot = ((Plan *) node)->state->es_snapshot; - - if (teeState->tee_leftScanDesc == NULL) - { - teeState->tee_leftScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - snapshot, - 0, /* num scan keys */ - NULL /* scan keys */ - ); - } - if (teeState->tee_rightScanDesc == NULL) - { - teeState->tee_rightScanDesc = heap_beginscan(bufferRel, - ScanDirectionIsBackward(dir), - snapshot, - 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; - - 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(ERROR, "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)) - { - /* - * heap_insert changes something... - */ - if (slot->ttc_buffer != InvalidBuffer) - heapTuple = heap_copytuple(slot->val); - else - heapTuple = slot->val; - - /* insert into temporary relation */ - heap_insert(bufferRel, heapTuple); - - if (slot->ttc_buffer != InvalidBuffer) - heap_freetuple(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)); - } - - /* - * 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)); - - /* - * 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 (scanDesc->rs_cbuf != InvalidBuffer) - IncrBufferRefCount(scanDesc->rs_cbuf); - - slot = teeState->cstate.cs_ResultTupleSlot; - slot->ttc_tupleDescriptor = RelationGetDescr(bufferRel); - - result = ExecStoreTuple(heapTuple, /* tuple to store */ - slot, /* slot to store in */ - scanDesc->rs_cbuf, /* 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; -} - -/* --------------------------------------------------------------------- - * 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_drop(bufferRel); - teeState->tee_bufferRel = NULL; - if (teeState->tee_mcxt) - { - orig = CurrentMemoryContext; - MemoryContextSwitchTo(teeState->tee_mcxt); - } - else - orig = 0; - - 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/execAmi.c b/src/backend/executor/execAmi.c deleted file mode 100644 index 7ceb7cd2c6f..00000000000 --- a/src/backend/executor/execAmi.c +++ /dev/null @@ -1,258 +0,0 @@ -/*------------------------------------------------------------------------- - * - * execAmi.c - * miscellaneous executor access method routines - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: execAmi.c,v 1.64 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/genam.h" -#include "access/heapam.h" -#include "catalog/heap.h" -#include "executor/execdebug.h" -#include "executor/instrument.h" -#include "executor/nodeAgg.h" -#include "executor/nodeAppend.h" -#include "executor/nodeGroup.h" -#include "executor/nodeGroup.h" -#include "executor/nodeHash.h" -#include "executor/nodeHashjoin.h" -#include "executor/nodeIndexscan.h" -#include "executor/nodeTidscan.h" -#include "executor/nodeLimit.h" -#include "executor/nodeMaterial.h" -#include "executor/nodeMergejoin.h" -#include "executor/nodeNestloop.h" -#include "executor/nodeResult.h" -#include "executor/nodeSeqscan.h" -#include "executor/nodeSetOp.h" -#include "executor/nodeSort.h" -#include "executor/nodeSubplan.h" -#include "executor/nodeSubqueryscan.h" -#include "executor/nodeFunctionscan.h" -#include "executor/nodeUnique.h" - - -/* ---------------------------------------------------------------- - * ExecReScan - * - * XXX this should be extended to cope with all the node types.. - * - * takes the new expression context as an argument, so that - * index scans needn't have their scan keys updated separately - * - marcel 09/20/94 - * ---------------------------------------------------------------- - */ -void -ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent) -{ - if (node->instrument) - InstrEndLoop(node->instrument); - - if (node->chgParam != NULL) /* Wow! */ - { - List *lst; - - foreach(lst, node->initPlan) - { - Plan *splan = ((SubPlan *) lfirst(lst))->plan; - - if (splan->extParam != NULL) /* don't care about child - * locParam */ - SetChangedParamList(splan, node->chgParam); - if (splan->chgParam != NULL) - ExecReScanSetParamPlan((SubPlan *) lfirst(lst), node); - } - foreach(lst, node->subPlan) - { - Plan *splan = ((SubPlan *) lfirst(lst))->plan; - - if (splan->extParam != NULL) - SetChangedParamList(splan, node->chgParam); - } - /* Well. Now set chgParam for left/right trees. */ - if (node->lefttree != NULL) - SetChangedParamList(node->lefttree, node->chgParam); - if (node->righttree != NULL) - SetChangedParamList(node->righttree, node->chgParam); - } - - switch (nodeTag(node)) - { - case T_SeqScan: - ExecSeqReScan((SeqScan *) node, exprCtxt, parent); - break; - - case T_IndexScan: - ExecIndexReScan((IndexScan *) node, exprCtxt, parent); - break; - - case T_TidScan: - ExecTidReScan((TidScan *) node, exprCtxt, parent); - break; - - case T_SubqueryScan: - ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent); - break; - - case T_FunctionScan: - ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent); - break; - - case T_Material: - ExecMaterialReScan((Material *) node, exprCtxt, parent); - break; - - case T_NestLoop: - ExecReScanNestLoop((NestLoop *) node, exprCtxt, parent); - break; - - case T_HashJoin: - ExecReScanHashJoin((HashJoin *) node, exprCtxt, parent); - break; - - case T_Hash: - ExecReScanHash((Hash *) node, exprCtxt, parent); - break; - - case T_Agg: - ExecReScanAgg((Agg *) node, exprCtxt, parent); - break; - - case T_Group: - ExecReScanGroup((Group *) node, exprCtxt, parent); - break; - - case T_Result: - ExecReScanResult((Result *) node, exprCtxt, parent); - break; - - case T_Unique: - ExecReScanUnique((Unique *) node, exprCtxt, parent); - break; - - case T_SetOp: - ExecReScanSetOp((SetOp *) node, exprCtxt, parent); - break; - - case T_Limit: - ExecReScanLimit((Limit *) node, exprCtxt, parent); - break; - - case T_Sort: - ExecReScanSort((Sort *) node, exprCtxt, parent); - break; - - case T_MergeJoin: - ExecReScanMergeJoin((MergeJoin *) node, exprCtxt, parent); - break; - - case T_Append: - ExecReScanAppend((Append *) node, exprCtxt, parent); - break; - - default: - elog(ERROR, "ExecReScan: node type %d not supported", - nodeTag(node)); - return; - } - - if (node->chgParam != NULL) - { - freeList(node->chgParam); - node->chgParam = NULL; - } -} - -/* ---------------------------------------------------------------- - * ExecMarkPos - * - * Marks the current scan position. - * - * XXX Needs to be extended to include all the node types, - * or at least all the ones that can be directly below a mergejoin. - * ---------------------------------------------------------------- - */ -void -ExecMarkPos(Plan *node) -{ - switch (nodeTag(node)) - { - case T_SeqScan: - ExecSeqMarkPos((SeqScan *) node); - break; - - case T_IndexScan: - ExecIndexMarkPos((IndexScan *) node); - break; - - case T_FunctionScan: - ExecFunctionMarkPos((FunctionScan *) node); - break; - - case T_Material: - ExecMaterialMarkPos((Material *) node); - break; - - case T_Sort: - ExecSortMarkPos((Sort *) node); - break; - - case T_TidScan: - ExecTidMarkPos((TidScan *) node); - break; - - default: - /* don't make hard error unless caller asks to restore... */ - elog(LOG, "ExecMarkPos: node type %d not supported", - nodeTag(node)); - break; - } -} - -/* ---------------------------------------------------------------- - * ExecRestrPos - * - * restores the scan position previously saved with ExecMarkPos() - * - * XXX Needs to be extended to include all the node types, - * or at least all the ones that can be directly below a mergejoin. - * ---------------------------------------------------------------- - */ -void -ExecRestrPos(Plan *node) -{ - switch (nodeTag(node)) - { - case T_SeqScan: - ExecSeqRestrPos((SeqScan *) node); - break; - - case T_IndexScan: - ExecIndexRestrPos((IndexScan *) node); - break; - - case T_FunctionScan: - ExecFunctionRestrPos((FunctionScan *) node); - break; - - case T_Material: - ExecMaterialRestrPos((Material *) node); - break; - - case T_Sort: - ExecSortRestrPos((Sort *) node); - break; - - default: - elog(ERROR, "ExecRestrPos: node type %d not supported", - nodeTag(node)); - break; - } -} diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c deleted file mode 100644 index 94c375012e1..00000000000 --- a/src/backend/executor/execJunk.c +++ /dev/null @@ -1,431 +0,0 @@ -/*------------------------------------------------------------------------- - * - * junk.c - * Junk attribute support stuff.... - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execJunk.c,v 1.30 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "executor/executor.h" -#include "nodes/makefuncs.h" - -/*------------------------------------------------------------------------- - * 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 true 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. - * - * The initial targetlist and associated tuple descriptor are passed in. - * An optional resultSlot can be passed as well. - *------------------------------------------------------------------------- - */ -JunkFilter * -ExecInitJunkFilter(List *targetList, TupleDesc tupType, - TupleTableSlot *slot) -{ - MemoryContext oldContext; - MemoryContext junkContext; - JunkFilter *junkfilter; - List *cleanTargetList; - int len, - cleanLength; - TupleDesc cleanTupType; - List *t; - TargetEntry *tle; - Resdom *resdom, - *cleanResdom; - bool resjunk; - AttrNumber cleanResno; - AttrNumber *cleanMap; - Node *expr; - - /* - * Make a memory context that will hold the JunkFilter as well as all - * the subsidiary structures we are about to create. We use smaller- - * than-default sizing parameters since we don't expect a very large - * volume of stuff here. - */ - junkContext = AllocSetContextCreate(CurrentMemoryContext, - "JunkFilterContext", - 1024, - 1024, - ALLOCSET_DEFAULT_MAXSIZE); - oldContext = MemoryContextSwitchTo(junkContext); - - /* - * First find the "clean" target list, i.e. all the entries in the - * original target list which have a false '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) - { - /* - * 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 = makeTargetEntry(cleanResdom, 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 = makeList1(cleanFjoin); - - 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) makeTargetEntry(cleanResdom, (Node *) 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) makeTargetEntry(cleanResdom, (Node *) expr); - cleanFjList = lappend(cleanFjList, tle); - } - lappend(cleanTargetList, cleanFjList); -#endif - } - } - - /* - * Now calculate the tuple type for the cleaned tuple (we were already - * given the type for the original targetlist). - */ - cleanTupType = ExecTypeFromTL(cleanTargetList); - - len = ExecTargetListLength(targetList); - cleanLength = ExecTargetListLength(cleanTargetList); - - /* - * Now calculate the "map" between the original tuple's 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) - { - cleanMap = (AttrNumber *) palloc(cleanLength * sizeof(AttrNumber)); - cleanResno = 1; - foreach(t, targetList) - { - TargetEntry *tle = lfirst(t); - - if (tle->resdom != NULL) - { - resdom = tle->resdom; - expr = tle->expr; - resjunk = resdom->resjunk; - if (!resjunk) - { - 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 struct. - */ - 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; - junkfilter->jf_junkContext = junkContext; - junkfilter->jf_resultSlot = slot; - - if (slot) - ExecSetSlotDescriptor(slot, cleanTupType, false); - - MemoryContextSwitchTo(oldContext); - - return junkfilter; -} - -/*------------------------------------------------------------------------- - * ExecFreeJunkFilter - * - * Release the data structures created by ExecInitJunkFilter. - *------------------------------------------------------------------------- - */ -void -ExecFreeJunkFilter(JunkFilter *junkfilter) -{ - /* - * Since the junkfilter is inside its own context, we just have to - * delete the context and we're set. - */ - MemoryContextDelete(junkfilter->jf_junkContext); -} - -/*------------------------------------------------------------------------- - * ExecGetJunkAttribute - * - * Given a tuple (slot), the junk filter and a junk attribute's name, - * extract & return the value and isNull flag of this attribute. - * - * It returns false iff no junk attribute with such name was found. - *------------------------------------------------------------------------- - */ -bool -ExecGetJunkAttribute(JunkFilter *junkfilter, - TupleTableSlot *slot, - char *attrName, - Datum *value, - bool *isNull) -{ - List *targetList; - List *t; - Resdom *resdom; - AttrNumber resno; - char *resname; - bool 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 && (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 = junkfilter->jf_tupType; - - *value = heap_getattr(tuple, resno, tupType, isNull); - - return true; -} - -/*------------------------------------------------------------------------- - * ExecRemoveJunk - * - * Construct and return a tuple with all the junk attributes removed. - * - * Note: for historical reasons, this does not store the constructed - * tuple into the junkfilter's resultSlot. The caller should do that - * if it wants to. - *------------------------------------------------------------------------- - */ -HeapTuple -ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot) -{ - HeapTuple tuple; - HeapTuple cleanTuple; - AttrNumber *cleanMap; - TupleDesc cleanTupType; - TupleDesc tupType; - int cleanLength; - bool isNull; - int i; - 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 = junkfilter->jf_tupType; - cleanTupType = 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) - { - values = (Datum *) palloc(cleanLength * sizeof(Datum)); - nulls = (char *) palloc(cleanLength * sizeof(char)); - } - else - { - values = values_array; - nulls = nulls_array; - } - - /* - * Exctract one by one all the values of the "clean" tuple. - */ - for (i = 0; i < cleanLength; i++) - { - values[i] = heap_getattr(tuple, cleanMap[i], tupType, &isNull); - - if (isNull) - nulls[i] = 'n'; - else - nulls[i] = ' '; - } - - /* - * Now form the new tuple. - */ - cleanTuple = heap_formtuple(cleanTupType, - values, - nulls); - - /* - * We are done. Free any space allocated for 'values' and 'nulls' and - * return the new tuple. - */ - if (cleanLength > 64) - { - pfree(values); - pfree(nulls); - } - - return cleanTuple; -} diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c deleted file mode 100644 index 27b187f2a5e..00000000000 --- a/src/backend/executor/execMain.c +++ /dev/null @@ -1,1941 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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 direction and count arguments that specify whether - * the plan is to be executed forwards, backwards, and for how many tuples. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.165 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "commands/tablecmds.h" -#include "commands/trigger.h" -#include "executor/execdebug.h" -#include "executor/execdefs.h" -#include "miscadmin.h" -#include "optimizer/var.h" -#include "parser/parsetree.h" -#include "utils/acl.h" -#include "utils/lsyscache.h" - - -/* decls for local routines only used within this module */ -static TupleDesc InitPlan(CmdType operation, - Query *parseTree, - Plan *plan, - EState *estate); -static void initResultRelInfo(ResultRelInfo *resultRelInfo, - Index resultRelationIndex, - List *rangeTable, - CmdType operation); -static void EndPlan(Plan *plan, EState *estate); -static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan, - CmdType operation, - long numberTuples, - ScanDirection direction, - DestReceiver *destfunc); -static void ExecRetrieve(TupleTableSlot *slot, - DestReceiver *destfunc, - EState *estate); -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); -static TupleTableSlot *EvalPlanQualNext(EState *estate); -static void EndEvalPlanQual(EState *estate); -static void ExecCheckQueryPerms(CmdType operation, Query *parseTree, - Plan *plan); -static void ExecCheckPlanPerms(Plan *plan, List *rangeTable, - CmdType operation); -static void ExecCheckRTPerms(List *rangeTable, CmdType operation); -static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation); - -/* end of local decls */ - - -/* ---------------------------------------------------------------- - * ExecutorStart - * - * This routine must be called at the beginning of any execution of any - * query plan - * - * returns a TupleDesc which describes the attributes of the tuples to - * be returned by the query. (Same value is saved in queryDesc) - * - * NB: the CurrentMemoryContext when this is called must be the context - * to be used as the per-query context for the query plan. ExecutorRun() - * and ExecutorEnd() must be called in this same memory context. - * ---------------------------------------------------------------- - */ -TupleDesc -ExecutorStart(QueryDesc *queryDesc, EState *estate) -{ - TupleDesc result; - - /* sanity checks */ - Assert(queryDesc != NULL); - - if (queryDesc->plantree->nParamExec > 0) - { - estate->es_param_exec_vals = (ParamExecData *) - palloc(queryDesc->plantree->nParamExec * sizeof(ParamExecData)); - MemSet(estate->es_param_exec_vals, 0, - queryDesc->plantree->nParamExec * sizeof(ParamExecData)); - } - - /* - * Make our own private copy of the current query snapshot data. - * - * This "freezes" our idea of which tuples are good and which are not - * for the life of this query, even if it outlives the current command - * and current snapshot. - */ - estate->es_snapshot = CopyQuerySnapshot(); - - /* - * Initialize the plan - */ - result = InitPlan(queryDesc->operation, - queryDesc->parsetree, - queryDesc->plantree, - estate); - - queryDesc->tupDesc = result; - - 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. - * - * If direction is NoMovementScanDirection then nothing is done - * except to start up/shut down the destination. Otherwise, - * we retrieve up to 'count' tuples in the specified direction. - * - * Note: count = 0 is interpreted as no portal limit, e.g. run to - * completion. - * - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecutorRun(QueryDesc *queryDesc, EState *estate, - ScanDirection direction, long count) -{ - CmdType operation; - Plan *plan; - CommandDest dest; - DestReceiver *destfunc; - TupleTableSlot *result; - - /* - * sanity checks - */ - Assert(queryDesc != NULL); - - /* - * extract information from the query descriptor and the query - * feature. - */ - operation = queryDesc->operation; - plan = queryDesc->plantree; - dest = queryDesc->dest; - - /* - * startup tuple receiver - */ - estate->es_processed = 0; - estate->es_lastoid = InvalidOid; - - destfunc = DestToFunction(dest); - (*destfunc->setup) (destfunc, (int) operation, - queryDesc->portalName, queryDesc->tupDesc); - - /* - * run plan - */ - if (direction == NoMovementScanDirection) - result = NULL; - else - result = ExecutePlan(estate, - plan, - operation, - count, - direction, - destfunc); - - /* - * shutdown receiver - */ - (*destfunc->cleanup) (destfunc); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecutorEnd - * - * This routine must be called at the end of execution of any - * query plan - * ---------------------------------------------------------------- - */ -void -ExecutorEnd(QueryDesc *queryDesc, EState *estate) -{ - /* sanity checks */ - Assert(queryDesc != NULL); - - EndPlan(queryDesc->plantree, estate); - - if (estate->es_snapshot != NULL) - { - if (estate->es_snapshot->xcnt > 0) - pfree(estate->es_snapshot->xip); - pfree(estate->es_snapshot); - estate->es_snapshot = NULL; - } - - if (estate->es_param_exec_vals != NULL) - { - pfree(estate->es_param_exec_vals); - estate->es_param_exec_vals = NULL; - } -} - - -/* - * ExecCheckQueryPerms - * Check access permissions for all relations referenced in a query. - */ -static void -ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan) -{ - /* - * Check RTEs in the query's primary rangetable. - */ - ExecCheckRTPerms(parseTree->rtable, operation); - - /* - * Search for subplans and APPEND nodes to check their rangetables. - */ - ExecCheckPlanPerms(plan, parseTree->rtable, operation); -} - -/* - * ExecCheckPlanPerms - * Recursively scan the plan tree to check access permissions in - * subplans. - */ -static void -ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation) -{ - List *subp; - - if (plan == NULL) - return; - - /* Check subplans, which we assume are plain SELECT queries */ - - foreach(subp, plan->initPlan) - { - SubPlan *subplan = (SubPlan *) lfirst(subp); - - ExecCheckRTPerms(subplan->rtable, CMD_SELECT); - ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT); - } - foreach(subp, plan->subPlan) - { - SubPlan *subplan = (SubPlan *) lfirst(subp); - - ExecCheckRTPerms(subplan->rtable, CMD_SELECT); - ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT); - } - - /* Check lower plan nodes */ - - ExecCheckPlanPerms(plan->lefttree, rangeTable, operation); - ExecCheckPlanPerms(plan->righttree, rangeTable, operation); - - /* Do node-type-specific checks */ - - switch (nodeTag(plan)) - { - case T_SubqueryScan: - { - SubqueryScan *scan = (SubqueryScan *) plan; - RangeTblEntry *rte; - - /* Recursively check the subquery */ - rte = rt_fetch(scan->scan.scanrelid, rangeTable); - Assert(rte->rtekind == RTE_SUBQUERY); - ExecCheckQueryPerms(operation, rte->subquery, scan->subplan); - break; - } - case T_Append: - { - Append *app = (Append *) plan; - List *appendplans; - - foreach(appendplans, app->appendplans) - { - ExecCheckPlanPerms((Plan *) lfirst(appendplans), - rangeTable, - operation); - } - break; - } - - default: - break; - } -} - -/* - * ExecCheckRTPerms - * Check access permissions for all relations listed in a range table. - */ -static void -ExecCheckRTPerms(List *rangeTable, CmdType operation) -{ - List *lp; - - foreach(lp, rangeTable) - { - RangeTblEntry *rte = lfirst(lp); - - ExecCheckRTEPerms(rte, operation); - } -} - -/* - * ExecCheckRTEPerms - * Check access permissions for a single RTE. - */ -static void -ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation) -{ - Oid relOid; - Oid userid; - AclResult aclcheck_result; - - /* - * Only plain-relation RTEs need to be checked here. Subquery RTEs - * will be checked when ExecCheckPlanPerms finds the SubqueryScan node, - * and function RTEs are checked by init_fcache when the function is - * prepared for execution. Join and special RTEs need no checks. - */ - if (rte->rtekind != RTE_RELATION) - return; - - relOid = rte->relid; - - /* - * userid to check as: current user unless we have a setuid - * indication. - * - * Note: GetUserId() is presently fast enough that there's no harm in - * calling it separately for each RTE. If that stops being true, we - * could call it once in ExecCheckQueryPerms and pass the userid down - * from there. But for now, no need for the extra clutter. - */ - userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); - -#define CHECK(MODE) pg_class_aclcheck(relOid, userid, MODE) - - if (rte->checkForRead) - { - aclcheck_result = CHECK(ACL_SELECT); - if (aclcheck_result != ACLCHECK_OK) - aclcheck_error(aclcheck_result, get_rel_name(relOid)); - } - - if (rte->checkForWrite) - { - /* - * Note: write access in a SELECT context means SELECT FOR UPDATE. - * Right now we don't distinguish that from true update as far as - * permissions checks are concerned. - */ - switch (operation) - { - case CMD_INSERT: - aclcheck_result = CHECK(ACL_INSERT); - break; - case CMD_SELECT: - case CMD_UPDATE: - aclcheck_result = CHECK(ACL_UPDATE); - break; - case CMD_DELETE: - aclcheck_result = CHECK(ACL_DELETE); - break; - default: - elog(ERROR, "ExecCheckRTEPerms: bogus operation %d", - operation); - aclcheck_result = ACLCHECK_OK; /* keep compiler quiet */ - break; - } - if (aclcheck_result != ACLCHECK_OK) - aclcheck_error(aclcheck_result, get_rel_name(relOid)); - } -} - - -/* =============================================================== - * =============================================================== - static routines follow - * =============================================================== - * =============================================================== - */ - -typedef struct execRowMark -{ - Relation relation; - Index rti; - char resname[32]; -} execRowMark; - -typedef struct evalPlanQual -{ - Plan *plan; - Index rti; - EState estate; - struct evalPlanQual *free; -} evalPlanQual; - -/* ---------------------------------------------------------------- - * 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; - Relation intoRelationDesc; - TupleDesc tupType; - - /* - * Do permissions checks. - */ - ExecCheckQueryPerms(operation, parseTree, plan); - - /* - * get information from query descriptor - */ - rangeTable = parseTree->rtable; - - /* - * initialize the node's execution state - */ - estate->es_range_table = rangeTable; - - /* - * if there is a result relation, initialize result relation stuff - */ - if (parseTree->resultRelation != 0 && operation != CMD_SELECT) - { - List *resultRelations = parseTree->resultRelations; - int numResultRelations; - ResultRelInfo *resultRelInfos; - - if (resultRelations != NIL) - { - /* - * Multiple result relations (due to inheritance) - * parseTree->resultRelations identifies them all - */ - ResultRelInfo *resultRelInfo; - - numResultRelations = length(resultRelations); - resultRelInfos = (ResultRelInfo *) - palloc(numResultRelations * sizeof(ResultRelInfo)); - resultRelInfo = resultRelInfos; - while (resultRelations != NIL) - { - initResultRelInfo(resultRelInfo, - lfirsti(resultRelations), - rangeTable, - operation); - resultRelInfo++; - resultRelations = lnext(resultRelations); - } - } - else - { - /* - * Single result relation identified by - * parseTree->resultRelation - */ - numResultRelations = 1; - resultRelInfos = (ResultRelInfo *) palloc(sizeof(ResultRelInfo)); - initResultRelInfo(resultRelInfos, - parseTree->resultRelation, - rangeTable, - operation); - } - - estate->es_result_relations = resultRelInfos; - estate->es_num_result_relations = numResultRelations; - /* Initialize to first or only result rel */ - estate->es_result_relation_info = resultRelInfos; - } - else - { - /* - * if no result relation, then set state appropriately - */ - estate->es_result_relations = NULL; - estate->es_num_result_relations = 0; - estate->es_result_relation_info = NULL; - } - - /* - * Have to lock relations selected for update - */ - estate->es_rowMark = NIL; - if (parseTree->rowMarks != NIL) - { - List *l; - - foreach(l, parseTree->rowMarks) - { - Index rti = lfirsti(l); - Oid relid = getrelid(rti, rangeTable); - Relation relation; - execRowMark *erm; - - relation = heap_open(relid, RowShareLock); - erm = (execRowMark *) palloc(sizeof(execRowMark)); - erm->relation = relation; - erm->rti = rti; - sprintf(erm->resname, "ctid%u", rti); - estate->es_rowMark = lappend(estate->es_rowMark, erm); - } - } - - /* - * initialize the executor "tuple" table. We need slots for all the - * plan nodes, plus possibly output slots for the junkfilter(s). At - * this point we aren't sure if we need junkfilters, so just add slots - * for them unconditionally. - */ - { - int nSlots = ExecCountSlotsNode(plan); - - if (parseTree->resultRelations != NIL) - nSlots += length(parseTree->resultRelations); - else - nSlots += 1; - estate->es_tupleTable = ExecCreateTupleTable(nSlots); - } - - /* mark EvalPlanQual not active */ - estate->es_origPlan = plan; - estate->es_evalPlanQual = NULL; - estate->es_evTuple = NULL; - estate->es_evTupleNull = NULL; - estate->es_useEvalPlan = false; - - /* - * 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 */ - - /* - * Initialize the junk filter if needed. SELECT and INSERT queries - * need a filter if there are any junk attrs in the tlist. UPDATE and - * DELETE always need one, since there's always a junk 'ctid' - * attribute present --- no need to look first. - */ - { - bool junk_filter_needed = false; - List *tlist; - - switch (operation) - { - case CMD_SELECT: - case CMD_INSERT: - foreach(tlist, plan->targetlist) - { - TargetEntry *tle = (TargetEntry *) lfirst(tlist); - - if (tle->resdom->resjunk) - { - junk_filter_needed = true; - break; - } - } - break; - case CMD_UPDATE: - case CMD_DELETE: - junk_filter_needed = true; - break; - default: - break; - } - - if (junk_filter_needed) - { - /* - * If there are multiple result relations, each one needs its - * own junk filter. Note this is only possible for - * UPDATE/DELETE, so we can't be fooled by some needing a - * filter and some not. - */ - if (parseTree->resultRelations != NIL) - { - List *subplans; - ResultRelInfo *resultRelInfo; - - /* Top plan had better be an Append here. */ - Assert(IsA(plan, Append)); - Assert(((Append *) plan)->isTarget); - subplans = ((Append *) plan)->appendplans; - Assert(length(subplans) == estate->es_num_result_relations); - resultRelInfo = estate->es_result_relations; - while (subplans != NIL) - { - Plan *subplan = (Plan *) lfirst(subplans); - JunkFilter *j; - - j = ExecInitJunkFilter(subplan->targetlist, - ExecGetTupType(subplan), - ExecAllocTableSlot(estate->es_tupleTable)); - resultRelInfo->ri_junkFilter = j; - resultRelInfo++; - subplans = lnext(subplans); - } - - /* - * Set active junkfilter too; at this point ExecInitAppend - * has already selected an active result relation... - */ - estate->es_junkFilter = - estate->es_result_relation_info->ri_junkFilter; - } - else - { - /* Normal case with just one JunkFilter */ - JunkFilter *j; - - j = ExecInitJunkFilter(plan->targetlist, - tupType, - ExecAllocTableSlot(estate->es_tupleTable)); - estate->es_junkFilter = j; - if (estate->es_result_relation_info) - estate->es_result_relation_info->ri_junkFilter = j; - - /* For SELECT, want to return the cleaned tuple type */ - if (operation == CMD_SELECT) - tupType = j->jf_cleanTupType; - } - } - else - estate->es_junkFilter = NULL; - } - - /* - * initialize the "into" relation - */ - intoRelationDesc = (Relation) NULL; - - if (operation == CMD_SELECT) - { - if (!parseTree->isPortal) - { - /* - * a select into table --- need to create the "into" table - */ - if (parseTree->into != NULL) - { - char *intoName; - Oid namespaceId; - Oid intoRelationId; - TupleDesc tupdesc; - - /* - * find namespace to create in, check permissions - */ - intoName = parseTree->into->relname; - namespaceId = RangeVarGetCreationNamespace(parseTree->into); - - if (!isTempNamespace(namespaceId)) - { - AclResult aclresult; - - aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), - ACL_CREATE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, - get_namespace_name(namespaceId)); - } - - /* - * have to copy tupType to get rid of constraints - */ - tupdesc = CreateTupleDescCopy(tupType); - - intoRelationId = - heap_create_with_catalog(intoName, - namespaceId, - tupdesc, - RELKIND_RELATION, - false, - true, - allowSystemTableMods); - - FreeTupleDesc(tupdesc); - - /* - * Advance command counter so that the newly-created - * relation's catalog tuples will be visible to heap_open. - */ - CommandCounterIncrement(); - - /* - * If necessary, create a TOAST table for the into - * relation. Note that AlterTableCreateToastTable ends - * with CommandCounterIncrement(), so that the TOAST table - * will be visible for insertion. - */ - AlterTableCreateToastTable(intoRelationId, true); - - intoRelationDesc = heap_open(intoRelationId, - AccessExclusiveLock); - } - } - } - - estate->es_into_relation_descriptor = intoRelationDesc; - - return tupType; -} - -/* - * Initialize ResultRelInfo data for one result relation - */ -static void -initResultRelInfo(ResultRelInfo *resultRelInfo, - Index resultRelationIndex, - List *rangeTable, - CmdType operation) -{ - Oid resultRelationOid; - Relation resultRelationDesc; - - resultRelationOid = getrelid(resultRelationIndex, rangeTable); - resultRelationDesc = heap_open(resultRelationOid, RowExclusiveLock); - - switch (resultRelationDesc->rd_rel->relkind) - { - case RELKIND_SEQUENCE: - elog(ERROR, "You can't change sequence relation %s", - RelationGetRelationName(resultRelationDesc)); - break; - case RELKIND_TOASTVALUE: - elog(ERROR, "You can't change toast relation %s", - RelationGetRelationName(resultRelationDesc)); - break; - case RELKIND_VIEW: - elog(ERROR, "You can't change view relation %s", - RelationGetRelationName(resultRelationDesc)); - break; - } - - MemSet(resultRelInfo, 0, sizeof(ResultRelInfo)); - resultRelInfo->type = T_ResultRelInfo; - resultRelInfo->ri_RangeTableIndex = resultRelationIndex; - resultRelInfo->ri_RelationDesc = resultRelationDesc; - resultRelInfo->ri_NumIndices = 0; - resultRelInfo->ri_IndexRelationDescs = NULL; - resultRelInfo->ri_IndexRelationInfo = NULL; - resultRelInfo->ri_TrigDesc = resultRelationDesc->trigdesc; - resultRelInfo->ri_TrigFunctions = NULL; - resultRelInfo->ri_ConstraintExprs = NULL; - resultRelInfo->ri_junkFilter = NULL; - - /* - * If there are indices on the result relation, open them and save - * descriptors in the result relation info, so that we can add new - * index entries for the tuples we add/update. We need not do this - * for a DELETE, however, since deletion doesn't affect indexes. - */ - if (resultRelationDesc->rd_rel->relhasindex && - operation != CMD_DELETE) - ExecOpenIndices(resultRelInfo); -} - -/* ---------------------------------------------------------------- - * EndPlan - * - * Cleans up the query plan -- closes files and free up storages - * ---------------------------------------------------------------- - */ -static void -EndPlan(Plan *plan, EState *estate) -{ - ResultRelInfo *resultRelInfo; - int i; - List *l; - - /* - * shut down any PlanQual processing we were doing - */ - if (estate->es_evalPlanQual != NULL) - EndEvalPlanQual(estate); - - /* - * shut down the node-type-specific query processing - */ - ExecEndNode(plan, NULL); - - /* - * destroy the executor "tuple" table. - */ - ExecDropTupleTable(estate->es_tupleTable, true); - estate->es_tupleTable = NULL; - - /* - * close the result relation(s) if any, but hold locks until xact - * commit. Also clean up junkfilters if present. - */ - resultRelInfo = estate->es_result_relations; - for (i = estate->es_num_result_relations; i > 0; i--) - { - /* Close indices and then the relation itself */ - ExecCloseIndices(resultRelInfo); - heap_close(resultRelInfo->ri_RelationDesc, NoLock); - /* Delete the junkfilter if any */ - if (resultRelInfo->ri_junkFilter != NULL) - ExecFreeJunkFilter(resultRelInfo->ri_junkFilter); - resultRelInfo++; - } - - /* - * close the "into" relation if necessary, again keeping lock - */ - if (estate->es_into_relation_descriptor != NULL) - heap_close(estate->es_into_relation_descriptor, NoLock); - - /* - * There might be a junkfilter without a result relation. - */ - if (estate->es_num_result_relations == 0 && - estate->es_junkFilter != NULL) - { - ExecFreeJunkFilter(estate->es_junkFilter); - estate->es_junkFilter = NULL; - } - - /* - * close any relations selected FOR UPDATE, again keeping locks - */ - foreach(l, estate->es_rowMark) - { - execRowMark *erm = lfirst(l); - - heap_close(erm->relation, NoLock); - } -} - -/* ---------------------------------------------------------------- - * ExecutePlan - * - * processes the query plan to retrieve 'numberTuples' tuples in the - * direction specified. - * Retrieves all tuples if numberTuples is 0 - * - * result is either a slot containing the last tuple in the case - * of a RETRIEVE or NULL otherwise. - * - * Note: the ctid attribute is a 'junk' attribute that is removed before the - * user can see it - * ---------------------------------------------------------------- - */ -static TupleTableSlot * -ExecutePlan(EState *estate, - Plan *plan, - CmdType operation, - long numberTuples, - ScanDirection direction, - DestReceiver *destfunc) -{ - JunkFilter *junkfilter; - TupleTableSlot *slot; - ItemPointer tupleid = NULL; - ItemPointerData tuple_ctid; - long current_tuple_count; - TupleTableSlot *result; - - /* - * 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 (;;) - { - /* Reset the per-output-tuple exprcontext */ - ResetPerTupleExprContext(estate); - - /* - * Execute the plan and obtain a tuple - */ -lnext: ; - if (estate->es_useEvalPlan) - { - slot = EvalPlanQualNext(estate); - if (TupIsNull(slot)) - slot = ExecProcNode(plan, NULL); - } - else - slot = ExecProcNode(plan, NULL); - - /* - * 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 junkfilter's resultSlot. - * (Formerly, we stored it back over the "dirty" tuple, which is - * WRONG because that tuple slot has the wrong descriptor.) - * - * Also, extract all the junk information we need. - */ - if ((junkfilter = estate->es_junkFilter) != (JunkFilter *) NULL) - { - Datum datum; - HeapTuple newTuple; - bool isNull; - - /* - * extract the 'ctid' junk attribute. - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE) - { - if (!ExecGetJunkAttribute(junkfilter, - slot, - "ctid", - &datum, - &isNull)) - elog(ERROR, "ExecutePlan: NO (junk) `ctid' was found!"); - - /* shouldn't ever get a null result... */ - if (isNull) - elog(ERROR, "ExecutePlan: (junk) `ctid' is NULL!"); - - tupleid = (ItemPointer) DatumGetPointer(datum); - tuple_ctid = *tupleid; /* make sure we don't free the - * ctid!! */ - tupleid = &tuple_ctid; - } - else if (estate->es_rowMark != NIL) - { - List *l; - - lmark: ; - foreach(l, estate->es_rowMark) - { - execRowMark *erm = lfirst(l); - Buffer buffer; - HeapTupleData tuple; - TupleTableSlot *newSlot; - int test; - - if (!ExecGetJunkAttribute(junkfilter, - slot, - erm->resname, - &datum, - &isNull)) - elog(ERROR, "ExecutePlan: NO (junk) `%s' was found!", - erm->resname); - - /* shouldn't ever get a null result... */ - if (isNull) - elog(ERROR, "ExecutePlan: (junk) `%s' is NULL!", - erm->resname); - - tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); - test = heap_mark4update(erm->relation, &tuple, &buffer, - estate->es_snapshot->curcid); - ReleaseBuffer(buffer); - switch (test) - { - case HeapTupleSelfUpdated: - case HeapTupleMayBeUpdated: - break; - - case HeapTupleUpdated: - if (XactIsoLevel == XACT_SERIALIZABLE) - elog(ERROR, "Can't serialize access due to concurrent update"); - if (!(ItemPointerEquals(&(tuple.t_self), - (ItemPointer) DatumGetPointer(datum)))) - { - newSlot = EvalPlanQual(estate, erm->rti, &(tuple.t_self)); - if (!(TupIsNull(newSlot))) - { - slot = newSlot; - estate->es_useEvalPlan = true; - goto lmark; - } - } - - /* - * if tuple was deleted or PlanQual failed for - * updated tuple - we must not return this - * tuple! - */ - goto lnext; - - default: - elog(ERROR, "Unknown status %u from heap_mark4update", test); - return (NULL); - } - } - } - - /* - * Finally create a new "clean" tuple with all junk attributes - * removed - */ - newTuple = ExecRemoveJunk(junkfilter, slot); - - slot = ExecStoreTuple(newTuple, /* tuple to store */ - junkfilter->jf_resultSlot, /* dest 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 its attributes. - */ - - switch (operation) - { - case CMD_SELECT: - ExecRetrieve(slot, /* slot containing tuple */ - destfunc, /* destination's tuple-receiver - * obj */ - estate); /* */ - 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); - result = NULL; - break; - - default: - elog(LOG, "ExecutePlan: unknown operation in queryDesc"); - result = NULL; - break; - } - - /* - * check our tuple count.. if we've processed the proper number - * then quit, else loop again and process more tuples.. - */ - current_tuple_count++; - 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, - DestReceiver *destfunc, - EState *estate) -{ - 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 (estate->es_into_relation_descriptor != NULL) - { - heap_insert(estate->es_into_relation_descriptor, tuple, - estate->es_snapshot->curcid); - IncrAppended(); - } - - /* - * send the tuple to the front end (or the screen) - */ - (*destfunc->receiveTuple) (tuple, attrtype, destfunc); - IncrRetrieved(); - (estate->es_processed)++; -} - -/* ---------------------------------------------------------------- - * 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; - ResultRelInfo *resultRelInfo; - Relation resultRelationDesc; - int numIndices; - Oid newId; - - /* - * get the heap tuple out of the tuple table slot - */ - tuple = slot->val; - - /* - * get information on the (current) result relation - */ - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - - /* BEFORE ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) - { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); - - if (newtuple == NULL) /* "do nothing" */ - return; - - if (newtuple != tuple) /* modified by Trigger(s) */ - { - /* - * Insert modified tuple into tuple table slot, replacing the - * original. We assume that it was allocated in per-tuple - * memory context, and therefore will go away by itself. The - * tuple table slot should not try to clear it. - */ - ExecStoreTuple(newtuple, slot, InvalidBuffer, false); - tuple = newtuple; - } - } - - /* - * Check the constraints of the tuple - */ - if (resultRelationDesc->rd_att->constr) - ExecConstraints("ExecAppend", resultRelInfo, slot, estate); - - /* - * insert the tuple - */ - newId = heap_insert(resultRelationDesc, tuple, - estate->es_snapshot->curcid); - - IncrAppended(); - (estate->es_processed)++; - estate->es_lastoid = newId; - setLastTid(&(tuple->t_self)); - - /* - * 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 = resultRelInfo->ri_NumIndices; - if (numIndices > 0) - ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); - - /* AFTER ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc) - ExecARInsertTriggers(estate, resultRelInfo, tuple); -} - -/* ---------------------------------------------------------------- - * ExecDelete - * - * DELETE is like append, we delete the tuple and its - * index tuples. - * ---------------------------------------------------------------- - */ -static void -ExecDelete(TupleTableSlot *slot, - ItemPointer tupleid, - EState *estate) -{ - ResultRelInfo *resultRelInfo; - Relation resultRelationDesc; - ItemPointerData ctid; - int result; - - /* - * get information on the (current) result relation - */ - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - - /* BEFORE ROW DELETE Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) - { - bool dodelete; - - dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid); - - if (!dodelete) /* "do nothing" */ - return; - } - - /* - * delete the tuple - */ -ldelete:; - result = heap_delete(resultRelationDesc, tupleid, - &ctid, - estate->es_snapshot->curcid); - switch (result) - { - case HeapTupleSelfUpdated: - return; - - case HeapTupleMayBeUpdated: - break; - - case HeapTupleUpdated: - if (XactIsoLevel == XACT_SERIALIZABLE) - elog(ERROR, "Can't serialize access due to concurrent update"); - else if (!(ItemPointerEquals(tupleid, &ctid))) - { - TupleTableSlot *epqslot = EvalPlanQual(estate, - resultRelInfo->ri_RangeTableIndex, &ctid); - - if (!TupIsNull(epqslot)) - { - *tupleid = ctid; - goto ldelete; - } - } - /* tuple already deleted; nothing to do */ - return; - - default: - elog(ERROR, "Unknown status %u from heap_delete", result); - return; - } - - IncrDeleted(); - (estate->es_processed)++; - - /* - * 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 - */ - - /* AFTER ROW DELETE Triggers */ - if (resultRelInfo->ri_TrigDesc) - ExecARDeleteTriggers(estate, resultRelInfo, tupleid); -} - -/* ---------------------------------------------------------------- - * 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) -{ - HeapTuple tuple; - ResultRelInfo *resultRelInfo; - Relation resultRelationDesc; - ItemPointerData ctid; - int result; - int numIndices; - - /* - * abort the operation if not running transactions - */ - if (IsBootstrapProcessingMode()) - { - elog(WARNING, "ExecReplace: replace can't run without transactions"); - return; - } - - /* - * get the heap tuple out of the tuple table slot - */ - tuple = slot->val; - - /* - * get information on the (current) result relation - */ - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - - /* BEFORE ROW UPDATE Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) - { - HeapTuple newtuple; - - newtuple = ExecBRUpdateTriggers(estate, resultRelInfo, - tupleid, tuple); - - if (newtuple == NULL) /* "do nothing" */ - return; - - if (newtuple != tuple) /* modified by Trigger(s) */ - { - /* - * Insert modified tuple into tuple table slot, replacing the - * original. We assume that it was allocated in per-tuple - * memory context, and therefore will go away by itself. The - * tuple table slot should not try to clear it. - */ - ExecStoreTuple(newtuple, slot, InvalidBuffer, false); - tuple = newtuple; - } - } - - /* - * Check the constraints of the tuple - * - * If we generate a new candidate tuple after EvalPlanQual testing, we - * must loop back here and recheck constraints. (We don't need to - * redo triggers, however. If there are any BEFORE triggers then - * trigger.c will have done mark4update to lock the correct tuple, so - * there's no need to do them again.) - */ -lreplace:; - if (resultRelationDesc->rd_att->constr) - ExecConstraints("ExecReplace", resultRelInfo, slot, estate); - - /* - * replace the heap tuple - */ - result = heap_update(resultRelationDesc, tupleid, tuple, - &ctid, - estate->es_snapshot->curcid); - switch (result) - { - case HeapTupleSelfUpdated: - return; - - case HeapTupleMayBeUpdated: - break; - - case HeapTupleUpdated: - if (XactIsoLevel == XACT_SERIALIZABLE) - elog(ERROR, "Can't serialize access due to concurrent update"); - else if (!(ItemPointerEquals(tupleid, &ctid))) - { - TupleTableSlot *epqslot = EvalPlanQual(estate, - resultRelInfo->ri_RangeTableIndex, &ctid); - - if (!TupIsNull(epqslot)) - { - *tupleid = ctid; - tuple = ExecRemoveJunk(estate->es_junkFilter, epqslot); - slot = ExecStoreTuple(tuple, - estate->es_junkFilter->jf_resultSlot, - InvalidBuffer, true); - goto lreplace; - } - } - /* tuple already deleted; nothing to do */ - return; - - default: - elog(ERROR, "Unknown status %u from heap_update", result); - return; - } - - IncrReplaced(); - (estate->es_processed)++; - - /* - * 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 vacuum daemon. All we - * do is insert new index tuples. -cim 9/27/89 - */ - - /* - * process indices - * - * heap_update 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 = resultRelInfo->ri_NumIndices; - if (numIndices > 0) - ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); - - /* AFTER ROW UPDATE Triggers */ - if (resultRelInfo->ri_TrigDesc) - ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); -} - -static char * -ExecRelCheck(ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, EState *estate) -{ - Relation rel = resultRelInfo->ri_RelationDesc; - int ncheck = rel->rd_att->constr->num_check; - ConstrCheck *check = rel->rd_att->constr->check; - ExprContext *econtext; - MemoryContext oldContext; - List *qual; - int i; - - /* - * If first time through for this result relation, build expression - * nodetrees for rel's constraint expressions. Keep them in the - * per-query memory context so they'll survive throughout the query. - */ - if (resultRelInfo->ri_ConstraintExprs == NULL) - { - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); - for (i = 0; i < ncheck; i++) - { - qual = (List *) stringToNode(check[i].ccbin); - resultRelInfo->ri_ConstraintExprs[i] = qual; - } - MemoryContextSwitchTo(oldContext); - } - - /* - * We will use the EState's per-tuple context for evaluating - * constraint expressions (creating it if it's not already there). - */ - econtext = GetPerTupleExprContext(estate); - - /* Arrange for econtext's scan tuple to be the tuple under test */ - econtext->ecxt_scantuple = slot; - - /* And evaluate the constraints */ - for (i = 0; i < ncheck; i++) - { - qual = resultRelInfo->ri_ConstraintExprs[i]; - - /* - * NOTE: SQL92 specifies that a NULL result from a constraint - * expression is not to be treated as a failure. Therefore, tell - * ExecQual to return TRUE for NULL. - */ - if (!ExecQual(qual, econtext, true)) - return check[i].ccname; - } - - /* NULL result means no error */ - return (char *) NULL; -} - -void -ExecConstraints(char *caller, ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, EState *estate) -{ - Relation rel = resultRelInfo->ri_RelationDesc; - HeapTuple tuple = slot->val; - TupleConstr *constr = rel->rd_att->constr; - - Assert(constr); - - if (constr->has_not_null) - { - int natts = rel->rd_att->natts; - int attrChk; - - for (attrChk = 1; attrChk <= natts; attrChk++) - { - if (rel->rd_att->attrs[attrChk - 1]->attnotnull && - heap_attisnull(tuple, attrChk)) - elog(ERROR, "%s: Fail to add null value in not null attribute %s", - caller, NameStr(rel->rd_att->attrs[attrChk - 1]->attname)); - } - } - - if (constr->num_check > 0) - { - char *failed; - - if ((failed = ExecRelCheck(resultRelInfo, slot, estate)) != NULL) - elog(ERROR, "%s: rejected due to CHECK constraint %s", - caller, failed); - } -} - -/* - * Check a modified tuple to see if we want to process its updated version - * under READ COMMITTED rules. - * - * See backend/executor/README for some info about how this works. - */ -TupleTableSlot * -EvalPlanQual(EState *estate, Index rti, ItemPointer tid) -{ - evalPlanQual *epq; - EState *epqstate; - Relation relation; - HeapTupleData tuple; - HeapTuple copyTuple = NULL; - int rtsize; - bool endNode; - - Assert(rti != 0); - - /* - * find relation containing target tuple - */ - if (estate->es_result_relation_info != NULL && - estate->es_result_relation_info->ri_RangeTableIndex == rti) - relation = estate->es_result_relation_info->ri_RelationDesc; - else - { - List *l; - - relation = NULL; - foreach(l, estate->es_rowMark) - { - if (((execRowMark *) lfirst(l))->rti == rti) - { - relation = ((execRowMark *) lfirst(l))->relation; - break; - } - } - if (relation == NULL) - elog(ERROR, "EvalPlanQual: can't find RTE %d", (int) rti); - } - - /* - * fetch tid tuple - * - * Loop here to deal with updated or busy tuples - */ - tuple.t_self = *tid; - for (;;) - { - Buffer buffer; - - if (heap_fetch(relation, SnapshotDirty, &tuple, &buffer, false, NULL)) - { - TransactionId xwait = SnapshotDirty->xmax; - - if (TransactionIdIsValid(SnapshotDirty->xmin)) - elog(ERROR, "EvalPlanQual: t_xmin is uncommitted ?!"); - - /* - * If tuple is being updated by other transaction then we have - * to wait for its commit/abort. - */ - if (TransactionIdIsValid(xwait)) - { - ReleaseBuffer(buffer); - XactLockTableWait(xwait); - continue; - } - - /* - * We got tuple - now copy it for use by recheck query. - */ - copyTuple = heap_copytuple(&tuple); - ReleaseBuffer(buffer); - break; - } - - /* - * Oops! Invalid tuple. Have to check is it updated or deleted. - * Note that it's possible to get invalid SnapshotDirty->tid if - * tuple updated by this transaction. Have we to check this ? - */ - if (ItemPointerIsValid(&(SnapshotDirty->tid)) && - !(ItemPointerEquals(&(tuple.t_self), &(SnapshotDirty->tid)))) - { - /* updated, so look at the updated copy */ - tuple.t_self = SnapshotDirty->tid; - continue; - } - - /* - * Deleted or updated by this transaction; forget it. - */ - return NULL; - } - - /* - * For UPDATE/DELETE we have to return tid of actual row we're - * executing PQ for. - */ - *tid = tuple.t_self; - - /* - * Need to run a recheck subquery. Find or create a PQ stack entry. - */ - epq = (evalPlanQual *) estate->es_evalPlanQual; - rtsize = length(estate->es_range_table); - endNode = true; - - if (epq != NULL && epq->rti == 0) - { - /* Top PQ stack entry is idle, so re-use it */ - Assert(!(estate->es_useEvalPlan) && - epq->estate.es_evalPlanQual == NULL); - epq->rti = rti; - endNode = false; - } - - /* - * If this is request for another RTE - Ra, - then we have to check - * wasn't PlanQual requested for Ra already and if so then Ra' row was - * updated again and we have to re-start old execution for Ra and - * forget all what we done after Ra was suspended. Cool? -:)) - */ - if (epq != NULL && epq->rti != rti && - epq->estate.es_evTuple[rti - 1] != NULL) - { - do - { - evalPlanQual *oldepq; - - /* pop previous PlanQual from the stack */ - epqstate = &(epq->estate); - oldepq = (evalPlanQual *) epqstate->es_evalPlanQual; - Assert(oldepq->rti != 0); - /* stop execution */ - ExecEndNode(epq->plan, NULL); - ExecDropTupleTable(epqstate->es_tupleTable, true); - epqstate->es_tupleTable = NULL; - heap_freetuple(epqstate->es_evTuple[epq->rti - 1]); - epqstate->es_evTuple[epq->rti - 1] = NULL; - /* push current PQ to freePQ stack */ - oldepq->free = epq; - epq = oldepq; - estate->es_evalPlanQual = (Pointer) epq; - } while (epq->rti != rti); - } - - /* - * If we are requested for another RTE then we have to suspend - * execution of current PlanQual and start execution for new one. - */ - if (epq == NULL || epq->rti != rti) - { - /* try to reuse plan used previously */ - evalPlanQual *newepq = (epq != NULL) ? epq->free : NULL; - - if (newepq == NULL) /* first call or freePQ stack is empty */ - { - newepq = (evalPlanQual *) palloc(sizeof(evalPlanQual)); - newepq->free = NULL; - - /* - * Each stack level has its own copy of the plan tree. This - * is wasteful, but necessary as long as plan nodes point to - * exec state nodes rather than vice versa. Note that - * copyfuncs.c doesn't attempt to copy the exec state nodes, - * which is a good thing in this situation. - */ - newepq->plan = copyObject(estate->es_origPlan); - - /* - * Init stack level's EState. We share top level's copy of - * es_result_relations array and other non-changing status. We - * need our own tupletable, es_param_exec_vals, and other - * changeable state. - */ - epqstate = &(newepq->estate); - memcpy(epqstate, estate, sizeof(EState)); - epqstate->es_direction = ForwardScanDirection; - if (estate->es_origPlan->nParamExec > 0) - epqstate->es_param_exec_vals = (ParamExecData *) - palloc(estate->es_origPlan->nParamExec * - sizeof(ParamExecData)); - epqstate->es_tupleTable = NULL; - epqstate->es_per_tuple_exprcontext = NULL; - - /* - * Each epqstate must have its own es_evTupleNull state, but - * all the stack entries share es_evTuple state. This allows - * sub-rechecks to inherit the value being examined by an - * outer recheck. - */ - epqstate->es_evTupleNull = (bool *) palloc(rtsize * sizeof(bool)); - if (epq == NULL) - { - /* first PQ stack entry */ - epqstate->es_evTuple = (HeapTuple *) - palloc(rtsize * sizeof(HeapTuple)); - memset(epqstate->es_evTuple, 0, rtsize * sizeof(HeapTuple)); - } - else - { - /* later stack entries share the same storage */ - epqstate->es_evTuple = epq->estate.es_evTuple; - } - } - else - { - /* recycle previously used EState */ - epqstate = &(newepq->estate); - } - /* push current PQ to the stack */ - epqstate->es_evalPlanQual = (Pointer) epq; - epq = newepq; - estate->es_evalPlanQual = (Pointer) epq; - epq->rti = rti; - endNode = false; - } - - Assert(epq->rti == rti); - epqstate = &(epq->estate); - - /* - * Ok - we're requested for the same RTE. Unfortunately we still have - * to end and restart execution of the plan, because ExecReScan - * wouldn't ensure that upper plan nodes would reset themselves. We - * could make that work if insertion of the target tuple were - * integrated with the Param mechanism somehow, so that the upper plan - * nodes know that their children's outputs have changed. - */ - if (endNode) - { - /* stop execution */ - ExecEndNode(epq->plan, NULL); - ExecDropTupleTable(epqstate->es_tupleTable, true); - epqstate->es_tupleTable = NULL; - } - - /* - * free old RTE' tuple, if any, and store target tuple where - * relation's scan node will see it - */ - if (epqstate->es_evTuple[rti - 1] != NULL) - heap_freetuple(epqstate->es_evTuple[rti - 1]); - epqstate->es_evTuple[rti - 1] = copyTuple; - - /* - * Initialize for new recheck query; be careful to copy down state - * that might have changed in top EState. - */ - epqstate->es_result_relation_info = estate->es_result_relation_info; - epqstate->es_junkFilter = estate->es_junkFilter; - if (estate->es_origPlan->nParamExec > 0) - memset(epqstate->es_param_exec_vals, 0, - estate->es_origPlan->nParamExec * sizeof(ParamExecData)); - memset(epqstate->es_evTupleNull, false, rtsize * sizeof(bool)); - epqstate->es_useEvalPlan = false; - Assert(epqstate->es_tupleTable == NULL); - epqstate->es_tupleTable = - ExecCreateTupleTable(estate->es_tupleTable->size); - - ExecInitNode(epq->plan, epqstate, NULL); - - return EvalPlanQualNext(estate); -} - -static TupleTableSlot * -EvalPlanQualNext(EState *estate) -{ - evalPlanQual *epq = (evalPlanQual *) estate->es_evalPlanQual; - EState *epqstate = &(epq->estate); - evalPlanQual *oldepq; - TupleTableSlot *slot; - - Assert(epq->rti != 0); - -lpqnext:; - slot = ExecProcNode(epq->plan, NULL); - - /* - * No more tuples for this PQ. Continue previous one. - */ - if (TupIsNull(slot)) - { - /* stop execution */ - ExecEndNode(epq->plan, NULL); - ExecDropTupleTable(epqstate->es_tupleTable, true); - epqstate->es_tupleTable = NULL; - heap_freetuple(epqstate->es_evTuple[epq->rti - 1]); - epqstate->es_evTuple[epq->rti - 1] = NULL; - /* pop old PQ from the stack */ - oldepq = (evalPlanQual *) epqstate->es_evalPlanQual; - if (oldepq == (evalPlanQual *) NULL) - { - epq->rti = 0; /* this is the first (oldest) */ - estate->es_useEvalPlan = false; /* PQ - mark as free and */ - return (NULL); /* continue Query execution */ - } - Assert(oldepq->rti != 0); - /* push current PQ to freePQ stack */ - oldepq->free = epq; - epq = oldepq; - epqstate = &(epq->estate); - estate->es_evalPlanQual = (Pointer) epq; - goto lpqnext; - } - - return (slot); -} - -static void -EndEvalPlanQual(EState *estate) -{ - evalPlanQual *epq = (evalPlanQual *) estate->es_evalPlanQual; - EState *epqstate = &(epq->estate); - evalPlanQual *oldepq; - - if (epq->rti == 0) /* plans already shutdowned */ - { - Assert(epq->estate.es_evalPlanQual == NULL); - return; - } - - for (;;) - { - /* stop execution */ - ExecEndNode(epq->plan, NULL); - ExecDropTupleTable(epqstate->es_tupleTable, true); - epqstate->es_tupleTable = NULL; - if (epqstate->es_evTuple[epq->rti - 1] != NULL) - { - heap_freetuple(epqstate->es_evTuple[epq->rti - 1]); - epqstate->es_evTuple[epq->rti - 1] = NULL; - } - /* pop old PQ from the stack */ - oldepq = (evalPlanQual *) epqstate->es_evalPlanQual; - if (oldepq == (evalPlanQual *) NULL) - { - epq->rti = 0; /* this is the first (oldest) */ - estate->es_useEvalPlan = false; /* PQ - mark as free */ - break; - } - Assert(oldepq->rti != 0); - /* push current PQ to freePQ stack */ - oldepq->free = epq; - epq = oldepq; - epqstate = &(epq->estate); - estate->es_evalPlanQual = (Pointer) epq; - } -} diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c deleted file mode 100644 index 2054a6cf059..00000000000 --- a/src/backend/executor/execProcnode.c +++ /dev/null @@ -1,759 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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 its subnodes and do the appropriate - * processing.. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.30 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecInitNode - initialize a plan node and its subplans - * ExecProcNode - get a tuple by executing the plan node - * ExecEndNode - shut down a plan node and its subplans - * ExecCountSlotsNode - count tuple slots needed by plan tree - * ExecGetTupType - get result tuple type of a plan node - * - * 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 "postgres.h" - -#include "executor/executor.h" -#include "executor/instrument.h" -#include "executor/nodeAgg.h" -#include "executor/nodeAppend.h" -#include "executor/nodeGroup.h" -#include "executor/nodeHash.h" -#include "executor/nodeHashjoin.h" -#include "executor/nodeIndexscan.h" -#include "executor/nodeTidscan.h" -#include "executor/nodeLimit.h" -#include "executor/nodeMaterial.h" -#include "executor/nodeMergejoin.h" -#include "executor/nodeNestloop.h" -#include "executor/nodeResult.h" -#include "executor/nodeSeqscan.h" -#include "executor/nodeSetOp.h" -#include "executor/nodeSort.h" -#include "executor/nodeSubplan.h" -#include "executor/nodeSubqueryscan.h" -#include "executor/nodeFunctionscan.h" -#include "executor/nodeUnique.h" -#include "miscadmin.h" -#include "tcop/tcopprot.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; - List *subp; - - /* - * do nothing when we get to the end of a leaf on tree. - */ - if (node == NULL) - return FALSE; - - /* Set up instrumentation for this node if the parent has it */ - if (!node->instrument && parent && parent->instrument) - node->instrument = InstrAlloc(); - - foreach(subp, node->initPlan) - { - result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node); - if (result == FALSE) - 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; - - case T_TidScan: - result = ExecInitTidScan((TidScan *) node, estate, parent); - break; - - case T_SubqueryScan: - result = ExecInitSubqueryScan((SubqueryScan *) node, estate, - parent); - break; - - case T_FunctionScan: - result = ExecInitFunctionScan((FunctionScan *) 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; - - case T_Hash: - result = ExecInitHash((Hash *) node, estate, parent); - break; - - case T_HashJoin: - result = ExecInitHashJoin((HashJoin *) 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_SetOp: - result = ExecInitSetOp((SetOp *) node, estate, parent); - break; - - case T_Limit: - result = ExecInitLimit((Limit *) node, estate, parent); - break; - - case T_Group: - result = ExecInitGroup((Group *) node, estate, parent); - break; - - case T_Agg: - result = ExecInitAgg((Agg *) node, estate, parent); - break; - - default: - elog(ERROR, "ExecInitNode: node type %d unsupported", - (int) nodeTag(node)); - result = FALSE; - break; - } - - if (result != FALSE) - { - foreach(subp, node->subPlan) - { - result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node); - if (result == FALSE) - return FALSE; - } - } - - return result; -} - - -/* ---------------------------------------------------------------- - * ExecProcNode - * - * Initial States: - * the query tree must be initialized once by calling ExecInit. - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecProcNode(Plan *node, Plan *parent) -{ - TupleTableSlot *result; - - CHECK_FOR_INTERRUPTS(); - - /* - * deal with NULL nodes.. - */ - if (node == NULL) - return NULL; - - if (node->chgParam != NULL) /* something changed */ - ExecReScan(node, NULL, parent); /* let ReScan handle this */ - - if (node->instrument) - InstrStartNode(node->instrument); - - 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; - - case T_TidScan: - result = ExecTidScan((TidScan *) node); - break; - - case T_SubqueryScan: - result = ExecSubqueryScan((SubqueryScan *) node); - break; - - case T_FunctionScan: - result = ExecFunctionScan((FunctionScan *) node); - break; - - /* - * join nodes - */ - case T_NestLoop: - result = ExecNestLoop((NestLoop *) node); - break; - - case T_MergeJoin: - result = ExecMergeJoin((MergeJoin *) node); - break; - - case T_Hash: - result = ExecHash((Hash *) node); - break; - - case T_HashJoin: - result = ExecHashJoin((HashJoin *) 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_SetOp: - result = ExecSetOp((SetOp *) node); - break; - - case T_Limit: - result = ExecLimit((Limit *) node); - break; - - case T_Group: - result = ExecGroup((Group *) node); - break; - - case T_Agg: - result = ExecAgg((Agg *) node); - break; - - default: - elog(ERROR, "ExecProcNode: node type %d unsupported", - (int) nodeTag(node)); - result = NULL; - break; - } - - if (node->instrument) - InstrStopNode(node->instrument, !TupIsNull(result)); - - 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); - - case T_TidScan: - return ExecCountSlotsTidScan((TidScan *) node); - - case T_SubqueryScan: - return ExecCountSlotsSubqueryScan((SubqueryScan *) node); - - case T_FunctionScan: - return ExecCountSlotsFunctionScan((FunctionScan *) node); - - /* - * join nodes - */ - case T_NestLoop: - return ExecCountSlotsNestLoop((NestLoop *) node); - - case T_MergeJoin: - return ExecCountSlotsMergeJoin((MergeJoin *) node); - - case T_Hash: - return ExecCountSlotsHash((Hash *) node); - - case T_HashJoin: - return ExecCountSlotsHashJoin((HashJoin *) 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_SetOp: - return ExecCountSlotsSetOp((SetOp *) node); - - case T_Limit: - return ExecCountSlotsLimit((Limit *) node); - - case T_Group: - return ExecCountSlotsGroup((Group *) node); - - case T_Agg: - return ExecCountSlotsAgg((Agg *) node); - - default: - elog(ERROR, "ExecCountSlotsNode: node type %d unsupported", - (int) 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) -{ - List *subp; - - /* - * do nothing when we get to the end of a leaf on tree. - */ - if (node == NULL) - return; - - foreach(subp, node->initPlan) - ExecEndSubPlan((SubPlan *) lfirst(subp)); - foreach(subp, node->subPlan) - ExecEndSubPlan((SubPlan *) lfirst(subp)); - if (node->chgParam != NULL) - { - freeList(node->chgParam); - node->chgParam = NULL; - } - - 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; - - case T_TidScan: - ExecEndTidScan((TidScan *) node); - break; - - case T_SubqueryScan: - ExecEndSubqueryScan((SubqueryScan *) node); - break; - - case T_FunctionScan: - ExecEndFunctionScan((FunctionScan *) node); - break; - - /* - * join nodes - */ - case T_NestLoop: - ExecEndNestLoop((NestLoop *) node); - break; - - case T_MergeJoin: - ExecEndMergeJoin((MergeJoin *) node); - break; - - case T_Hash: - ExecEndHash((Hash *) node); - break; - - case T_HashJoin: - ExecEndHashJoin((HashJoin *) 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_SetOp: - ExecEndSetOp((SetOp *) node); - break; - - case T_Limit: - ExecEndLimit((Limit *) node); - break; - - case T_Group: - ExecEndGroup((Group *) node); - break; - - case T_Agg: - ExecEndAgg((Agg *) node); - break; - - default: - elog(ERROR, "ExecEndNode: node type %d unsupported", - (int) nodeTag(node)); - break; - } - - if (node->instrument) - InstrEndLoop(node->instrument); -} - - -/* ---------------------------------------------------------------- - * 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. - * - * ---------------------------------------------------------------- - */ -TupleDesc -ExecGetTupType(Plan *node) -{ - TupleTableSlot *slot; - - if (node == NULL) - return NULL; - - 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: - { - AppendState *appendstate = ((Append *) node)->appendstate; - - slot = appendstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_IndexScan: - { - CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate; - - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_TidScan: - { - CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate; - - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_SubqueryScan: - { - CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate; - - slot = scanstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_FunctionScan: - { - CommonScanState *scanstate = ((FunctionScan *) 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->cstate.cs_ResultTupleSlot; - } - break; - - case T_SetOp: - { - SetOpState *setopstate = ((SetOp *) node)->setopstate; - - slot = setopstate->cstate.cs_ResultTupleSlot; - } - break; - - case T_Limit: - { - LimitState *limitstate = ((Limit *) node)->limitstate; - - slot = limitstate->cstate.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; - - default: - - /* - * should never get here - */ - elog(ERROR, "ExecGetTupType: node type %d unsupported", - (int) nodeTag(node)); - return NULL; - } - - return slot->ttc_tupleDescriptor; -} diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c deleted file mode 100644 index 0b2f24d917a..00000000000 --- a/src/backend/executor/execQual.c +++ /dev/null @@ -1,1929 +0,0 @@ -/*------------------------------------------------------------------------- - * - * execQual.c - * Routines to evaluate qualification and targetlist expressions - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.94 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecEvalExpr - evaluate an expression and return a datum - * ExecEvalExprSwitchContext - same, but switch into eval memory context - * ExecQual - return true/false if qualification is satisfied - * ExecProject - 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. - * - * ExecProject() 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 "postgres.h" - -#include "access/heapam.h" -#include "executor/execdebug.h" -#include "executor/functions.h" -#include "executor/nodeSubplan.h" -#include "utils/array.h" -#include "utils/builtins.h" -#include "utils/fcache.h" - - -/* static function decls */ -static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, - bool *isNull); -static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull); -static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, - List *argList, ExprContext *econtext); -static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull); -static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); - - -/*---------- - * ExecEvalArrayRef - * - * This function takes an ArrayRef and returns the extracted Datum - * if it's a simple reference, or the modified array value if it's - * an array assignment (i.e., array element or slice insertion). - * - * NOTE: if we get a NULL result from a subexpression, we return NULL when - * it's an array reference, or the unmodified source array when it's an - * array assignment. This may seem peculiar, but if we return NULL (as was - * done in versions up through 7.0) then an assignment like - * UPDATE table SET arrayfield[4] = NULL - * will result in setting the whole array to NULL, which is certainly not - * very desirable. By returning the source array we make the assignment - * into a no-op, instead. (Eventually we need to redesign arrays so that - * individual elements can be NULL, but for now, let's try to protect users - * from shooting themselves in the foot.) - * - * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here, - * even though that might seem natural, because this code needs to support - * both varlena arrays and fixed-length array types. DatumGetArrayTypeP() - * only works for the varlena kind. The routines we call in arrayfuncs.c - * have to know the difference (that's what they need refattrlength for). - *---------- - */ -static Datum -ExecEvalArrayRef(ArrayRef *arrayRef, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - ArrayType *array_source; - ArrayType *resultArray; - bool isAssignment = (arrayRef->refassgnexpr != NULL); - List *elt; - int i = 0, - j = 0; - IntArray upper, - lower; - int *lIndex; - - if (arrayRef->refexpr != NULL) - { - array_source = (ArrayType *) - DatumGetPointer(ExecEvalExpr(arrayRef->refexpr, - econtext, - isNull, - isDone)); - - /* - * If refexpr yields NULL, result is always NULL, for now anyway. - * (This means you cannot assign to an element or slice of an - * array that's NULL; it'll just stay NULL.) - */ - if (*isNull) - return (Datum) NULL; - } - else - { - /* - * Empty refexpr indicates we are doing an INSERT into an array - * column. For now, we just take the refassgnexpr (which the - * parser will have ensured is an array value) and return it - * as-is, ignoring any subscripts that may have been supplied in - * the INSERT column list. This is a kluge, but it's not real - * clear what the semantics ought to be... - */ - array_source = NULL; - } - - foreach(elt, arrayRef->refupperindexpr) - { - if (i >= MAXDIM) - elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions", - MAXDIM); - - upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt), - econtext, - isNull, - NULL)); - /* If any index expr yields NULL, result is NULL or source array */ - if (*isNull) - { - if (!isAssignment || array_source == NULL) - return (Datum) NULL; - *isNull = false; - return PointerGetDatum(array_source); - } - } - - if (arrayRef->reflowerindexpr != NIL) - { - foreach(elt, arrayRef->reflowerindexpr) - { - if (j >= MAXDIM) - elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions", - MAXDIM); - - lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt), - econtext, - isNull, - NULL)); - - /* - * If any index expr yields NULL, result is NULL or source - * array - */ - if (*isNull) - { - if (!isAssignment || array_source == NULL) - return (Datum) NULL; - *isNull = false; - return PointerGetDatum(array_source); - } - } - if (i != j) - elog(ERROR, - "ExecEvalArrayRef: upper and lower indices mismatch"); - lIndex = lower.indx; - } - else - lIndex = NULL; - - if (isAssignment) - { - Datum sourceData = ExecEvalExpr(arrayRef->refassgnexpr, - econtext, - isNull, - NULL); - - /* - * For now, can't cope with inserting NULL into an array, so make - * it a no-op per discussion above... - */ - if (*isNull) - { - if (array_source == NULL) - return (Datum) NULL; - *isNull = false; - return PointerGetDatum(array_source); - } - - if (array_source == NULL) - return sourceData; /* XXX do something else? */ - - if (lIndex == NULL) - resultArray = array_set(array_source, i, - upper.indx, - sourceData, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, - isNull); - else - resultArray = array_set_slice(array_source, i, - upper.indx, lower.indx, - (ArrayType *) DatumGetPointer(sourceData), - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, - isNull); - return PointerGetDatum(resultArray); - } - - if (lIndex == NULL) - return array_ref(array_source, i, - upper.indx, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, - isNull); - else - { - resultArray = array_get_slice(array_source, i, - upper.indx, lower.indx, - arrayRef->refelembyval, - arrayRef->refelemlength, - arrayRef->refattrlength, - isNull); - return PointerGetDatum(resultArray); - } -} - - -/* ---------------------------------------------------------------- - * ExecEvalAggref - * - * Returns a Datum whose value is the value of the precomputed - * aggregate found in the given expression context. - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull) -{ - if (econtext->ecxt_aggvalues == NULL) /* safety check */ - elog(ERROR, "ExecEvalAggref: no aggregates in this expression context"); - - *isNull = econtext->ecxt_aggnulls[aggref->aggno]; - return econtext->ecxt_aggvalues[aggref->aggno]; -} - -/* ---------------------------------------------------------------- - * ExecEvalVar - * - * Returns a Datum whose value is the value of a range - * variable with respect to given expression context. - * - * - * As an entry condition, we expect that the datatype the - * plan expects to get (as told by our "variable" argument) is in - * fact the datatype of the attribute the plan says to fetch (as - * seen in the current context, identified by our "econtext" - * argument). - * - * If we fetch a Type A attribute and Caller treats it as if it - * were Type B, there will be undefined results (e.g. crash). - * One way these might mismatch now is that we're accessing a - * catalog class and the type information in the pg_attribute - * class does not match the hardcoded pg_attribute information - * (in pg_attribute.h) for the class in question. - * - * We have an Assert to make sure this entry condition is met. - * - * ---------------------------------------------------------------- */ -static Datum -ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) -{ - Datum result; - TupleTableSlot *slot; - AttrNumber attnum; - HeapTuple heapTuple; - TupleDesc tuple_type; - - /* - * 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; - - attnum = variable->varattno; - - /* (See prolog for explanation of this Assert) */ - Assert(attnum <= 0 || - (attnum - 1 <= tuple_type->natts - 1 && - tuple_type->attrs[attnum - 1] != NULL && - variable->vartype == tuple_type->attrs[attnum - 1]->atttypid)); - - /* - * 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. - * - * XXX this is a horrid crock: since the pointer to the slot might live - * longer than the current evaluation context, we are forced to copy - * the tuple and slot into a long-lived context --- we use - * TransactionCommandContext which should be safe enough. This - * represents a serious memory leak if many such tuples are processed - * in one command, however. We ought to redesign the representation - * of whole-tuple datums so that this is not necessary. - * - * We assume it's OK to point to the existing tupleDescriptor, rather - * than copy that too. - */ - if (attnum == InvalidAttrNumber) - { - MemoryContext oldContext; - TupleTableSlot *tempSlot; - HeapTuple tup; - - oldContext = MemoryContextSwitchTo(TransactionCommandContext); - tempSlot = MakeTupleTableSlot(); - tup = heap_copytuple(heapTuple); - ExecStoreTuple(tup, tempSlot, InvalidBuffer, true); - ExecSetSlotDescriptor(tempSlot, tuple_type, false); - MemoryContextSwitchTo(oldContext); - return PointerGetDatum(tempSlot); - } - - result = heap_getattr(heapTuple, /* tuple containing attribute */ - attnum, /* attribute number of desired - * attribute */ - tuple_type, /* tuple descriptor of tuple */ - isNull); /* return: is attribute null? */ - - 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 = expression->paramkind; - AttrNumber thisParameterId = expression->paramid; - int matchFound; - ParamListInfo paramList; - - if (thisParameterKind == PARAM_EXEC) - { - ParamExecData *prm; - - prm = &(econtext->ecxt_param_exec_vals[thisParameterId]); - if (prm->execPlan != NULL) - { - ExecSetParamPlan(prm->execPlan, econtext); - /* ExecSetParamPlan should have processed this param... */ - Assert(prm->execPlan == NULL); - } - *isNull = prm->isnull; - return prm->value; - } - - thisParameterName = expression->paramname; - 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(ERROR, - "ExecEvalParam: new/old params with same id & diff names"); - } - } - break; - default: - - /* - * oops! this is not supposed to happen! - */ - elog(ERROR, "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(ERROR, "ExecEvalParam: Unknown value for parameter %s", - thisParameterName); - } - - /* - * return the value. - */ - *isNull = paramList->isnull; - 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(). - */ -Datum -GetAttributeByNum(TupleTableSlot *slot, - AttrNumber attrno, - bool *isNull) -{ - Datum retval; - - if (!AttributeNumberIsValid(attrno)) - elog(ERROR, "GetAttributeByNum: Invalid attribute number"); - - if (!AttrNumberIsForUserDefinedAttr(attrno)) - elog(ERROR, "GetAttributeByNum: cannot access system attributes here"); - - if (isNull == (bool *) NULL) - elog(ERROR, "GetAttributeByNum: a NULL isNull flag was passed"); - - if (TupIsNull(slot)) - { - *isNull = true; - return (Datum) 0; - } - - retval = heap_getattr(slot->val, - attrno, - slot->ttc_tupleDescriptor, - isNull); - if (*isNull) - return (Datum) 0; - - return retval; -} - -Datum -GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) -{ - AttrNumber attrno; - TupleDesc tupdesc; - Datum retval; - int natts; - int i; - - if (attname == NULL) - elog(ERROR, "GetAttributeByName: Invalid attribute name"); - - if (isNull == (bool *) NULL) - elog(ERROR, "GetAttributeByName: a NULL isNull flag was passed"); - - if (TupIsNull(slot)) - { - *isNull = true; - return (Datum) 0; - } - - tupdesc = slot->ttc_tupleDescriptor; - natts = slot->val->t_data->t_natts; - - attrno = InvalidAttrNumber; - for (i = 0; i < tupdesc->natts; i++) - { - if (namestrcmp(&(tupdesc->attrs[i]->attname), attname) == 0) - { - attrno = tupdesc->attrs[i]->attnum; - break; - } - } - - if (attrno == InvalidAttrNumber) - elog(ERROR, "GetAttributeByName: attribute %s not found", attname); - - retval = heap_getattr(slot->val, - attrno, - tupdesc, - isNull); - if (*isNull) - return (Datum) 0; - - return retval; -} - -/* - * Evaluate arguments for a function. - */ -static ExprDoneCond -ExecEvalFuncArgs(FunctionCallInfo fcinfo, - List *argList, - ExprContext *econtext) -{ - ExprDoneCond argIsDone; - int i; - List *arg; - - argIsDone = ExprSingleResult; /* default assumption */ - - i = 0; - foreach(arg, argList) - { - ExprDoneCond thisArgIsDone; - - fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg), - econtext, - &fcinfo->argnull[i], - &thisArgIsDone); - - if (thisArgIsDone != ExprSingleResult) - { - /* - * We allow only one argument to have a set value; we'd need - * much more complexity to keep track of multiple set - * arguments (cf. ExecTargetList) and it doesn't seem worth - * it. - */ - if (argIsDone != ExprSingleResult) - elog(ERROR, "Functions and operators can take only one set argument"); - argIsDone = thisArgIsDone; - } - i++; - } - - fcinfo->nargs = i; - - return argIsDone; -} - -/* - * ExecMakeFunctionResult - * - * Evaluate the arguments to a function and then the function itself. - * - * NOTE: econtext is used only for evaluating the argument expressions; - * it is not passed to the function itself. - */ -Datum -ExecMakeFunctionResult(FunctionCachePtr fcache, - List *arguments, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Datum result; - FunctionCallInfoData fcinfo; - ReturnSetInfo rsinfo; /* for functions returning sets */ - ExprDoneCond argDone; - bool hasSetArg; - int i; - - /* - * arguments is a list of expressions to evaluate before passing to - * the function manager. We skip the evaluation if it was already - * done in the previous call (ie, we are continuing the evaluation of - * a set-valued function). Otherwise, collect the current argument - * values into fcinfo. - */ - if (!fcache->setArgsValid) - { - /* Need to prep callinfo structure */ - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &(fcache->func); - argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext); - if (argDone == ExprEndResult) - { - /* input is an empty set, so return an empty set. */ - *isNull = true; - if (isDone) - *isDone = ExprEndResult; - else - elog(ERROR, "Set-valued function called in context that cannot accept a set"); - return (Datum) 0; - } - hasSetArg = (argDone != ExprSingleResult); - } - else - { - /* Copy callinfo from previous evaluation */ - memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo)); - hasSetArg = fcache->setHasSetArg; - /* Reset flag (we may set it again below) */ - fcache->setArgsValid = false; - } - - /* - * If function returns set, prepare a resultinfo node for - * communication - */ - if (fcache->func.fn_retset) - { - fcinfo.resultinfo = (Node *) &rsinfo; - rsinfo.type = T_ReturnSetInfo; - rsinfo.econtext = econtext; - } - - /* - * now return the value gotten by calling the function manager, - * passing the function the evaluated parameter values. - */ - if (fcache->func.fn_retset || hasSetArg) - { - /* - * We need to return a set result. Complain if caller not ready - * to accept one. - */ - if (isDone == NULL) - elog(ERROR, "Set-valued function called in context that cannot accept a set"); - - /* - * This loop handles the situation where we have both a set - * argument and a set-valued function. Once we have exhausted the - * function's value(s) for a particular argument value, we have to - * get the next argument value and start the function over again. - * We might have to do it more than once, if the function produces - * an empty result set for a particular input value. - */ - for (;;) - { - /* - * If function is strict, and there are any NULL arguments, - * skip calling the function (at least for this set of args). - */ - bool callit = true; - - if (fcache->func.fn_strict) - { - for (i = 0; i < fcinfo.nargs; i++) - { - if (fcinfo.argnull[i]) - { - callit = false; - break; - } - } - } - - if (callit) - { - fcinfo.isnull = false; - rsinfo.isDone = ExprSingleResult; - result = FunctionCallInvoke(&fcinfo); - *isNull = fcinfo.isnull; - *isDone = rsinfo.isDone; - } - else - { - result = (Datum) 0; - *isNull = true; - *isDone = ExprEndResult; - } - - if (*isDone != ExprEndResult) - { - /* - * Got a result from current argument. If function itself - * returns set, save the current argument values to re-use - * on the next call. - */ - if (fcache->func.fn_retset) - { - memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo)); - fcache->setHasSetArg = hasSetArg; - fcache->setArgsValid = true; - } - - /* - * Make sure we say we are returning a set, even if the - * function itself doesn't return sets. - */ - *isDone = ExprMultipleResult; - break; - } - - /* Else, done with this argument */ - if (!hasSetArg) - break; /* input not a set, so done */ - - /* Re-eval args to get the next element of the input set */ - argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext); - - if (argDone != ExprMultipleResult) - { - /* End of argument set, so we're done. */ - *isNull = true; - *isDone = ExprEndResult; - result = (Datum) 0; - break; - } - - /* - * If we reach here, loop around to run the function on the - * new argument. - */ - } - } - else - { - /* - * Non-set case: much easier. - * - * If function is strict, and there are any NULL arguments, skip - * calling the function and return NULL. - */ - if (fcache->func.fn_strict) - { - for (i = 0; i < fcinfo.nargs; i++) - { - if (fcinfo.argnull[i]) - { - *isNull = true; - return (Datum) 0; - } - } - } - fcinfo.isnull = false; - result = FunctionCallInvoke(&fcinfo); - *isNull = fcinfo.isnull; - } - - return result; -} - - -/* ---------------------------------------------------------------- - * ExecEvalOper - * ExecEvalFunc - * - * Evaluate the functional result of a list of arguments by calling the - * function manager. - * ---------------------------------------------------------------- - */ - -/* ---------------------------------------------------------------- - * ExecEvalOper - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalOper(Expr *opClause, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Oper *op; - List *argList; - FunctionCachePtr fcache; - - /* - * 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) - { - fcache = init_fcache(op->opid, length(argList), - econtext->ecxt_per_query_memory); - op->op_fcache = fcache; - } - - return ExecMakeFunctionResult(fcache, argList, econtext, - isNull, isDone); -} - -/* ---------------------------------------------------------------- - * ExecEvalFunc - * ---------------------------------------------------------------- - */ - -static Datum -ExecEvalFunc(Expr *funcClause, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Func *func; - List *argList; - FunctionCachePtr fcache; - - /* - * 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) - { - fcache = init_fcache(func->funcid, length(argList), - econtext->ecxt_per_query_memory); - func->func_fcache = fcache; - } - - return ExecMakeFunctionResult(fcache, 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... - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull) -{ - Node *clause; - Datum expr_value; - - clause = lfirst(notclause->args); - - expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); - - /* - * 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. - */ - return BoolGetDatum(!DatumGetBool(expr_value)); -} - -/* ---------------------------------------------------------------- - * ExecEvalOr - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull) -{ - List *clauses; - List *clause; - bool AnyNull; - Datum clause_value; - - clauses = orExpr->args; - AnyNull = false; - - /* - * If any of the clauses is TRUE, the OR result is TRUE regardless of - * the states of the rest of the clauses, so we can stop evaluating - * and return TRUE immediately. If none are TRUE and one or more is - * NULL, we return NULL; otherwise we return FALSE. This makes sense - * when you interpret NULL as "don't know": if we have a TRUE then the - * OR is TRUE even if we aren't sure about some of the other inputs. - * If all the known inputs are FALSE, but we have one or more "don't - * knows", then we have to report that we "don't know" what the OR's - * result should be --- perhaps one of the "don't knows" would have - * been TRUE if we'd known its value. Only when all the inputs are - * known to be FALSE can we state confidently that the OR's result is - * FALSE. - */ - foreach(clause, clauses) - { - clause_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, isNull, NULL); - - /* - * if we have a non-null true result, then return it. - */ - if (*isNull) - AnyNull = true; /* remember we got a null */ - else if (DatumGetBool(clause_value)) - return clause_value; - } - - /* AnyNull is true if at least one clause evaluated to NULL */ - *isNull = AnyNull; - return BoolGetDatum(false); -} - -/* ---------------------------------------------------------------- - * ExecEvalAnd - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) -{ - List *clauses; - List *clause; - bool AnyNull; - Datum clause_value; - - clauses = andExpr->args; - AnyNull = false; - - /* - * If any of the clauses is FALSE, the AND result is FALSE regardless - * of the states of the rest of the clauses, so we can stop evaluating - * and return FALSE immediately. If none are FALSE and one or more is - * NULL, we return NULL; otherwise we return TRUE. This makes sense - * when you interpret NULL as "don't know", using the same sort of - * reasoning as for OR, above. - */ - foreach(clause, clauses) - { - clause_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, isNull, NULL); - - /* - * if we have a non-null false result, then return it. - */ - if (*isNull) - AnyNull = true; /* remember we got a null */ - else if (!DatumGetBool(clause_value)) - return clause_value; - } - - /* AnyNull is true if at least one clause evaluated to NULL */ - *isNull = AnyNull; - return BoolGetDatum(!AnyNull); -} - -/* ---------------------------------------------------------------- - * ExecEvalCase - * - * Evaluate a CASE clause. Will have boolean expressions - * inside the WHEN clauses, and will have expressions - * for results. - * - thomas 1998-11-09 - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone) -{ - List *clauses; - List *clause; - Datum clause_value; - - clauses = caseExpr->args; - - /* - * we evaluate each of the WHEN clauses in turn, as soon as one is - * true we return the corresponding result. If none are true then we - * return the value of the default clause, or NULL if there is none. - */ - foreach(clause, clauses) - { - CaseWhen *wclause = lfirst(clause); - - clause_value = ExecEvalExpr(wclause->expr, - econtext, - isNull, - NULL); - - /* - * if we have a true test, then we return the result, since the - * case statement is satisfied. A NULL result from the test is - * not considered true. - */ - if (DatumGetBool(clause_value) && !*isNull) - { - return ExecEvalExpr(wclause->result, - econtext, - isNull, - isDone); - } - } - - if (caseExpr->defresult) - { - return ExecEvalExpr(caseExpr->defresult, - econtext, - isNull, - isDone); - } - - *isNull = true; - return (Datum) 0; -} - -/* ---------------------------------------------------------------- - * ExecEvalNullTest - * - * Evaluate a NullTest node. - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalNullTest(NullTest *ntest, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Datum result; - - result = ExecEvalExpr(ntest->arg, econtext, isNull, isDone); - switch (ntest->nulltesttype) - { - case IS_NULL: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(true); - } - else - return BoolGetDatum(false); - case IS_NOT_NULL: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(false); - } - else - return BoolGetDatum(true); - default: - elog(ERROR, "ExecEvalNullTest: unexpected nulltesttype %d", - (int) ntest->nulltesttype); - return (Datum) 0; /* keep compiler quiet */ - } -} - -/* ---------------------------------------------------------------- - * ExecEvalBooleanTest - * - * Evaluate a BooleanTest node. - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalBooleanTest(BooleanTest *btest, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Datum result; - - result = ExecEvalExpr(btest->arg, econtext, isNull, isDone); - switch (btest->booltesttype) - { - case IS_TRUE: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(false); - } - else if (DatumGetBool(result)) - return BoolGetDatum(true); - else - return BoolGetDatum(false); - case IS_NOT_TRUE: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(true); - } - else if (DatumGetBool(result)) - return BoolGetDatum(false); - else - return BoolGetDatum(true); - case IS_FALSE: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(false); - } - else if (DatumGetBool(result)) - return BoolGetDatum(false); - else - return BoolGetDatum(true); - case IS_NOT_FALSE: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(true); - } - else if (DatumGetBool(result)) - return BoolGetDatum(true); - else - return BoolGetDatum(false); - case IS_UNKNOWN: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(true); - } - else - return BoolGetDatum(false); - case IS_NOT_UNKNOWN: - if (*isNull) - { - *isNull = false; - return BoolGetDatum(false); - } - else - return BoolGetDatum(true); - default: - elog(ERROR, "ExecEvalBooleanTest: unexpected booltesttype %d", - (int) btest->booltesttype); - return (Datum) 0; /* keep compiler quiet */ - } -} - -/* ---------------------------------------------------------------- - * ExecEvalFieldSelect - * - * Evaluate a FieldSelect node. - * ---------------------------------------------------------------- - */ -static Datum -ExecEvalFieldSelect(FieldSelect *fselect, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Datum result; - TupleTableSlot *resSlot; - - result = ExecEvalExpr(fselect->arg, econtext, isNull, isDone); - if (*isNull) - return result; - resSlot = (TupleTableSlot *) DatumGetPointer(result); - Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot)); - result = heap_getattr(resSlot->val, - fselect->fieldnum, - resSlot->ttc_tupleDescriptor, - isNull); - return result; -} - -/* ---------------------------------------------------------------- - * ExecEvalExpr - * - * Recursively evaluate a targetlist or qualification expression. - * - * Inputs: - * expression: the expression tree to evaluate - * econtext: evaluation context information - * - * Outputs: - * return value: Datum value of result - * *isNull: set to TRUE if result is NULL (actual return value is - * meaningless if so); set to FALSE if non-null result - * *isDone: set to indicator of set-result status - * - * A caller that can only accept a singleton (non-set) result should pass - * NULL for isDone; if the expression computes a set result then an elog() - * error will be reported. If the caller does pass an isDone pointer then - * *isDone is set to one of these three states: - * ExprSingleResult singleton result (not a set) - * ExprMultipleResult return value is one element of a set - * ExprEndResult there are no more elements in the set - * When ExprMultipleResult is returned, the caller should invoke - * ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult - * is returned after the last real set element. For convenience isNull will - * always be set TRUE when ExprEndResult is returned, but this should not be - * taken as indicating a NULL element of the set. Note that these return - * conventions allow us to distinguish among a singleton NULL, a NULL element - * of a set, and an empty set. - * - * The caller should already have switched into the temporary memory - * context econtext->ecxt_per_tuple_memory. The convenience entry point - * ExecEvalExprSwitchContext() is provided for callers who don't prefer to - * do the switch in an outer loop. We do not do the switch here because - * it'd be a waste of cycles during recursive entries to ExecEvalExpr(). - * - * This routine is an inner loop routine and must be as fast as possible. - * ---------------------------------------------------------------- - */ -Datum -ExecEvalExpr(Node *expression, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Datum retDatum; - - /* Set default values for result flags: non-null, not a set result */ - *isNull = false; - if (isDone) - *isDone = ExprSingleResult; - - /* Is this still necessary? Doubtful... */ - if (expression == NULL) - { - *isNull = true; - return (Datum) 0; - } - - /* - * here we dispatch the work to the appropriate type of function given - * the type of our expression. - */ - switch (nodeTag(expression)) - { - case T_Var: - retDatum = ExecEvalVar((Var *) expression, econtext, isNull); - break; - case T_Const: - { - Const *con = (Const *) expression; - - retDatum = con->constvalue; - *isNull = con->constisnull; - break; - } - case T_Param: - retDatum = ExecEvalParam((Param *) expression, econtext, isNull); - break; - case T_Aggref: - retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull); - break; - case T_ArrayRef: - retDatum = ExecEvalArrayRef((ArrayRef *) expression, - econtext, - isNull, - isDone); - break; - case T_Expr: - { - Expr *expr = (Expr *) expression; - - switch (expr->opType) - { - case OP_EXPR: - retDatum = ExecEvalOper(expr, econtext, - isNull, isDone); - break; - case FUNC_EXPR: - retDatum = ExecEvalFunc(expr, econtext, - isNull, isDone); - break; - case OR_EXPR: - retDatum = ExecEvalOr(expr, econtext, isNull); - break; - case AND_EXPR: - retDatum = ExecEvalAnd(expr, econtext, isNull); - break; - case NOT_EXPR: - retDatum = ExecEvalNot(expr, econtext, isNull); - break; - case SUBPLAN_EXPR: - retDatum = ExecSubPlan((SubPlan *) expr->oper, - expr->args, econtext, - isNull); - break; - default: - elog(ERROR, "ExecEvalExpr: unknown expression type %d", - expr->opType); - retDatum = 0; /* keep compiler quiet */ - break; - } - break; - } - case T_FieldSelect: - retDatum = ExecEvalFieldSelect((FieldSelect *) expression, - econtext, - isNull, - isDone); - break; - case T_RelabelType: - retDatum = ExecEvalExpr(((RelabelType *) expression)->arg, - econtext, - isNull, - isDone); - break; - case T_CaseExpr: - retDatum = ExecEvalCase((CaseExpr *) expression, - econtext, - isNull, - isDone); - break; - case T_NullTest: - retDatum = ExecEvalNullTest((NullTest *) expression, - econtext, - isNull, - isDone); - break; - case T_BooleanTest: - retDatum = ExecEvalBooleanTest((BooleanTest *) expression, - econtext, - isNull, - isDone); - break; - - default: - elog(ERROR, "ExecEvalExpr: unknown expression type %d", - nodeTag(expression)); - retDatum = 0; /* keep compiler quiet */ - break; - } - - return retDatum; -} /* ExecEvalExpr() */ - - -/* - * Same as above, but get into the right allocation context explicitly. - */ -Datum -ExecEvalExprSwitchContext(Node *expression, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) -{ - Datum retDatum; - MemoryContext oldContext; - - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - retDatum = ExecEvalExpr(expression, econtext, isNull, isDone); - MemoryContextSwitchTo(oldContext); - return retDatum; -} - - -/* ---------------------------------------------------------------- - * ExecQual / ExecTargetList / ExecProject - * ---------------------------------------------------------------- - */ - -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - List *qlist; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - IncrProcessed(); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE - * result, we can stop evaluating and return FALSE --- the AND result - * must be FALSE. Also, if we find a NULL result when resultForNull - * is FALSE, we can stop and return FALSE --- the AND result must be - * FALSE or NULL in that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will - * happen when the AND result is indeed TRUE, or when the AND result - * is NULL (one or more NULL subresult, with all the rest TRUE) and - * the caller has specified resultForNull = TRUE. - */ - result = true; - - foreach(qlist, qual) - { - Node *clause = (Node *) lfirst(qlist); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - -/* - * Number of items in a tlist (including any resjunk items!) - */ -int -ExecTargetListLength(List *targetlist) -{ - int len = 0; - List *tl; - - foreach(tl, targetlist) - { - TargetEntry *curTle = (TargetEntry *) lfirst(tl); - - if (curTle->resdom != NULL) - len++; - else - len += curTle->fjoin->fj_nNodes; - } - return len; -} - -/* - * Number of items in a tlist, not including any resjunk items - */ -int -ExecCleanTargetListLength(List *targetlist) -{ - int len = 0; - List *tl; - - foreach(tl, targetlist) - { - TargetEntry *curTle = (TargetEntry *) lfirst(tl); - - if (curTle->resdom != NULL) - { - if (!curTle->resdom->resjunk) - len++; - } - else - len += curTle->fjoin->fj_nNodes; - } - return len; -} - -/* ---------------------------------------------------------------- - * ExecTargetList - * - * Evaluates a targetlist with respect to the current - * expression context and return a tuple. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * ---------------------------------------------------------------- - */ -static HeapTuple -ExecTargetList(List *targetlist, - int nodomains, - TupleDesc targettype, - Datum *values, - ExprContext *econtext, - ExprDoneCond *isDone) -{ - MemoryContext oldContext; - -#define NPREALLOCDOMAINS 64 - char nullsArray[NPREALLOCDOMAINS]; - bool fjIsNullArray[NPREALLOCDOMAINS]; - ExprDoneCond itemIsDoneArray[NPREALLOCDOMAINS]; - char *nulls; - bool *fjIsNull; - ExprDoneCond *itemIsDone; - List *tl; - TargetEntry *tle; - AttrNumber resind; - HeapTuple newTuple; - bool isNull; - bool haveDoneSets; - static struct tupleDesc NullTupleDesc; /* we assume this inits to - * zeroes */ - - /* - * debugging stuff - */ - EV_printf("ExecTargetList: tl is "); - EV_nodeDisplay(targetlist); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * There used to be some klugy and demonstrably broken code here that - * special-cased the situation where targetlist == NIL. Now we just - * fall through and return an empty-but-valid tuple. We do, however, - * have to cope with the possibility that targettype is NULL --- - * heap_formtuple won't like that, so pass a dummy descriptor with - * natts = 0 to deal with it. - */ - if (targettype == NULL) - targettype = &NullTupleDesc; - - /* - * allocate an array of char's to hold the "null" information only if - * we have a really large targetlist. otherwise we use the stack. - * - * We also allocate a bool array that is used to hold fjoin result state, - * and another array that holds the isDone status for each targetlist - * item. The isDone status is needed so that we can iterate, - * generating multiple tuples, when one or more tlist items return - * sets. (We expect the caller to call us again if we return: - * - * isDone = ExprMultipleResult.) - */ - if (nodomains > NPREALLOCDOMAINS) - { - nulls = (char *) palloc(nodomains * sizeof(char)); - fjIsNull = (bool *) palloc(nodomains * sizeof(bool)); - itemIsDone = (ExprDoneCond *) palloc(nodomains * sizeof(ExprDoneCond)); - } - else - { - nulls = nullsArray; - fjIsNull = fjIsNullArray; - itemIsDone = itemIsDoneArray; - } - - /* - * evaluate all the expressions in the target list - */ - - if (isDone) - *isDone = ExprSingleResult; /* until proven otherwise */ - - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - tle = lfirst(tl); - - if (tle->resdom != NULL) - { - resind = tle->resdom->resno - 1; - - values[resind] = ExecEvalExpr(tle->expr, - econtext, - &isNull, - &itemIsDone[resind]); - nulls[resind] = isNull ? 'n' : ' '; - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - elog(ERROR, "Set-valued function called in context that cannot accept a set"); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - else - { -#ifdef SETS_FIXED - 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); - - /* - * XXX this is wrong, but since fjoin code is completely - * broken anyway, I'm not going to worry about it now --- tgl - * 8/23/00 - */ - if (isDone && *isDone == ExprEndResult) - { - MemoryContextSwitchTo(oldContext); - newTuple = NULL; - goto exit; - } - - /* - * get the result from the inner node - */ - fjRes = (Resdom *) fjNode->fj_innerNode; - resind = fjRes->resno - 1; - values[resind] = results[0]; - nulls[resind] = fjIsNull[0] ? 'n' : ' '; - - /* - * Get results from all of the outer nodes - */ - for (curNode = 1; - curNode < nNodes; - curNode++, fjTlist = lnext(fjTlist)) - { - Node *outernode = lfirst(fjTlist); - - fjRes = (Resdom *) outernode->iterexpr; - resind = fjRes->resno - 1; - values[resind] = results[curNode]; - nulls[resind] = fjIsNull[curNode] ? 'n' : ' '; - } -#else - elog(ERROR, "ExecTargetList: fjoin nodes not currently supported"); -#endif - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is - * complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - newTuple = NULL; - goto exit; - } - else - { - /* - * We have some done and some undone sets. Restart the done - * ones so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - tle = lfirst(tl); - - if (tle->resdom != NULL) - { - resind = tle->resdom->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(tle->expr, - econtext, - &isNull, - &itemIsDone[resind]); - nulls[resind] = isNull ? 'n' : ' '; - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty - * set. Guess we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we - * still have to cycle the nonempty sets to completion, else - * resources will not be released from subplans etc. - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - tle = lfirst(tl); - - if (tle->resdom != NULL) - { - resind = tle->resdom->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - (void) ExecEvalExpr(tle->expr, - econtext, - &isNull, - &itemIsDone[resind]); - } - } - } - - MemoryContextSwitchTo(oldContext); - newTuple = NULL; - goto exit; - } - } - } - - /* - * form the new result tuple (in the caller's memory context!) - */ - MemoryContextSwitchTo(oldContext); - - newTuple = (HeapTuple) heap_formtuple(targettype, values, nulls); - -exit: - - /* - * free the status arrays if we palloc'd them - */ - if (nodomains > NPREALLOCDOMAINS) - { - pfree(nulls); - pfree(fjIsNull); - pfree(itemIsDone); - } - - return newTuple; -} - -/* ---------------------------------------------------------------- - * ExecProject - * - * projects a tuple based on 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, ExprDoneCond *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; - - /* - * form a new result tuple (if possible --- result can be NULL) - */ - newTuple = ExecTargetList(targetlist, - len, - tupType, - tupValue, - econtext, - isDone); - - /* - * store the tuple in the projection slot and return the slot. - */ - return 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 deleted file mode 100644 index e8df691c8ea..00000000000 --- a/src/backend/executor/execScan.c +++ /dev/null @@ -1,147 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.20 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <sys/types.h> -#include <sys/file.h> - -#include "executor/executor.h" -#include "miscadmin.h" -#include "utils/memutils.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 - * responsible 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. - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecScan(Scan *node, - ExecScanAccessMtd accessMtd) /* function returning a tuple */ -{ - CommonScanState *scanstate; - EState *estate; - ExprContext *econtext; - List *qual; - ExprDoneCond isDone; - TupleTableSlot *resultSlot; - - /* - * Fetch data from node - */ - estate = node->plan.state; - scanstate = node->scanstate; - econtext = scanstate->cstate.cs_ExprContext; - qual = node->plan.qual; - - /* - * Check to see if we're still projecting out tuples from a previous - * scan tuple (because there is a function-returning-set in the - * projection expressions). If so, try to project another one. - */ - if (scanstate->cstate.cs_TupFromTlist) - { - resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return resultSlot; - /* Done with that source tuple... */ - scanstate->cstate.cs_TupFromTlist = false; - } - - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. Note this can't - * happen until we're done projecting out tuples from a scan tuple. - */ - ResetExprContext(econtext); - - /* - * get a tuple from the access method loop until we obtain a tuple - * which passes the qualification. - */ - for (;;) - { - TupleTableSlot *slot; - - CHECK_FOR_INTERRUPTS(); - - slot = (*accessMtd) (node); - - /* - * if the slot returned by the accessMtd contains NULL, then it - * means there is nothing more to scan so we just return an empty - * slot, being careful to use the projection result slot so it has - * correct tupleDesc. - */ - if (TupIsNull(slot)) - { - return ExecStoreTuple(NULL, - scanstate->cstate.cs_ProjInfo->pi_slot, - InvalidBuffer, - true); - } - - /* - * place the current tuple into the expr context - */ - econtext->ecxt_scantuple = slot; - - /* - * check that the current tuple satisfies the qual-clause - * - * check for non-nil qual here to avoid a function call to ExecQual() - * when the qual is nil ... saves only a few cycles, but they add - * up ... - */ - if (!qual || ExecQual(qual, econtext, false)) - { - /* - * Found a satisfactory scan tuple. - * - * Form a projection tuple, store it in the result tuple slot and - * return it --- unless we find we can project no tuples from - * this scan tuple, in which case continue scan. - */ - resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone); - if (isDone != ExprEndResult) - { - scanstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult); - return resultSlot; - } - } - - /* - * Tuple fails qual, so free per-tuple memory and try again. - */ - ResetExprContext(econtext); - } -} diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c deleted file mode 100644 index 86a16ddb04f..00000000000 --- a/src/backend/executor/execTuples.c +++ /dev/null @@ -1,795 +0,0 @@ -/*------------------------------------------------------------------------- - * - * 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.). - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.53 2002/06/20 20:29:27 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * - * TABLE CREATE/DELETE - * ExecCreateTupleTable - create a new tuple table - * ExecDropTupleTable - destroy a table - * - * SLOT RESERVATION - * 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 - * ExecSetSlotDescriptor - set a slot's tuple descriptor - * ExecSetSlotDescriptorIsNew - diddle the slot-desc-is-new flag - * - * SLOT STATUS PREDICATES - * TupIsNull - true when slot contains no tuple(Macro) - * - * CONVENIENCE INITIALIZATION ROUTINES - * ExecInitResultTupleSlot \ convenience routines to initialize - * ExecInitScanTupleSlot \ the various tuple slots for nodes - * ExecInitExtraTupleSlot / which store copies of tuples. - * ExecInitNullTupleSlot / - * - * Routines that probably belong somewhere else: - * 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 ExecProject() 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 ExecDropTupleTable() 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 "postgres.h" - -#include "funcapi.h" -#include "access/heapam.h" -#include "catalog/pg_type.h" -#include "executor/executor.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; -} - -/* -------------------------------- - * ExecDropTupleTable - * - * This frees the storage used by the tuple table itself - * and optionally frees the contents of the table also. - * It is expected that this routine be called by EndPlan(). - * -------------------------------- - */ -void -ExecDropTupleTable(TupleTable table, /* tuple table */ - bool shouldFree) /* true if we should free slot - * contents */ -{ - int next; /* next available 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 and drop - * refcounts of any referenced buffers, if that's what the caller - * wants. (There is probably no good reason for the caller ever not - * to want it!) - */ - if (shouldFree) - { - for (i = 0; i < next; i++) - { - ExecClearTuple(&array[i]); - if (array[i].ttc_shouldFreeDesc && - array[i].ttc_tupleDescriptor != NULL) - FreeTupleDesc(array[i].ttc_tupleDescriptor); - } - } - - /* - * 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 * -ExecAllocTableSlot(TupleTable table) -{ - int slotnum; /* new slot number */ - TupleTableSlot *slot; - - /* - * 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. Fail if it does. - * -mer 4 Aug 1992 - */ - if (table->next >= table->size) - elog(ERROR, "Plan requires more slots than are available" - "\n\tsend 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++; - - slot = &(table->array[slotnum]); - - /* Make sure the allocated slot is valid (and empty) */ - slot->type = T_TupleTableSlot; - slot->val = (HeapTuple) NULL; - slot->ttc_shouldFree = true; - slot->ttc_descIsNew = true; - slot->ttc_shouldFreeDesc = true; - slot->ttc_tupleDescriptor = (TupleDesc) NULL; - slot->ttc_buffer = InvalidBuffer; - - return slot; -} - -/* -------------------------------- - * MakeTupleTableSlot - * - * This routine makes an empty standalone TupleTableSlot. - * It really shouldn't exist, but there are a few places - * that do this, so we may as well centralize the knowledge - * of what's in one ... - * -------------------------------- - */ -TupleTableSlot * -MakeTupleTableSlot(void) -{ - TupleTableSlot *slot = makeNode(TupleTableSlot); - - /* This should match ExecAllocTableSlot() */ - slot->val = (HeapTuple) NULL; - slot->ttc_shouldFree = true; - slot->ttc_descIsNew = true; - slot->ttc_shouldFreeDesc = true; - slot->ttc_tupleDescriptor = (TupleDesc) NULL; - slot->ttc_buffer = InvalidBuffer; - - return slot; -} - -/* ---------------------------------------------------------------- - * tuple table slot accessor functions - * ---------------------------------------------------------------- - */ - -/* -------------------------------- - * ExecStoreTuple - * - * This function is used to store a tuple into a specified - * slot in the tuple table. - * - * tuple: tuple to store - * slot: slot to store it in - * buffer: disk buffer if tuple is in a disk page, else InvalidBuffer - * shouldFree: true if ExecClearTuple should pfree() the tuple - * when done with it - * - * If 'buffer' is not InvalidBuffer, the tuple table code acquires a pin - * on the buffer which is held until the slot is cleared, so that the tuple - * won't go away on us. - * - * shouldFree is normally set 'true' for tuples constructed on-the-fly. - * It must always be 'false' for tuples that are stored in disk pages, - * since we don't want to try to pfree those. - * - * Another case where it is 'false' is when the referenced tuple is held - * in a tuple table slot belonging to a lower-level executor Proc node. - * In this case the lower-level slot retains ownership and responsibility - * for eventually releasing the tuple. When this method is used, we must - * be certain that the upper-level Proc node will lose interest in the tuple - * sooner than the lower-level one does! If you're not certain, copy the - * lower-level tuple with heap_copytuple and let the upper-level table - * slot assume ownership of the copy! - * - * Return value is just the passed-in slot pointer. - * -------------------------------- - */ -TupleTableSlot * -ExecStoreTuple(HeapTuple tuple, - TupleTableSlot *slot, - Buffer buffer, - bool shouldFree) -{ - /* - * sanity checks - */ - Assert(slot != NULL); - /* passing shouldFree=true for a tuple on a disk page is not sane */ - Assert(BufferIsValid(buffer) ? (!shouldFree) : true); - - /* clear out any old contents of the slot */ - 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; - - /* - * If tuple is on a disk page, keep the page pinned as long as we hold - * a pointer into it. - */ - if (BufferIsValid(buffer)) - IncrBufferRefCount(buffer); - - return slot; -} - -/* -------------------------------- - * ExecClearTuple - * - * This function is used to clear out a slot in the tuple table. - * - * NB: only the tuple is cleared, not the tuple descriptor (if any). - * -------------------------------- - */ -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) - heap_freetuple(oldtuple); - - slot->val = (HeapTuple) NULL; - - slot->ttc_shouldFree = true; /* probably useless code... */ - - /* - * Drop the pin on the referenced buffer, if there is one. - */ - if (BufferIsValid(slot->ttc_buffer)) - ReleaseBuffer(slot->ttc_buffer); - - slot->ttc_buffer = InvalidBuffer; - - return slot; -} - -/* -------------------------------- - * ExecSetSlotDescriptor - * - * This function is used to set the tuple descriptor associated - * with the slot's tuple. - * -------------------------------- - */ -void -ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ - TupleDesc tupdesc, /* new tuple descriptor */ - bool shouldFree) /* is desc owned by slot? */ -{ - if (slot->ttc_shouldFreeDesc && - slot->ttc_tupleDescriptor != NULL) - FreeTupleDesc(slot->ttc_tupleDescriptor); - - slot->ttc_tupleDescriptor = tupdesc; - slot->ttc_shouldFreeDesc = shouldFree; -} - -/* -------------------------------- - * 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; -} - -/* ---------------------------------------------------------------- - * tuple table slot status predicates - * ---------------------------------------------------------------- - */ - -/* ---------------------------------------------------------------- - * convenience initialization routines - * ---------------------------------------------------------------- - */ -/* -------------------------------- - * ExecInit{Result,Scan,Extra}TupleSlot - * - * These are convenience routines to initialize the specified slot - * in nodes inheriting the appropriate state. ExecInitExtraTupleSlot - * is used for initializing special-purpose slots. - * -------------------------------- - */ -#define INIT_SLOT_DEFS \ - TupleTable tupleTable; \ - TupleTableSlot* slot - -#define INIT_SLOT_ALLOC \ - tupleTable = (TupleTable) estate->es_tupleTable; \ - slot = ExecAllocTableSlot(tupleTable); - -/* ---------------- - * ExecInitResultTupleSlot - * ---------------- - */ -void -ExecInitResultTupleSlot(EState *estate, CommonState *commonstate) -{ - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - commonstate->cs_ResultTupleSlot = slot; -} - -/* ---------------- - * ExecInitScanTupleSlot - * ---------------- - */ -void -ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate) -{ - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - commonscanstate->css_ScanTupleSlot = slot; -} - -/* ---------------- - * ExecInitExtraTupleSlot - * ---------------- - */ -TupleTableSlot * -ExecInitExtraTupleSlot(EState *estate) -{ - INIT_SLOT_DEFS; - INIT_SLOT_ALLOC; - return slot; -} - -/* ---------------- - * ExecInitNullTupleSlot - * - * Build a slot containing an all-nulls tuple of the given type. - * This is used as a substitute for an input tuple when performing an - * outer join. - * ---------------- - */ -TupleTableSlot * -ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) -{ - TupleTableSlot *slot = ExecInitExtraTupleSlot(estate); - - /* - * Since heap_getattr() will treat attributes beyond a tuple's t_natts - * as being NULL, we can make an all-nulls tuple just by making it be - * of zero length. However, the slot descriptor must match the real - * tupType. - */ - HeapTuple nullTuple; - Datum values[1]; - char nulls[1]; - static struct tupleDesc NullTupleDesc; /* we assume this inits to - * zeroes */ - - ExecSetSlotDescriptor(slot, tupType, false); - - nullTuple = heap_formtuple(&NullTupleDesc, values, nulls); - - return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true); -} - -/* ---------------------------------------------------------------- - * ExecTypeFromTL - * - * Generate a tuple descriptor for the result tuple of a targetlist. - * Note that resjunk columns, if any, are included in the result. - * - * Currently there are about 4 different places where we create - * TupleDescriptors. They should all be merged, or perhaps - * be rewritten to call BuildDesc(). - * ---------------------------------------------------------------- - */ -TupleDesc -ExecTypeFromTL(List *targetList) -{ - List *tlitem; - 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); - - /* - * scan list, generate type info for each entry - */ - foreach(tlitem, targetList) - { - TargetEntry *tle = lfirst(tlitem); - - if (tle->resdom != NULL) - { - resdom = tle->resdom; - restype = resdom->restype; - - TupleDescInitEntry(typeInfo, - resdom->resno, - resdom->resname, - restype, - resdom->restypmod, - 0, - false); - -#ifdef NOT_USED - ExecSetTypeInfo(resdom->resno - 1, - typeInfo, - (Oid) restype, - resdom->resno, - resdom->reslen, - NameStr(*resdom->resname), - get_typbyval(restype), - get_typalign(restype)); -#endif - } - else - { - /* XXX this branch looks fairly broken ... tgl 12/2000 */ - Resdom *fjRes; - List *fjTlistP; - List *fjList = lfirst(tlitem); - -#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, - restype, - fjRes->restypmod, - 0, - false); -#ifdef NOT_USED - ExecSetTypeInfo(fjRes->resno - 1, - typeInfo, - (Oid) restype, - fjRes->resno, - fjRes->reslen, - (char *) fjRes->resname, - get_typbyval(restype), - get_typalign(restype)); -#endif - - foreach(fjTlistP, lnext(fjList)) - { - TargetEntry *fjTle = lfirst(fjTlistP); - - fjRes = fjTle->resdom; - - TupleDescInitEntry(typeInfo, - fjRes->resno, - fjRes->resname, - restype, - fjRes->restypmod, - 0, - false); - -#ifdef NOT_USED - ExecSetTypeInfo(fjRes->resno - 1, - typeInfo, - (Oid) fjRes->restype, - fjRes->resno, - fjRes->reslen, - (char *) fjRes->resname, - get_typbyval(fjRes->restype), - get_typalign(fjRes->restype)); -#endif - } - } - } - - return typeInfo; -} - -/* - * TupleDescGetSlot - Initialize a slot based on the supplied - * tupledesc - */ -TupleTableSlot * -TupleDescGetSlot(TupleDesc tupdesc) -{ - TupleTableSlot *slot; - - /* Make a standalone slot */ - slot = MakeTupleTableSlot(); - - /* Bind the tuple description to the slot */ - ExecSetSlotDescriptor(slot, tupdesc, true); - - /* Return the slot */ - return slot; -} - -/* - * TupleDescGetAttInMetadata - Get a pointer to AttInMetadata based on the - * supplied TupleDesc. AttInMetadata can be used in conjunction with C strings - * to produce a properly formed tuple. - */ -AttInMetadata * -TupleDescGetAttInMetadata(TupleDesc tupdesc) -{ - int natts; - int i; - Oid atttypeid; - Oid attinfuncid; - Oid attelem; - FmgrInfo *attinfuncinfo; - Oid *attelems; - int4 *atttypmods; - AttInMetadata *attinmeta; - - attinmeta = (AttInMetadata *) palloc(sizeof(AttInMetadata)); - natts = tupdesc->natts; - - /* - * Gather info needed later to call the "in" function for each attribute - */ - attinfuncinfo = (FmgrInfo *) palloc(natts * sizeof(FmgrInfo)); - attelems = (Oid *) palloc(natts * sizeof(Oid)); - atttypmods = (int4 *) palloc(natts * sizeof(int4)); - - for (i = 0; i < natts; i++) - { - atttypeid = tupdesc->attrs[i]->atttypid; - get_type_metadata(atttypeid, &attinfuncid, &attelem); - - fmgr_info(attinfuncid, &attinfuncinfo[i]); - attelems[i] = attelem; - atttypmods[i] = tupdesc->attrs[i]->atttypmod; - } - attinmeta->tupdesc = tupdesc; - attinmeta->attinfuncs = attinfuncinfo; - attinmeta->attelems = attelems; - attinmeta->atttypmods = atttypmods; - - return attinmeta; -} - -/* - * BuildTupleFromCStrings - build a HeapTuple given user data in C string form. - * values is an array of C strings, one for each attribute of the return tuple. - */ -HeapTuple -BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) -{ - TupleDesc tupdesc; - int natts; - HeapTuple tuple; - char *nulls; - int i; - Datum *dvalues; - FmgrInfo attinfuncinfo; - Oid attelem; - int4 atttypmod; - - tupdesc = attinmeta->tupdesc; - natts = tupdesc->natts; - - dvalues = (Datum *) palloc(natts * sizeof(Datum)); - - /* Call the "in" function for each attribute */ - for (i = 0; i < natts; i++) - { - if (values[i] != NULL) - { - attinfuncinfo = attinmeta->attinfuncs[i]; - attelem = attinmeta->attelems[i]; - atttypmod = attinmeta->atttypmods[i]; - - dvalues[i] = FunctionCall3(&attinfuncinfo, CStringGetDatum(values[i]), - ObjectIdGetDatum(attelem), - Int32GetDatum(atttypmod)); - } - else - dvalues[i] = PointerGetDatum(NULL); - } - - /* - * Form a tuple - */ - nulls = (char *) palloc(natts * sizeof(char)); - for (i = 0; i < natts; i++) - { - if (DatumGetPointer(dvalues[i]) != NULL) - nulls[i] = ' '; - else - nulls[i] = 'n'; - } - tuple = heap_formtuple(tupdesc, dvalues, nulls); - - return tuple; -} - diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c deleted file mode 100644 index 122417efd61..00000000000 --- a/src/backend/executor/execUtils.c +++ /dev/null @@ -1,793 +0,0 @@ -/*------------------------------------------------------------------------- - * - * execUtils.c - * miscellaneous executor utility routines - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.83 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecAssignExprContext Common code for plan node init routines. - * - * ExecOpenIndices \ - * ExecCloseIndices | referenced by InitPlan, EndPlan, - * ExecInsertIndexTuples / ExecAppend, ExecReplace - * - * RegisterExprContextCallback Register function shutdown callback - * UnregisterExprContextCallback Deregister function shutdown callback - * - * NOTES - * This file has traditionally been the place to stick misc. - * executor support stuff that doesn't really go anyplace else. - * - */ - -#include "postgres.h" - -#include "access/genam.h" -#include "access/heapam.h" -#include "catalog/catname.h" -#include "catalog/index.h" -#include "catalog/catalog.h" -#include "catalog/pg_index.h" -#include "executor/execdebug.h" -#include "miscadmin.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/memutils.h" -#include "utils/relcache.h" -#include "utils/syscache.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. */ - - -static void ShutdownExprContext(ExprContext *econtext); - -/* ---------------------------------------------------------------- - * statistic functions - * ---------------------------------------------------------------- - */ - -/* ---------------------------------------------------------------- - * ResetTupleCount - * ---------------------------------------------------------------- - */ -#ifdef NOT_USED -void -ResetTupleCount(void) -{ - NTupleProcessed = 0; - NTupleRetrieved = 0; - NTupleAppended = 0; - NTupleDeleted = 0; - NTupleReplaced = 0; - NIndexTupleProcessed = 0; -} -#endif - -/* ---------------------------------------------------------------- - * PrintTupleCount - * ---------------------------------------------------------------- - */ -#ifdef NOT_USED -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"); -} -#endif - -/* ---------------------------------------------------------------- - * miscellaneous node-init support functions - * ---------------------------------------------------------------- - */ - -/* ---------------- - * ExecAssignExprContext - * - * This initializes the ExprContext field. It is only necessary - * to do this for nodes which use ExecQual or ExecProject - * because those routines depend on econtext. Other nodes that - * don't have to evaluate expressions don't need to do this. - * - * Note: we assume CurrentMemoryContext is the correct per-query context. - * This should be true during plan node initialization. - * ---------------- - */ -void -ExecAssignExprContext(EState *estate, CommonState *commonstate) -{ - ExprContext *econtext = makeNode(ExprContext); - - econtext->ecxt_scantuple = NULL; - econtext->ecxt_innertuple = NULL; - econtext->ecxt_outertuple = NULL; - econtext->ecxt_per_query_memory = CurrentMemoryContext; - - /* - * Create working memory for expression evaluation in this context. - */ - econtext->ecxt_per_tuple_memory = - AllocSetContextCreate(CurrentMemoryContext, - "PlanExprContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - econtext->ecxt_param_exec_vals = estate->es_param_exec_vals; - econtext->ecxt_param_list_info = estate->es_param_list_info; - econtext->ecxt_aggvalues = NULL; - econtext->ecxt_aggnulls = NULL; - econtext->ecxt_callbacks = NULL; - - commonstate->cs_ExprContext = econtext; -} - -/* ---------------- - * MakeExprContext - * - * Build an expression context for use outside normal plan-node cases. - * A fake scan-tuple slot can be supplied (pass NULL if not needed). - * A memory context sufficiently long-lived to use as fcache context - * must be supplied as well. - * ---------------- - */ -ExprContext * -MakeExprContext(TupleTableSlot *slot, - MemoryContext queryContext) -{ - ExprContext *econtext = makeNode(ExprContext); - - econtext->ecxt_scantuple = slot; - econtext->ecxt_innertuple = NULL; - econtext->ecxt_outertuple = NULL; - econtext->ecxt_per_query_memory = queryContext; - - /* - * We make the temporary context a child of current working context, - * not of the specified queryContext. This seems reasonable but I'm - * not totally sure about it... - * - * Expression contexts made via this routine typically don't live long - * enough to get reset, so specify a minsize of 0. That avoids - * alloc'ing any memory in the common case where expr eval doesn't use - * any. - */ - econtext->ecxt_per_tuple_memory = - AllocSetContextCreate(CurrentMemoryContext, - "TempExprContext", - 0, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - econtext->ecxt_param_exec_vals = NULL; - econtext->ecxt_param_list_info = NULL; - econtext->ecxt_aggvalues = NULL; - econtext->ecxt_aggnulls = NULL; - econtext->ecxt_callbacks = NULL; - - return econtext; -} - -/* - * Free an ExprContext made by MakeExprContext, including the temporary - * context used for expression evaluation. Note this will cause any - * pass-by-reference expression result to go away! - */ -void -FreeExprContext(ExprContext *econtext) -{ - /* Call any registered callbacks */ - ShutdownExprContext(econtext); - /* And clean up the memory used */ - MemoryContextDelete(econtext->ecxt_per_tuple_memory); - pfree(econtext); -} - -/* - * Build a per-output-tuple ExprContext for an EState. - * - * This is normally invoked via GetPerTupleExprContext() macro. - */ -ExprContext * -MakePerTupleExprContext(EState *estate) -{ - if (estate->es_per_tuple_exprcontext == NULL) - { - MemoryContext oldContext; - - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - estate->es_per_tuple_exprcontext = - MakeExprContext(NULL, estate->es_query_cxt); - MemoryContextSwitchTo(oldContext); - } - return estate->es_per_tuple_exprcontext; -} - -/* ---------------------------------------------------------------- - * Result slot tuple type and ProjectionInfo support - * ---------------------------------------------------------------- - */ - -/* ---------------- - * ExecAssignResultType - * ---------------- - */ -void -ExecAssignResultType(CommonState *commonstate, - TupleDesc tupDesc, bool shouldFree) -{ - TupleTableSlot *slot = commonstate->cs_ResultTupleSlot; - - ExecSetSlotDescriptor(slot, tupDesc, shouldFree); -} - -/* ---------------- - * ExecAssignResultTypeFromOuterPlan - * ---------------- - */ -void -ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate) -{ - Plan *outerPlan; - TupleDesc tupDesc; - - outerPlan = outerPlan(node); - tupDesc = ExecGetTupType(outerPlan); - - ExecAssignResultType(commonstate, tupDesc, false); -} - -/* ---------------- - * ExecAssignResultTypeFromTL - * ---------------- - */ -void -ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate) -{ - TupleDesc tupDesc; - - tupDesc = ExecTypeFromTL(node->targetlist); - ExecAssignResultType(commonstate, tupDesc, true); -} - -/* ---------------- - * ExecGetResultType - * ---------------- - */ -TupleDesc -ExecGetResultType(CommonState *commonstate) -{ - TupleTableSlot *slot = commonstate->cs_ResultTupleSlot; - - return slot->ttc_tupleDescriptor; -} - -/* ---------------- - * 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; -} - -/* ---------------- - * ExecFreeExprContext - * ---------------- - */ -void -ExecFreeExprContext(CommonState *commonstate) -{ - ExprContext *econtext; - - /* - * get expression context. if NULL then this node has none so we just - * return. - */ - econtext = commonstate->cs_ExprContext; - if (econtext == NULL) - return; - - /* - * clean up any registered callbacks - */ - ShutdownExprContext(econtext); - - /* - * clean up memory used. - */ - MemoryContextDelete(econtext->ecxt_per_tuple_memory); - pfree(econtext); - commonstate->cs_ExprContext = 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; -} - -/* ---------------- - * ExecAssignScanType - * ---------------- - */ -void -ExecAssignScanType(CommonScanState *csstate, - TupleDesc tupDesc, bool shouldFree) -{ - TupleTableSlot *slot = csstate->css_ScanTupleSlot; - - ExecSetSlotDescriptor(slot, tupDesc, shouldFree); -} - -/* ---------------- - * ExecAssignScanTypeFromOuterPlan - * ---------------- - */ -void -ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate) -{ - Plan *outerPlan; - TupleDesc tupDesc; - - outerPlan = outerPlan(node); - tupDesc = ExecGetTupType(outerPlan); - - ExecAssignScanType(csstate, tupDesc, false); -} - - -/* ---------------------------------------------------------------- - * ExecInsertIndexTuples support - * ---------------------------------------------------------------- - */ - -/* ---------------------------------------------------------------- - * ExecOpenIndices - * - * Find the indices associated with a result relation, open them, - * and save information about them in the result ResultRelInfo. - * - * At entry, caller has already opened and locked - * resultRelInfo->ri_RelationDesc. - * - * This used to be horribly ugly code, and slow too because it - * did a sequential scan of pg_index. Now we rely on the relcache - * to cache a list of the OIDs of the indices associated with any - * specific relation, and we use the pg_index syscache to get the - * entries we need from pg_index. - * ---------------------------------------------------------------- - */ -void -ExecOpenIndices(ResultRelInfo *resultRelInfo) -{ - Relation resultRelation = resultRelInfo->ri_RelationDesc; - List *indexoidlist, - *indexoidscan; - int len, - i; - RelationPtr relationDescs; - IndexInfo **indexInfoArray; - - resultRelInfo->ri_NumIndices = 0; - - /* checks for disabled indexes */ - if (!RelationGetForm(resultRelation)->relhasindex) - return; - if (IsIgnoringSystemIndexes() && - IsSystemRelation(resultRelation)) - return; - - /* - * Get cached list of index OIDs - */ - indexoidlist = RelationGetIndexList(resultRelation); - len = length(indexoidlist); - if (len == 0) - return; - - /* - * allocate space for result arrays - */ - relationDescs = (RelationPtr) palloc(len * sizeof(Relation)); - indexInfoArray = (IndexInfo **) palloc(len * sizeof(IndexInfo *)); - - resultRelInfo->ri_NumIndices = len; - resultRelInfo->ri_IndexRelationDescs = relationDescs; - resultRelInfo->ri_IndexRelationInfo = indexInfoArray; - - /* - * For each index, open the index relation and save pg_index info. - */ - i = 0; - foreach(indexoidscan, indexoidlist) - { - Oid indexOid = lfirsti(indexoidscan); - Relation indexDesc; - IndexInfo *ii; - - /* - * Open (and lock, if necessary) the index relation - * - * If the index AM is not safe for concurrent updates, obtain an - * exclusive lock on the index to lock out other updaters as well - * as readers (index_beginscan places AccessShareLock). We will - * release this lock in ExecCloseIndices. - * - * If the index AM supports concurrent updates, we obtain no lock - * here at all, which is a tad weird, but safe since any critical - * operation on the index (like deleting it) will acquire - * exclusive lock on the parent table. Perhaps someday we should - * acquire RowExclusiveLock on the index here? - * - * If there are multiple not-concurrent-safe indexes, all backends - * must lock the indexes in the same order or we will get - * deadlocks here during concurrent updates. This is guaranteed - * by RelationGetIndexList(), which promises to return the index - * list in OID order. - */ - indexDesc = index_open(indexOid); - - if (!indexDesc->rd_am->amconcurrent) - LockRelation(indexDesc, AccessExclusiveLock); - - /* - * extract index key information from the index's pg_index tuple - */ - ii = BuildIndexInfo(indexDesc->rd_index); - - relationDescs[i] = indexDesc; - indexInfoArray[i] = ii; - i++; - } - - freeList(indexoidlist); -} - -/* ---------------------------------------------------------------- - * ExecCloseIndices - * - * Close the index relations stored in resultRelInfo - * ---------------------------------------------------------------- - */ -void -ExecCloseIndices(ResultRelInfo *resultRelInfo) -{ - int i; - int numIndices; - RelationPtr indexDescs; - - numIndices = resultRelInfo->ri_NumIndices; - indexDescs = resultRelInfo->ri_IndexRelationDescs; - - for (i = 0; i < numIndices; i++) - { - if (indexDescs[i] == NULL) - continue; - - /* Drop lock, if one was acquired by ExecOpenIndices */ - if (!indexDescs[i]->rd_am->amconcurrent) - UnlockRelation(indexDescs[i], AccessExclusiveLock); - - index_close(indexDescs[i]); - } - - /* - * XXX should free indexInfo array here too. - */ -} - -/* ---------------------------------------------------------------- - * 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, - bool is_vacuum) -{ - HeapTuple heapTuple; - ResultRelInfo *resultRelInfo; - int i; - int numIndices; - RelationPtr relationDescs; - Relation heapRelation; - TupleDesc heapDescriptor; - IndexInfo **indexInfoArray; - ExprContext *econtext; - Datum datum[INDEX_MAX_KEYS]; - char nullv[INDEX_MAX_KEYS]; - - heapTuple = slot->val; - - /* - * Get information from the result relation info structure. - */ - resultRelInfo = estate->es_result_relation_info; - numIndices = resultRelInfo->ri_NumIndices; - relationDescs = resultRelInfo->ri_IndexRelationDescs; - indexInfoArray = resultRelInfo->ri_IndexRelationInfo; - heapRelation = resultRelInfo->ri_RelationDesc; - heapDescriptor = RelationGetDescr(heapRelation); - - /* - * We will use the EState's per-tuple context for evaluating - * predicates and functional-index functions (creating it if it's not - * already there). - */ - econtext = GetPerTupleExprContext(estate); - - /* Arrange for econtext's scan tuple to be the tuple under test */ - econtext->ecxt_scantuple = slot; - - /* - * for each index, form and insert the index tuple - */ - for (i = 0; i < numIndices; i++) - { - IndexInfo *indexInfo; - List *predicate; - InsertIndexResult result; - - if (relationDescs[i] == NULL) - continue; - - indexInfo = indexInfoArray[i]; - predicate = indexInfo->ii_Predicate; - if (predicate != NIL) - { - /* Skip this index-update if the predicate isn't satisfied */ - if (!ExecQual(predicate, econtext, false)) - continue; - } - - /* - * FormIndexDatum fills in its datum and null parameters with - * attribute information taken from the given heap tuple. - */ - FormIndexDatum(indexInfo, - heapTuple, - heapDescriptor, - econtext->ecxt_per_tuple_memory, - datum, - nullv); - - /* - * The index AM does the rest. Note we suppress unique-index - * checks if we are being called from VACUUM, since VACUUM may - * need to move dead tuples that have the same keys as live ones. - */ - result = index_insert(relationDescs[i], /* index relation */ - datum, /* array of heaptuple Datums */ - nullv, /* info on nulls */ - &(heapTuple->t_self), /* tid of heap tuple */ - heapRelation, - relationDescs[i]->rd_uniqueindex && !is_vacuum); - - /* - * keep track of index inserts for debugging - */ - IncrIndexInserted(); - - if (result) - pfree(result); - } -} - -void -SetChangedParamList(Plan *node, List *newchg) -{ - List *nl; - - foreach(nl, newchg) - { - int paramId = lfirsti(nl); - - /* if this node doesn't depend on a param ... */ - if (!intMember(paramId, node->extParam) && - !intMember(paramId, node->locParam)) - continue; - /* if this param is already in list of changed ones ... */ - if (intMember(paramId, node->chgParam)) - continue; - /* else - add this param to the list */ - node->chgParam = lappendi(node->chgParam, paramId); - } -} - -/* - * Register a shutdown callback in an ExprContext. - * - * Shutdown callbacks will be called (in reverse order of registration) - * when the ExprContext is deleted or rescanned. This provides a hook - * for functions called in the context to do any cleanup needed --- it's - * particularly useful for functions returning sets. Note that the - * callback will *not* be called in the event that execution is aborted - * by an error. - */ -void -RegisterExprContextCallback(ExprContext *econtext, - ExprContextCallbackFunction function, - Datum arg) -{ - ExprContext_CB *ecxt_callback; - - /* Save the info in appropriate memory context */ - ecxt_callback = (ExprContext_CB *) - MemoryContextAlloc(econtext->ecxt_per_query_memory, - sizeof(ExprContext_CB)); - - ecxt_callback->function = function; - ecxt_callback->arg = arg; - - /* link to front of list for appropriate execution order */ - ecxt_callback->next = econtext->ecxt_callbacks; - econtext->ecxt_callbacks = ecxt_callback; -} - -/* - * Deregister a shutdown callback in an ExprContext. - * - * Any list entries matching the function and arg will be removed. - * This can be used if it's no longer necessary to call the callback. - */ -void -UnregisterExprContextCallback(ExprContext *econtext, - ExprContextCallbackFunction function, - Datum arg) -{ - ExprContext_CB **prev_callback; - ExprContext_CB *ecxt_callback; - - prev_callback = &econtext->ecxt_callbacks; - - while ((ecxt_callback = *prev_callback) != NULL) - { - if (ecxt_callback->function == function && ecxt_callback->arg == arg) - { - *prev_callback = ecxt_callback->next; - pfree(ecxt_callback); - } - else - { - prev_callback = &ecxt_callback->next; - } - } -} - -/* - * Call all the shutdown callbacks registered in an ExprContext. - * - * The callback list is emptied (important in case this is only a rescan - * reset, and not deletion of the ExprContext). - */ -static void -ShutdownExprContext(ExprContext *econtext) -{ - ExprContext_CB *ecxt_callback; - - /* - * Call each callback function in reverse registration order. - */ - while ((ecxt_callback = econtext->ecxt_callbacks) != NULL) - { - econtext->ecxt_callbacks = ecxt_callback->next; - (*ecxt_callback->function) (ecxt_callback->arg); - pfree(ecxt_callback); - } -} diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c deleted file mode 100644 index c7b7c398eb2..00000000000 --- a/src/backend/executor/functions.c +++ /dev/null @@ -1,611 +0,0 @@ -/*------------------------------------------------------------------------- - * - * functions.c - * Routines to handle functions called from the executor - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.52 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "executor/execdefs.h" -#include "executor/executor.h" -#include "executor/functions.h" -#include "tcop/pquery.h" -#include "tcop/tcopprot.h" -#include "tcop/utility.h" -#include "utils/builtins.h" -#include "utils/syscache.h" - - -/* - * We have an execution_state record for each query in a function. - */ -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) - - -/* - * An SQLFunctionCache record is built during the first call, - * and linked to from the fn_extra field of the FmgrInfo struct. - */ - -typedef struct -{ - int typlen; /* length of the return type */ - bool typbyval; /* true if return type is pass by value */ - bool returnsTuple; /* true if return type is a tuple */ - bool shutdown_reg; /* true if registered shutdown callback */ - - TupleTableSlot *funcSlot; /* if one result we need to copy it before - * we end execution of the function and - * free stuff */ - - /* head of linked list of execution_state records */ - execution_state *func_state; -} SQLFunctionCache; - -typedef SQLFunctionCache *SQLFunctionCachePtr; - - -/* non-export function prototypes */ -static execution_state *init_execution_state(char *src, - Oid *argOidVect, int nargs); -static void init_sql_fcache(FmgrInfo *finfo); -static void postquel_start(execution_state *es); -static TupleTableSlot *postquel_getnext(execution_state *es); -static void postquel_end(execution_state *es); -static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo); -static Datum postquel_execute(execution_state *es, - FunctionCallInfo fcinfo, - SQLFunctionCachePtr fcache); -static void ShutdownSQLFunction(Datum arg); - - -static execution_state * -init_execution_state(char *src, Oid *argOidVect, int nargs) -{ - execution_state *newes; - execution_state *nextes; - execution_state *preves; - List *queryTree_list, - *qtl_item; - - newes = (execution_state *) palloc(sizeof(execution_state)); - nextes = newes; - preves = (execution_state *) NULL; - - queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs); - - foreach(qtl_item, queryTree_list) - { - Query *queryTree = lfirst(qtl_item); - Plan *planTree; - EState *estate; - - planTree = pg_plan_query(queryTree); - - 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, NULL); - estate = CreateExecutorState(); - - if (nargs > 0) - { - int i; - ParamListInfo paramLI; - - paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData)); - - MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData)); - - estate->es_param_list_info = paramLI; - - for (i = 0; i < nargs; paramLI++, i++) - { - paramLI->kind = PARAM_NUM; - paramLI->id = i + 1; - paramLI->isnull = false; - paramLI->value = (Datum) NULL; - } - paramLI->kind = PARAM_INVALID; - } - else - estate->es_param_list_info = (ParamListInfo) NULL; - nextes->estate = estate; - preves = nextes; - nextes = (execution_state *) NULL; - } - - return newes; -} - - -static void -init_sql_fcache(FmgrInfo *finfo) -{ - Oid foid = finfo->fn_oid; - HeapTuple procedureTuple; - HeapTuple typeTuple; - Form_pg_proc procedureStruct; - Form_pg_type typeStruct; - SQLFunctionCachePtr fcache; - Oid *argOidVect; - char *src; - int nargs; - Datum tmp; - bool isNull; - - /* - * get the procedure tuple corresponding to the given function Oid - */ - procedureTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(foid), - 0, 0, 0); - if (!HeapTupleIsValid(procedureTuple)) - elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u", - foid); - - procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); - - /* - * get the return type from the procedure tuple - */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(procedureStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u", - procedureStruct->prorettype); - - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - - fcache = (SQLFunctionCachePtr) palloc(sizeof(SQLFunctionCache)); - MemSet(fcache, 0, sizeof(SQLFunctionCache)); - - /* - * get the type length and by-value flag from the type tuple - */ - fcache->typlen = typeStruct->typlen; - if (typeStruct->typrelid == InvalidOid) - { - /* The return type is not a relation, so just use byval */ - fcache->typbyval = typeStruct->typbyval; - fcache->returnsTuple = false; - } - else - { - /* - * This is a hack. We assume here that any function returning a - * tuple returns it by reference. This needs to be fixed, since - * actually the mechanism isn't quite like return-by-reference. - */ - fcache->typbyval = false; - fcache->returnsTuple = true; - } - - /* - * 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 (!finfo->fn_retset && !fcache->typbyval) - fcache->funcSlot = MakeTupleTableSlot(); - else - fcache->funcSlot = NULL; - - nargs = procedureStruct->pronargs; - - if (nargs > 0) - { - argOidVect = (Oid *) palloc(nargs * sizeof(Oid)); - memcpy(argOidVect, - procedureStruct->proargtypes, - nargs * sizeof(Oid)); - } - else - argOidVect = (Oid *) NULL; - - tmp = SysCacheGetAttr(PROCOID, - procedureTuple, - Anum_pg_proc_prosrc, - &isNull); - if (isNull) - elog(ERROR, "init_sql_fcache: null prosrc for procedure %u", - foid); - src = DatumGetCString(DirectFunctionCall1(textout, tmp)); - - fcache->func_state = init_execution_state(src, argOidVect, nargs); - - pfree(src); - - ReleaseSysCache(typeTuple); - ReleaseSysCache(procedureTuple); - - finfo->fn_extra = (void *) fcache; -} - - -static void -postquel_start(execution_state *es) -{ - /* - * Do nothing for utility commands. (create, destroy...) DZ - - * 30-8-1996 - */ - if (es->qd->operation == CMD_UTILITY) - return; - ExecutorStart(es->qd, es->estate); -} - -static TupleTableSlot * -postquel_getnext(execution_state *es) -{ - long count; - - if (es->qd->operation == CMD_UTILITY) - { - /* - * Process a utility command. (create, destroy...) DZ - 30-8-1996 - */ - ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL); - if (!LAST_POSTQUEL_COMMAND(es)) - CommandCounterIncrement(); - return (TupleTableSlot *) NULL; - } - - /* If it's not the last command, just run it to completion */ - count = (LAST_POSTQUEL_COMMAND(es)) ? 1L : 0L; - - return ExecutorRun(es->qd, es->estate, ForwardScanDirection, count); -} - -static void -postquel_end(execution_state *es) -{ - /* - * Do nothing for utility commands. (create, destroy...) DZ - - * 30-8-1996 - */ - if (es->qd->operation == CMD_UTILITY) - return; - ExecutorEnd(es->qd, es->estate); -} - -static void -postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo) -{ - EState *estate; - ParamListInfo paramLI; - - estate = es->estate; - paramLI = estate->es_param_list_info; - - while (paramLI->kind != PARAM_INVALID) - { - if (paramLI->kind == PARAM_NUM) - { - Assert(paramLI->id <= fcinfo->nargs); - paramLI->value = fcinfo->arg[paramLI->id - 1]; - paramLI->isnull = fcinfo->argnull[paramLI->id - 1]; - } - paramLI++; - } -} - -static TupleTableSlot * -copy_function_result(SQLFunctionCachePtr fcache, - TupleTableSlot *resultSlot) -{ - TupleTableSlot *funcSlot; - TupleDesc resultTd; - HeapTuple resultTuple; - HeapTuple newTuple; - - Assert(!TupIsNull(resultSlot)); - resultTuple = resultSlot->val; - - funcSlot = fcache->funcSlot; - - if (funcSlot == NULL) - return resultSlot; /* no need to copy result */ - - /* - * If first time through, we have to initialize the funcSlot's tuple - * descriptor. - */ - if (funcSlot->ttc_tupleDescriptor == NULL) - { - resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor); - ExecSetSlotDescriptor(funcSlot, resultTd, true); - ExecSetSlotDescriptorIsNew(funcSlot, true); - } - - newTuple = heap_copytuple(resultTuple); - - return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true); -} - -static Datum -postquel_execute(execution_state *es, - FunctionCallInfo fcinfo, - SQLFunctionCachePtr fcache) -{ - TupleTableSlot *slot; - Datum value; - - /* - * It's more right place to do it (before - * postquel_start->ExecutorStart). Now - * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But - * note: I HOPE we can do it here). - vadim 01/22/97 - */ - if (fcinfo->nargs > 0) - postquel_sub_params(es, fcinfo); - - if (es->status == F_EXEC_START) - { - postquel_start(es); - es->status = F_EXEC_RUN; - } - - slot = postquel_getnext(es); - - if (TupIsNull(slot)) - { - postquel_end(es); - es->status = F_EXEC_DONE; - fcinfo->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 we are supposed to return a tuple, we return the tuple slot - * pointer converted to Datum. If we are supposed to return a - * simple value, then project out the first attribute of the - * result tuple (ie, take the first result column of the final - * SELECT). - */ - if (fcache->returnsTuple) - { - /* - * XXX do we need to remove junk attrs from the result tuple? - * Probably OK to leave them, as long as they are at the end. - */ - value = PointerGetDatum(resSlot); - fcinfo->isnull = false; - } - else - { - value = heap_getattr(resSlot->val, - 1, - resSlot->ttc_tupleDescriptor, - &(fcinfo->isnull)); - - /* - * Note: if result type is pass-by-reference then we are - * returning a pointer into the tuple copied by - * copy_function_result. This is OK. - */ - } - - /* - * If this is a single valued function we have to end the function - * execution now. - */ - if (!fcinfo->flinfo->fn_retset) - { - 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 -fmgr_sql(PG_FUNCTION_ARGS) -{ - MemoryContext oldcontext; - SQLFunctionCachePtr fcache; - execution_state *es; - Datum result = 0; - - /* - * Switch to context in which the fcache lives. This ensures that - * parsetrees, plans, etc, will have sufficient lifetime. The - * sub-executor is responsible for deleting per-tuple information. - */ - oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); - - /* - * Initialize fcache and execution state if first time through. - */ - fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra; - if (fcache == NULL) - { - init_sql_fcache(fcinfo->flinfo); - fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra; - } - es = fcache->func_state; - Assert(es); - - /* - * Find first unfinished query in function. - */ - 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, fcinfo, fcache); - 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 = fcache->func_state; - while (es) - { - es->status = F_EXEC_START; - es = es->next; - } - - /* - * Let caller know we're finished. - */ - if (fcinfo->flinfo->fn_retset) - { - ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (rsi && IsA(rsi, ReturnSetInfo)) - rsi->isDone = ExprEndResult; - else - elog(ERROR, "Set-valued function called in context that cannot accept a set"); - fcinfo->isnull = true; - result = (Datum) 0; - - /* Deregister shutdown callback, if we made one */ - if (fcache->shutdown_reg) - { - UnregisterExprContextCallback(rsi->econtext, - ShutdownSQLFunction, - PointerGetDatum(fcache)); - fcache->shutdown_reg = false; - } - } - - MemoryContextSwitchTo(oldcontext); - - return result; - } - - /* - * If we got a result from a command within the function it has to be - * the final command. All others shouldn't be returning anything. - */ - Assert(LAST_POSTQUEL_COMMAND(es)); - - /* - * Let caller know we're not finished. - */ - if (fcinfo->flinfo->fn_retset) - { - ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; - - if (rsi && IsA(rsi, ReturnSetInfo)) - rsi->isDone = ExprMultipleResult; - else - elog(ERROR, "Set-valued function called in context that cannot accept a set"); - - /* - * Ensure we will get shut down cleanly if the exprcontext is - * not run to completion. - */ - if (!fcache->shutdown_reg) - { - RegisterExprContextCallback(rsi->econtext, - ShutdownSQLFunction, - PointerGetDatum(fcache)); - fcache->shutdown_reg = true; - } - } - - MemoryContextSwitchTo(oldcontext); - - return result; -} - -/* - * callback function in case a function-returning-set needs to be shut down - * before it has been run to completion - */ -static void -ShutdownSQLFunction(Datum arg) -{ - SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg); - execution_state *es = fcache->func_state; - - while (es != NULL) - { - /* Shut down anything still running */ - if (es->status == F_EXEC_RUN) - postquel_end(es); - /* Reset states to START in case we're called again */ - es->status = F_EXEC_START; - es = es->next; - } - - /* execUtils will deregister the callback... */ - fcache->shutdown_reg = false; -} diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c deleted file mode 100644 index 3b95544706b..00000000000 --- a/src/backend/executor/instrument.c +++ /dev/null @@ -1,122 +0,0 @@ -/*------------------------------------------------------------------------- - * - * instrument.c - * functions for instrumentation of plan execution - * - * - * Copyright (c) 2001, PostgreSQL Global Development Group - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/instrument.c,v 1.3 2002/03/02 21:39:25 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <unistd.h> - -#include "executor/instrument.h" - - -/* Allocate new instrumentation structure */ -Instrumentation * -InstrAlloc(void) -{ - Instrumentation *instr = palloc(sizeof(Instrumentation)); - - memset(instr, 0, sizeof(Instrumentation)); - - return instr; -} - -/* Entry to a plan node */ -void -InstrStartNode(Instrumentation *instr) -{ - if (!instr) - return; - - if (instr->starttime.tv_sec != 0 || instr->starttime.tv_usec != 0) - elog(LOG, "InstrStartTimer called twice in a row"); - else - gettimeofday(&instr->starttime, NULL); -} - -/* Exit from a plan node */ -void -InstrStopNode(Instrumentation *instr, bool returnedTuple) -{ - struct timeval endtime; - - if (!instr) - return; - - if (instr->starttime.tv_sec == 0 && instr->starttime.tv_usec == 0) - { - elog(LOG, "InstrStopNode without start"); - return; - } - - gettimeofday(&endtime, NULL); - - instr->counter.tv_sec += endtime.tv_sec - instr->starttime.tv_sec; - instr->counter.tv_usec += endtime.tv_usec - instr->starttime.tv_usec; - - /* Normalize after each add to avoid overflow/underflow of tv_usec */ - while (instr->counter.tv_usec < 0) - { - instr->counter.tv_usec += 1000000; - instr->counter.tv_sec--; - } - while (instr->counter.tv_usec >= 1000000) - { - instr->counter.tv_usec -= 1000000; - instr->counter.tv_sec++; - } - - instr->starttime.tv_sec = 0; - instr->starttime.tv_usec = 0; - - /* Is this the first tuple of this cycle? */ - if (!instr->running) - { - instr->running = true; - instr->firsttuple = (double) instr->counter.tv_sec + - (double) instr->counter.tv_usec / 1000000.0; - } - - if (returnedTuple) - instr->tuplecount += 1; -} - -/* Finish a run cycle for a plan node */ -void -InstrEndLoop(Instrumentation *instr) -{ - double totaltime; - - if (!instr) - return; - - /* Skip if nothing has happened, or already shut down */ - if (!instr->running) - return; - - /* Accumulate statistics */ - totaltime = (double) instr->counter.tv_sec + - (double) instr->counter.tv_usec / 1000000.0; - - instr->startup += instr->firsttuple; - instr->total += totaltime; - instr->ntuples += instr->tuplecount; - instr->nloops += 1; - - /* Reset for next cycle (if any) */ - instr->running = false; - instr->starttime.tv_sec = 0; - instr->starttime.tv_usec = 0; - instr->counter.tv_sec = 0; - instr->counter.tv_usec = 0; - instr->firsttuple = 0; - instr->tuplecount = 0; -} diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c deleted file mode 100644 index de839269cc3..00000000000 --- a/src/backend/executor/nodeAgg.c +++ /dev/null @@ -1,1053 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeAgg.c - * Routines to handle aggregate nodes. - * - * ExecAgg evaluates each aggregate in the following steps: - * - * transvalue = initcond - * foreach input_value do - * transvalue = transfunc(transvalue, input_value) - * result = finalfunc(transvalue) - * - * If a finalfunc is not supplied then the result is just the ending - * value of transvalue. - * - * If transfunc is marked "strict" in pg_proc and initcond is NULL, - * then the first non-NULL input_value is assigned directly to transvalue, - * and transfunc isn't applied until the second non-NULL input_value. - * The agg's input type and transtype must be the same in this case! - * - * If transfunc is marked "strict" then NULL input_values are skipped, - * keeping the previous transvalue. If transfunc is not strict then it - * is called for every input tuple and must deal with NULL initcond - * or NULL input_value for itself. - * - * If finalfunc is marked "strict" then it is not called when the - * ending transvalue is NULL, instead a NULL result is created - * automatically (this is just the usual handling of strict functions, - * of course). A non-strict finalfunc can make its own choice of - * what to return for a NULL ending transvalue. - * - * When the transvalue datatype is pass-by-reference, we have to be - * careful to ensure that the values survive across tuple cycles yet - * are not allowed to accumulate until end of query. We do this by - * "ping-ponging" between two memory contexts; successive calls to the - * transfunc are executed in alternate contexts, passing the previous - * transvalue that is in the other context. At the beginning of each - * tuple cycle we can reset the current output context to avoid memory - * usage growth. Note: we must use MemoryContextContains() to check - * whether the transfunc has perhaps handed us back one of its input - * values rather than a freshly palloc'd value; if so, we copy the value - * to the context we want it in. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.85 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/pg_aggregate.h" -#include "catalog/pg_operator.h" -#include "executor/executor.h" -#include "executor/nodeAgg.h" -#include "miscadmin.h" -#include "optimizer/clauses.h" -#include "parser/parse_coerce.h" -#include "parser/parse_expr.h" -#include "parser/parse_oper.h" -#include "utils/acl.h" -#include "utils/builtins.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" -#include "utils/tuplesort.h" -#include "utils/datum.h" - - -/* - * AggStatePerAggData - per-aggregate working state for the Agg scan - */ -typedef struct AggStatePerAggData -{ - /* - * These values are set up during ExecInitAgg() and do not change - * thereafter: - */ - - /* Link to Aggref node this working state is for */ - Aggref *aggref; - - /* Oids of transfer functions */ - Oid transfn_oid; - Oid finalfn_oid; /* may be InvalidOid */ - - /* - * fmgr lookup data for transfer functions --- only valid when - * corresponding oid is not InvalidOid. Note in particular that - * fn_strict flags are kept here. - */ - FmgrInfo transfn; - FmgrInfo finalfn; - - /* - * Type of input data and Oid of sort operator to use for it; only - * set/used when aggregate has DISTINCT flag. (These are not used - * directly by nodeAgg, but must be passed to the Tuplesort object.) - */ - Oid inputType; - Oid sortOperator; - - /* - * fmgr lookup data for input type's equality operator --- only - * set/used when aggregate has DISTINCT flag. - */ - FmgrInfo equalfn; - - /* - * initial value from pg_aggregate entry - */ - Datum initValue; - bool initValueIsNull; - - /* - * We need the len and byval info for the agg's input, result, and - * transition data types in order to know how to copy/delete values. - */ - int16 inputtypeLen, - resulttypeLen, - transtypeLen; - bool inputtypeByVal, - resulttypeByVal, - transtypeByVal; - - /* - * These values are working state that is initialized at the start of - * an input tuple group and updated for each input tuple. - * - * For a simple (non DISTINCT) aggregate, we just feed the input values - * straight to the transition function. If it's DISTINCT, we pass the - * input values into a Tuplesort object; then at completion of the - * input tuple group, we scan the sorted values, eliminate duplicates, - * and run the transition function on the rest. - */ - - Tuplesortstate *sortstate; /* sort object, if a DISTINCT agg */ - - Datum transValue; - bool transValueIsNull; - - bool noTransValue; /* true if transValue not set yet */ - - /* - * Note: noTransValue initially has the same value as - * transValueIsNull, and if true both are cleared to false at the same - * time. They are not the same though: if transfn later returns a - * NULL, we want to keep that NULL and not auto-replace it with a - * later input value. Only the first non-NULL input will be - * auto-substituted. - */ -} AggStatePerAggData; - - -static void initialize_aggregate(AggStatePerAgg peraggstate); -static void advance_transition_function(AggStatePerAgg peraggstate, - Datum newVal, bool isNull); -static void process_sorted_aggregate(AggState *aggstate, - AggStatePerAgg peraggstate); -static void finalize_aggregate(AggStatePerAgg peraggstate, - Datum *resultVal, bool *resultIsNull); -static Datum GetAggInitVal(Datum textInitVal, Oid transtype); - - -/* - * Initialize one aggregate for a new set of input values. - * - * When called, CurrentMemoryContext should be the per-query context. - */ -static void -initialize_aggregate(AggStatePerAgg peraggstate) -{ - Aggref *aggref = peraggstate->aggref; - - /* - * Start a fresh sort operation for each DISTINCT aggregate. - */ - if (aggref->aggdistinct) - { - /* - * In case of rescan, maybe there could be an uncompleted sort - * operation? Clean it up if so. - */ - if (peraggstate->sortstate) - tuplesort_end(peraggstate->sortstate); - - peraggstate->sortstate = - tuplesort_begin_datum(peraggstate->inputType, - peraggstate->sortOperator, - false); - } - - /* - * (Re)set transValue to the initial value. - * - * Note that when the initial value is pass-by-ref, we just reuse it - * without copying for each group. Hence, transition function had - * better not scribble on its input, or it will fail for GROUP BY! - */ - peraggstate->transValue = peraggstate->initValue; - peraggstate->transValueIsNull = peraggstate->initValueIsNull; - - /* - * If the initial value for the transition state doesn't exist in the - * pg_aggregate table then we will let the first non-NULL value - * returned from the outer procNode become the initial value. (This is - * useful for aggregates like max() and min().) The noTransValue flag - * signals that we still need to do this. - */ - peraggstate->noTransValue = peraggstate->initValueIsNull; -} - -/* - * Given a new input value, advance the transition function of an aggregate. - * - * When called, CurrentMemoryContext should be the context we want the - * transition function result to be delivered into on this cycle. - */ -static void -advance_transition_function(AggStatePerAgg peraggstate, - Datum newVal, bool isNull) -{ - FunctionCallInfoData fcinfo; - - if (peraggstate->transfn.fn_strict) - { - if (isNull) - { - /* - * For a strict transfn, nothing happens at a NULL input - * tuple; we just keep the prior transValue. However, if the - * transtype is pass-by-ref, we have to copy it into the new - * context because the old one is going to get reset. - */ - if (!peraggstate->transValueIsNull) - peraggstate->transValue = datumCopy(peraggstate->transValue, - peraggstate->transtypeByVal, - peraggstate->transtypeLen); - return; - } - if (peraggstate->noTransValue) - { - /* - * transValue has not been initialized. This is the first - * non-NULL input value. We use it as the initial value for - * transValue. (We already checked that the agg's input type - * is binary-compatible with its transtype, so straight copy - * here is OK.) - * - * We had better copy the datum if it is pass-by-ref, since the - * given pointer may be pointing into a scan tuple that will - * be freed on the next iteration of the scan. - */ - peraggstate->transValue = datumCopy(newVal, - peraggstate->transtypeByVal, - peraggstate->transtypeLen); - peraggstate->transValueIsNull = false; - peraggstate->noTransValue = false; - return; - } - if (peraggstate->transValueIsNull) - { - /* - * Don't call a strict function with NULL inputs. Note it is - * possible to get here despite the above tests, if the - * transfn is strict *and* returned a NULL on a prior cycle. - * If that happens we will propagate the NULL all the way to - * the end. - */ - return; - } - } - - /* OK to call the transition function */ - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &peraggstate->transfn; - fcinfo.nargs = 2; - fcinfo.arg[0] = peraggstate->transValue; - fcinfo.argnull[0] = peraggstate->transValueIsNull; - fcinfo.arg[1] = newVal; - fcinfo.argnull[1] = isNull; - - newVal = FunctionCallInvoke(&fcinfo); - - /* - * If the transition function was uncooperative, it may have given us - * a pass-by-ref result that points at the scan tuple or the - * prior-cycle working memory. Copy it into the active context if it - * doesn't look right. - */ - if (!peraggstate->transtypeByVal && !fcinfo.isnull && - !MemoryContextContains(CurrentMemoryContext, - DatumGetPointer(newVal))) - newVal = datumCopy(newVal, - peraggstate->transtypeByVal, - peraggstate->transtypeLen); - - peraggstate->transValue = newVal; - peraggstate->transValueIsNull = fcinfo.isnull; -} - -/* - * Run the transition function for a DISTINCT aggregate. This is called - * after we have completed entering all the input values into the sort - * object. We complete the sort, read out the values in sorted order, - * and run the transition function on each non-duplicate value. - * - * When called, CurrentMemoryContext should be the per-query context. - */ -static void -process_sorted_aggregate(AggState *aggstate, - AggStatePerAgg peraggstate) -{ - Datum oldVal = (Datum) 0; - bool haveOldVal = false; - MemoryContext oldContext; - Datum newVal; - bool isNull; - - tuplesort_performsort(peraggstate->sortstate); - - /* - * Note: if input type is pass-by-ref, the datums returned by the sort - * are freshly palloc'd in the per-query context, so we must be - * careful to pfree them when they are no longer needed. - */ - - while (tuplesort_getdatum(peraggstate->sortstate, true, - &newVal, &isNull)) - { - /* - * DISTINCT always suppresses nulls, per SQL spec, regardless of - * the transition function's strictness. - */ - if (isNull) - continue; - - /* - * Clear and select the current working context for evaluation of - * the equality function and transition function. - */ - MemoryContextReset(aggstate->agg_cxt[aggstate->which_cxt]); - oldContext = - MemoryContextSwitchTo(aggstate->agg_cxt[aggstate->which_cxt]); - - if (haveOldVal && - DatumGetBool(FunctionCall2(&peraggstate->equalfn, - oldVal, newVal))) - { - /* equal to prior, so forget this one */ - if (!peraggstate->inputtypeByVal) - pfree(DatumGetPointer(newVal)); - - /* - * note we do NOT flip contexts in this case, so no need to - * copy prior transValue to other context. - */ - } - else - { - advance_transition_function(peraggstate, newVal, false); - - /* - * Make the other context current so that this transition - * result is preserved. - */ - aggstate->which_cxt = 1 - aggstate->which_cxt; - /* forget the old value, if any */ - if (haveOldVal && !peraggstate->inputtypeByVal) - pfree(DatumGetPointer(oldVal)); - oldVal = newVal; - haveOldVal = true; - } - - MemoryContextSwitchTo(oldContext); - } - - if (haveOldVal && !peraggstate->inputtypeByVal) - pfree(DatumGetPointer(oldVal)); - - tuplesort_end(peraggstate->sortstate); - peraggstate->sortstate = NULL; -} - -/* - * Compute the final value of one aggregate. - * - * When called, CurrentMemoryContext should be the context where we want - * final values delivered (ie, the per-output-tuple expression context). - */ -static void -finalize_aggregate(AggStatePerAgg peraggstate, - Datum *resultVal, bool *resultIsNull) -{ - /* - * Apply the agg's finalfn if one is provided, else return transValue. - */ - if (OidIsValid(peraggstate->finalfn_oid)) - { - FunctionCallInfoData fcinfo; - - MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &peraggstate->finalfn; - fcinfo.nargs = 1; - fcinfo.arg[0] = peraggstate->transValue; - fcinfo.argnull[0] = peraggstate->transValueIsNull; - if (fcinfo.flinfo->fn_strict && peraggstate->transValueIsNull) - { - /* don't call a strict function with NULL inputs */ - *resultVal = (Datum) 0; - *resultIsNull = true; - } - else - { - *resultVal = FunctionCallInvoke(&fcinfo); - *resultIsNull = fcinfo.isnull; - } - } - else - { - *resultVal = peraggstate->transValue; - *resultIsNull = peraggstate->transValueIsNull; - } - - /* - * If result is pass-by-ref, make sure it is in the right context. - */ - if (!peraggstate->resulttypeByVal && !*resultIsNull && - !MemoryContextContains(CurrentMemoryContext, - DatumGetPointer(*resultVal))) - *resultVal = datumCopy(*resultVal, - peraggstate->resulttypeByVal, - peraggstate->resulttypeLen); -} - - -/* --------------------------------------- - * - * ExecAgg - - * - * ExecAgg receives tuples from its outer subplan and aggregates over - * the appropriate attribute for each aggregate function use (Aggref - * node) appearing in the targetlist or qual of the node. The number - * of tuples to aggregate over depends on whether a GROUP BY clause is - * present. We can produce an aggregate result row per group, or just - * one for the whole query. The value of each aggregate is stored in - * the expression context to be used when ExecProject evaluates the - * result tuple. - * - * If the outer subplan is a Group node, ExecAgg returns as many tuples - * as there are groups. - * - * ------------------------------------------ - */ -TupleTableSlot * -ExecAgg(Agg *node) -{ - AggState *aggstate; - EState *estate; - Plan *outerPlan; - ExprContext *econtext; - ProjectionInfo *projInfo; - Datum *aggvalues; - bool *aggnulls; - AggStatePerAgg peragg; - MemoryContext oldContext; - TupleTableSlot *resultSlot; - HeapTuple inputTuple; - int aggno; - bool isNull; - - /* - * get state info from node - */ - aggstate = node->aggstate; - estate = node->plan.state; - outerPlan = outerPlan(node); - econtext = aggstate->csstate.cstate.cs_ExprContext; - aggvalues = econtext->ecxt_aggvalues; - aggnulls = econtext->ecxt_aggnulls; - projInfo = aggstate->csstate.cstate.cs_ProjInfo; - peragg = aggstate->peragg; - - /* - * We loop retrieving groups until we find one matching - * node->plan.qual - */ - do - { - if (aggstate->agg_done) - return NULL; - - /* - * Clear the per-output-tuple context for each group - */ - MemoryContextReset(aggstate->tup_cxt); - - /* - * Initialize working state for a new input tuple group - */ - for (aggno = 0; aggno < aggstate->numaggs; aggno++) - { - AggStatePerAgg peraggstate = &peragg[aggno]; - - initialize_aggregate(peraggstate); - } - - inputTuple = NULL; /* no saved input tuple yet */ - - /* - * for each tuple from the outer plan, update all the aggregates - */ - for (;;) - { - TupleTableSlot *outerslot; - - outerslot = ExecProcNode(outerPlan, (Plan *) node); - if (TupIsNull(outerslot)) - break; - econtext->ecxt_scantuple = outerslot; - - /* - * Clear and select the current working context for evaluation - * of the input expressions and transition functions at this - * input tuple. - */ - econtext->ecxt_per_tuple_memory = - aggstate->agg_cxt[aggstate->which_cxt]; - ResetExprContext(econtext); - oldContext = - MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - for (aggno = 0; aggno < aggstate->numaggs; aggno++) - { - AggStatePerAgg peraggstate = &peragg[aggno]; - Aggref *aggref = peraggstate->aggref; - Datum newVal; - - newVal = ExecEvalExpr(aggref->target, econtext, - &isNull, NULL); - - if (aggref->aggdistinct) - { - /* in DISTINCT mode, we may ignore nulls */ - if (isNull) - continue; - /* putdatum has to be called in per-query context */ - MemoryContextSwitchTo(oldContext); - tuplesort_putdatum(peraggstate->sortstate, - newVal, isNull); - MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - } - else - { - advance_transition_function(peraggstate, - newVal, isNull); - } - } - - /* - * Make the other context current so that these transition - * results are preserved. - */ - aggstate->which_cxt = 1 - aggstate->which_cxt; - - MemoryContextSwitchTo(oldContext); - - /* - * Keep a copy of the first input tuple for the projection. - * (We only need one since only the GROUP BY columns in it can - * be referenced, and these will be the same for all tuples - * aggregated over.) - */ - if (!inputTuple) - inputTuple = heap_copytuple(outerslot->val); - } - - /* - * Done scanning input tuple group. Finalize each aggregate - * calculation, and stash results in the per-output-tuple context. - * - * This is a bit tricky when there are both DISTINCT and plain - * aggregates: we must first finalize all the plain aggs and then - * all the DISTINCT ones. This is needed because the last - * transition values for the plain aggs are stored in the - * not-current working context, and we have to evaluate those aggs - * (and stash the results in the output tup_cxt!) before we start - * flipping contexts again in process_sorted_aggregate. - */ - oldContext = MemoryContextSwitchTo(aggstate->tup_cxt); - for (aggno = 0; aggno < aggstate->numaggs; aggno++) - { - AggStatePerAgg peraggstate = &peragg[aggno]; - - if (!peraggstate->aggref->aggdistinct) - finalize_aggregate(peraggstate, - &aggvalues[aggno], &aggnulls[aggno]); - } - MemoryContextSwitchTo(oldContext); - for (aggno = 0; aggno < aggstate->numaggs; aggno++) - { - AggStatePerAgg peraggstate = &peragg[aggno]; - - if (peraggstate->aggref->aggdistinct) - { - process_sorted_aggregate(aggstate, peraggstate); - oldContext = MemoryContextSwitchTo(aggstate->tup_cxt); - finalize_aggregate(peraggstate, - &aggvalues[aggno], &aggnulls[aggno]); - MemoryContextSwitchTo(oldContext); - } - } - - /* - * If the outerPlan is a Group node, we will reach here after each - * group. We are not done unless the Group node is done (a little - * ugliness here while we reach into the Group's state to find - * out). Furthermore, when grouping we return nothing at all - * unless we had some input tuple(s). By the nature of Group, - * there are no empty groups, so if we get here with no input the - * whole scan is empty. - * - * If the outerPlan isn't a Group, we are done when we get here, and - * we will emit a (single) tuple even if there were no input - * tuples. - */ - if (IsA(outerPlan, Group)) - { - /* aggregation over groups */ - aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done; - /* check for no groups */ - if (inputTuple == NULL) - return NULL; - } - else - { - aggstate->agg_done = true; - - /* - * If inputtuple==NULL (ie, the outerPlan didn't return - * anything), create a dummy all-nulls input tuple for use by - * ExecProject. 99.44% of the time this is a waste of cycles, - * because ordinarily the projected output tuple's targetlist - * cannot contain any direct (non-aggregated) references to - * input columns, so the dummy tuple will not be referenced. - * However there are special cases where this isn't so --- in - * particular an UPDATE involving an aggregate will have a - * targetlist reference to ctid. We need to return a null for - * ctid in that situation, not coredump. - * - * The values returned for the aggregates will be the initial - * values of the transition functions. - */ - if (inputTuple == NULL) - { - TupleDesc tupType; - Datum *tupValue; - char *null_array; - AttrNumber attnum; - - tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor; - tupValue = projInfo->pi_tupValue; - /* watch out for null input tuples, though... */ - if (tupType && tupValue) - { - null_array = (char *) palloc(sizeof(char) * tupType->natts); - for (attnum = 0; attnum < tupType->natts; attnum++) - null_array[attnum] = 'n'; - inputTuple = heap_formtuple(tupType, tupValue, null_array); - pfree(null_array); - } - } - } - - /* - * Store the representative input tuple in the tuple table slot - * reserved for it. The tuple will be deleted when it is cleared - * from the slot. - */ - ExecStoreTuple(inputTuple, - aggstate->csstate.css_ScanTupleSlot, - InvalidBuffer, - true); - econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot; - - /* - * Do projection and qual check in the per-output-tuple context. - */ - econtext->ecxt_per_tuple_memory = aggstate->tup_cxt; - - /* - * Form a projection tuple using the aggregate results and the - * representative input tuple. Store it in the result tuple slot. - * Note we do not support aggregates returning sets ... - */ - resultSlot = ExecProject(projInfo, NULL); - - /* - * If the completed tuple does not match the qualifications, it is - * ignored and we loop back to try to process another group. - * Otherwise, return the tuple. - */ - } - while (!ExecQual(node->plan.qual, econtext, false)); - - 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; - AggStatePerAgg peragg; - Plan *outerPlan; - ExprContext *econtext; - int numaggs, - aggno; - List *alist; - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - aggstate = makeNode(AggState); - node->aggstate = aggstate; - aggstate->agg_done = false; - - /* - * find aggregates in targetlist and quals - * - * Note: pull_agg_clauses also checks that no aggs contain other agg - * calls in their arguments. This would make no sense under SQL - * semantics anyway (and it's forbidden by the spec). Because that is - * true, we don't need to worry about evaluating the aggs in any - * particular order. - */ - aggstate->aggs = nconc(pull_agg_clause((Node *) node->plan.targetlist), - pull_agg_clause((Node *) node->plan.qual)); - aggstate->numaggs = numaggs = length(aggstate->aggs); - if (numaggs <= 0) - { - /* - * This used to be treated as an error, but we can't do that - * anymore because constant-expression simplification could - * optimize away all of the Aggrefs in the targetlist and qual. - * So, just make a debug note, and force numaggs positive so that - * palloc()s below don't choke. - */ - elog(LOG, "ExecInitAgg: could not find any aggregate functions"); - numaggs = 1; - } - - /* - * Create expression context - */ - ExecAssignExprContext(estate, &aggstate->csstate.cstate); - - /* - * We actually need three separate expression memory contexts: one for - * calculating per-output-tuple values (ie, the finished aggregate - * results), and two that we ping-pong between for per-input-tuple - * evaluation of input expressions and transition functions. The - * context made by ExecAssignExprContext() is used as the output - * context. - */ - aggstate->tup_cxt = - aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory; - aggstate->agg_cxt[0] = - AllocSetContextCreate(CurrentMemoryContext, - "AggExprContext1", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - aggstate->agg_cxt[1] = - AllocSetContextCreate(CurrentMemoryContext, - "AggExprContext2", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - aggstate->which_cxt = 0; - -#define AGG_NSLOTS 2 - - /* - * tuple table initialization - */ - ExecInitScanTupleSlot(estate, &aggstate->csstate); - ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate); - - /* - * Set up aggregate-result storage in the expr context, and also - * allocate my private per-agg working storage - */ - econtext = aggstate->csstate.cstate.cs_ExprContext; - econtext->ecxt_aggvalues = (Datum *) palloc(sizeof(Datum) * numaggs); - MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * numaggs); - econtext->ecxt_aggnulls = (bool *) palloc(sizeof(bool) * numaggs); - MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * numaggs); - - peragg = (AggStatePerAgg) palloc(sizeof(AggStatePerAggData) * numaggs); - MemSet(peragg, 0, sizeof(AggStatePerAggData) * numaggs); - aggstate->peragg = peragg; - - /* - * initialize child nodes - */ - outerPlan = outerPlan(node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* - * initialize source tuple type. - */ - ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate); - - /* - * Initialize result tuple type and projection info. - */ - ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate); - ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate); - - /* - * Perform lookups of aggregate function info, and initialize the - * unchanging fields of the per-agg data - */ - aggno = -1; - foreach(alist, aggstate->aggs) - { - Aggref *aggref = (Aggref *) lfirst(alist); - AggStatePerAgg peraggstate = &peragg[++aggno]; - HeapTuple aggTuple; - Form_pg_aggregate aggform; - AclResult aclresult; - Oid transfn_oid, - finalfn_oid; - Datum textInitVal; - - /* Mark Aggref node with its associated index in the result array */ - aggref->aggno = aggno; - - /* Fill in the peraggstate data */ - peraggstate->aggref = aggref; - - aggTuple = SearchSysCache(AGGFNOID, - ObjectIdGetDatum(aggref->aggfnoid), - 0, 0, 0); - if (!HeapTupleIsValid(aggTuple)) - elog(ERROR, "ExecAgg: cache lookup failed for aggregate %u", - aggref->aggfnoid); - aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); - - /* Check permission to call aggregate function */ - aclresult = pg_proc_aclcheck(aggref->aggfnoid, GetUserId(), - ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, get_func_name(aggref->aggfnoid)); - - get_typlenbyval(aggref->aggtype, - &peraggstate->resulttypeLen, - &peraggstate->resulttypeByVal); - get_typlenbyval(aggform->aggtranstype, - &peraggstate->transtypeLen, - &peraggstate->transtypeByVal); - - /* - * initval is potentially null, so don't try to access it as a struct - * field. Must do it the hard way with SysCacheGetAttr. - */ - textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple, - Anum_pg_aggregate_agginitval, - &peraggstate->initValueIsNull); - - if (peraggstate->initValueIsNull) - peraggstate->initValue = (Datum) 0; - else - peraggstate->initValue = GetAggInitVal(textInitVal, - aggform->aggtranstype); - - peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; - peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; - - fmgr_info(transfn_oid, &peraggstate->transfn); - if (OidIsValid(finalfn_oid)) - fmgr_info(finalfn_oid, &peraggstate->finalfn); - - /* - * If the transfn is strict and the initval is NULL, make sure - * input type and transtype are the same (or at least binary- - * compatible), so that it's OK to use the first input value as - * the initial transValue. This should have been checked at agg - * definition time, but just in case... - */ - if (peraggstate->transfn.fn_strict && peraggstate->initValueIsNull) - { - /* - * Note: use the type from the input expression here, not - * from pg_proc.proargtypes, because the latter might be 0. - * (Consider COUNT(*).) - */ - Oid inputType = exprType(aggref->target); - - if (!IsBinaryCompatible(inputType, aggform->aggtranstype)) - elog(ERROR, "Aggregate %u needs to have compatible input type and transition type", - aggref->aggfnoid); - } - - if (aggref->aggdistinct) - { - /* - * Note: use the type from the input expression here, not - * from pg_proc.proargtypes, because the latter might be 0. - * (Consider COUNT(*).) - */ - Oid inputType = exprType(aggref->target); - Oid eq_function; - - peraggstate->inputType = inputType; - get_typlenbyval(inputType, - &peraggstate->inputtypeLen, - &peraggstate->inputtypeByVal); - - eq_function = compatible_oper_funcid(makeList1(makeString("=")), - inputType, inputType, - true); - if (!OidIsValid(eq_function)) - elog(ERROR, "Unable to identify an equality operator for type %s", - format_type_be(inputType)); - fmgr_info(eq_function, &(peraggstate->equalfn)); - peraggstate->sortOperator = any_ordering_op(inputType); - peraggstate->sortstate = NULL; - } - - ReleaseSysCache(aggTuple); - } - - return TRUE; -} - -static Datum -GetAggInitVal(Datum textInitVal, Oid transtype) -{ - char *strInitVal; - HeapTuple tup; - Oid typinput, - typelem; - Datum initVal; - - strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal)); - - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(transtype), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "GetAggInitVal: cache lookup failed on aggregate transition function return type %u", transtype); - - typinput = ((Form_pg_type) GETSTRUCT(tup))->typinput; - typelem = ((Form_pg_type) GETSTRUCT(tup))->typelem; - ReleaseSysCache(tup); - - initVal = OidFunctionCall3(typinput, - CStringGetDatum(strInitVal), - ObjectIdGetDatum(typelem), - Int32GetDatum(-1)); - - pfree(strInitVal); - return initVal; -} - -int -ExecCountSlotsAgg(Agg *node) -{ - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - AGG_NSLOTS; -} - -void -ExecEndAgg(Agg *node) -{ - AggState *aggstate = node->aggstate; - Plan *outerPlan; - - ExecFreeProjectionInfo(&aggstate->csstate.cstate); - - /* - * Make sure ExecFreeExprContext() frees the right expr context... - */ - aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory = - aggstate->tup_cxt; - ExecFreeExprContext(&aggstate->csstate.cstate); - - /* - * ... and I free the others. - */ - MemoryContextDelete(aggstate->agg_cxt[0]); - MemoryContextDelete(aggstate->agg_cxt[1]); - - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan *) node); - - /* clean up tuple table */ - ExecClearTuple(aggstate->csstate.css_ScanTupleSlot); -} - -void -ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent) -{ - AggState *aggstate = node->aggstate; - ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext; - - aggstate->agg_done = false; - MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * aggstate->numaggs); - MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * aggstate->numaggs); - - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); -} - -/* - * aggregate_dummy - dummy execution routine for aggregate functions - * - * This function is listed as the implementation (prosrc field) of pg_proc - * entries for aggregate functions. Its only purpose is to throw an error - * if someone mistakenly executes such a function in the normal way. - * - * Perhaps someday we could assign real meaning to the prosrc field of - * an aggregate? - */ -Datum -aggregate_dummy(PG_FUNCTION_ARGS) -{ - elog(ERROR, "Aggregate function %u called as normal function", - fcinfo->flinfo->fn_oid); - return (Datum) 0; /* keep compiler quiet */ -} diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c deleted file mode 100644 index 724f0c8ce89..00000000000 --- a/src/backend/executor/nodeAppend.c +++ /dev/null @@ -1,406 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeAppend.c - * routines to handle append nodes. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.45 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* INTERFACE ROUTINES - * ExecInitAppend - initialize the append node - * ExecProcAppend - retrieve the next tuple from the node - * ExecEndAppend - shut down the append node - * ExecReScanAppend - rescan 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 for unions, and 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 "postgres.h" - -#include "access/heapam.h" -#include "executor/execdebug.h" -#include "executor/nodeAppend.h" -#include "parser/parsetree.h" - -static bool exec_append_initialize_next(Append *node); - - -/* ---------------------------------------------------------------- - * 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. - * ---------------------------------------------------------------- - */ -static bool -exec_append_initialize_next(Append *node) -{ - EState *estate; - AppendState *appendstate; - int whichplan; - - /* - * get information from the append node - */ - estate = node->plan.state; - appendstate = node->appendstate; - whichplan = appendstate->as_whichplan; - - if (whichplan < appendstate->as_firstplan) - { - /* - * 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 - */ - appendstate->as_whichplan = appendstate->as_firstplan; - return FALSE; - } - else if (whichplan > appendstate->as_lastplan) - { - /* - * as above, end the scan if we go beyond the last scan in our - * list.. - */ - appendstate->as_whichplan = appendstate->as_lastplan; - return FALSE; - } - else - { - /* - * initialize the scan - * - * If we are controlling the target relation, select the proper - * active ResultRelInfo and junk filter for this target. - */ - if (node->isTarget) - { - Assert(whichplan < estate->es_num_result_relations); - estate->es_result_relation_info = - estate->es_result_relations + whichplan; - estate->es_junkFilter = - estate->es_result_relation_info->ri_junkFilter; - } - - 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.) - * - * Special case: during an EvalPlanQual recheck query of an inherited - * target relation, we only want to initialize and scan the single - * subplan that corresponds to the target relation being checked. - * ---------------------------------------------------------------- - */ -bool -ExecInitAppend(Append *node, EState *estate, Plan *parent) -{ - AppendState *appendstate; - int nplans; - List *appendplans; - bool *initialized; - int i; - Plan *initNode; - - CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext); - - /* - * assign execution state to node and get information for append state - */ - node->plan.state = estate; - - appendplans = node->appendplans; - nplans = length(appendplans); - - initialized = (bool *) palloc(nplans * sizeof(bool)); - MemSet(initialized, 0, nplans * sizeof(bool)); - - /* - * create new AppendState for our append node - */ - appendstate = makeNode(AppendState); - appendstate->as_nplans = nplans; - appendstate->as_initialized = initialized; - - node->appendstate = appendstate; - - /* - * Do we want to scan just one subplan? (Special case for - * EvalPlanQual) XXX pretty dirty way of determining that this case - * applies ... - */ - if (node->isTarget && estate->es_evTuple != NULL) - { - int tplan; - - tplan = estate->es_result_relation_info - estate->es_result_relations; - Assert(tplan >= 0 && tplan < nplans); - - appendstate->as_firstplan = tplan; - appendstate->as_lastplan = tplan; - } - else - { - /* normal case, scan all subplans */ - appendstate->as_firstplan = 0; - appendstate->as_lastplan = nplans - 1; - } - - /* - * Miscellaneous initialization - * - * Append plans don't have expression contexts because they never call - * ExecQual or ExecProject. - */ - -#define APPEND_NSLOTS 1 - - /* - * append nodes still have Result slots, which hold pointers to - * tuples, so we have to initialize them. - */ - ExecInitResultTupleSlot(estate, &appendstate->cstate); - - /* - * call ExecInitNode on each of the plans to be executed and save the - * results into the array "initialized" - */ - for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++) - { - appendstate->as_whichplan = i; - exec_append_initialize_next(node); - - initNode = (Plan *) nth(i, appendplans); - initialized[i] = ExecInitNode(initNode, estate, (Plan *) node); - } - - /* - * initialize tuple type - */ - ExecAssignResultTypeFromTL((Plan *) node, &appendstate->cstate); - appendstate->cstate.cs_ProjInfo = NULL; - - /* - * return the result from the first subplan's initialization - */ - appendstate->as_whichplan = appendstate->as_firstplan; - exec_append_initialize_next(node); - - return TRUE; -} - -int -ExecCountSlotsAppend(Append *node) -{ - List *plan; - int nSlots = 0; - - foreach(plan, node->appendplans) - 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. - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecProcAppend(Append *node) -{ - EState *estate; - AppendState *appendstate; - int whichplan; - List *appendplans; - Plan *subnode; - TupleTableSlot *result; - TupleTableSlot *result_slot; - ScanDirection direction; - - /* - * get information from the node - */ - appendstate = node->appendstate; - estate = node->plan.state; - direction = estate->es_direction; - appendplans = node->appendplans; - whichplan = appendstate->as_whichplan; - result_slot = appendstate->cstate.cs_ResultTupleSlot; - - /* - * figure out which subplan we are currently processing - */ - subnode = (Plan *) nth(whichplan, appendplans); - - if (subnode == NULL) - elog(LOG, "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. - * - * Note we rely on the subplan to retain ownership of the tuple for - * as long as we need it --- we don't copy it. - */ - return ExecStoreTuple(result->val, result_slot, InvalidBuffer, false); - } - else - { - /* - * .. go on to the "next" subplan in the appropriate direction and - * try processing again (recursively) - */ - if (ScanDirectionIsForward(direction)) - appendstate->as_whichplan++; - else - appendstate->as_whichplan--; - - /* - * return something from next node or an empty slot if 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) -{ - EState *estate; - AppendState *appendstate; - int nplans; - List *appendplans; - bool *initialized; - int i; - - /* - * get information from the node - */ - appendstate = node->appendstate; - estate = node->plan.state; - appendplans = node->appendplans; - nplans = appendstate->as_nplans; - initialized = appendstate->as_initialized; - - /* - * shut down each of the subscans - */ - for (i = 0; i < nplans; i++) - { - if (initialized[i]) - ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node); - } -} - -void -ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent) -{ - AppendState *appendstate = node->appendstate; - int i; - - for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++) - { - Plan *subnode; - - subnode = (Plan *) nth(i, node->appendplans); - - /* - * ExecReScan doesn't know about my subplans, so I have to do - * changed-parameter signaling myself. - */ - if (node->plan.chgParam != NULL) - SetChangedParamList(subnode, node->plan.chgParam); - - /* - * if chgParam of subnode is not null then plan will be re-scanned - * by first ExecProcNode. - */ - if (subnode->chgParam == NULL) - { - /* make sure estate is correct for this subnode (needed??) */ - appendstate->as_whichplan = i; - exec_append_initialize_next(node); - ExecReScan(subnode, exprCtxt, (Plan *) node); - } - } - appendstate->as_whichplan = appendstate->as_firstplan; - exec_append_initialize_next(node); -} diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c deleted file mode 100644 index 3645a188334..00000000000 --- a/src/backend/executor/nodeFunctionscan.c +++ /dev/null @@ -1,469 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeFunctionscan.c - * Support routines for scanning RangeFunctions (functions in rangetable). - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.2 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecFunctionScan scans a function. - * ExecFunctionNext retrieve next tuple in sequential order. - * ExecInitFunctionScan creates and initializes a functionscan node. - * ExecEndFunctionScan releases any storage allocated. - * ExecFunctionReScan rescans the function - */ -#include "postgres.h" - -#include "miscadmin.h" -#include "access/heapam.h" -#include "catalog/pg_type.h" -#include "executor/execdebug.h" -#include "executor/execdefs.h" -#include "executor/execdesc.h" -#include "executor/nodeFunctionscan.h" -#include "parser/parsetree.h" -#include "parser/parse_expr.h" -#include "parser/parse_type.h" -#include "storage/lmgr.h" -#include "tcop/pquery.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" -#include "utils/tuplestore.h" - -static TupleTableSlot *FunctionNext(FunctionScan *node); -static TupleTableSlot *function_getonetuple(TupleTableSlot *slot, - Node *expr, - ExprContext *econtext, - TupleDesc tupdesc, - bool returnsTuple, - bool *isNull, - ExprDoneCond *isDone); -static FunctionMode get_functionmode(Node *expr); - -/* ---------------------------------------------------------------- - * Scan Support - * ---------------------------------------------------------------- - */ -/* ---------------------------------------------------------------- - * FunctionNext - * - * This is a workhorse for ExecFunctionScan - * ---------------------------------------------------------------- - */ -static TupleTableSlot * -FunctionNext(FunctionScan *node) -{ - TupleTableSlot *slot; - Node *expr; - ExprContext *econtext; - TupleDesc tupdesc; - EState *estate; - ScanDirection direction; - Tuplestorestate *tuplestorestate; - FunctionScanState *scanstate; - bool should_free; - HeapTuple heapTuple; - - /* - * get information from the estate and scan state - */ - scanstate = (FunctionScanState *) node->scan.scanstate; - estate = node->scan.plan.state; - direction = estate->es_direction; - econtext = scanstate->csstate.cstate.cs_ExprContext; - - tuplestorestate = scanstate->tuplestorestate; - tupdesc = scanstate->tupdesc; - expr = scanstate->funcexpr; - - /* - * If first time through, read all tuples from function and pass them to - * tuplestore.c. Subsequent calls just fetch tuples from tuplestore. - */ - if (tuplestorestate == NULL) - { - /* - * Initialize tuplestore module. - */ - tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */ - SortMem); - - scanstate->tuplestorestate = (void *) tuplestorestate; - - /* - * Compute all the function tuples and pass to tuplestore. - */ - for (;;) - { - bool isNull; - ExprDoneCond isDone; - - isNull = false; - isDone = ExprSingleResult; - slot = function_getonetuple(scanstate->csstate.css_ScanTupleSlot, - expr, econtext, tupdesc, - scanstate->returnsTuple, - &isNull, &isDone); - if (TupIsNull(slot)) - break; - - tuplestore_puttuple(tuplestorestate, (void *) slot->val); - ExecClearTuple(slot); - - if (isDone != ExprMultipleResult) - break; - } - - /* - * Complete the store. - */ - tuplestore_donestoring(tuplestorestate); - } - - /* - * Get the next tuple from tuplestore. Return NULL if no more tuples. - */ - slot = scanstate->csstate.css_ScanTupleSlot; - heapTuple = tuplestore_getheaptuple(tuplestorestate, - ScanDirectionIsForward(direction), - &should_free); - - return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); -} - -/* ---------------------------------------------------------------- - * ExecFunctionScan(node) - * - * Scans the Function sequentially and returns the next qualifying - * tuple. - * It calls the ExecScan() routine and passes it the access method - * which retrieve tuples sequentially. - * - */ - -TupleTableSlot * -ExecFunctionScan(FunctionScan *node) -{ - /* - * use FunctionNext as access method - */ - return ExecScan(&node->scan, (ExecScanAccessMtd) FunctionNext); -} - -/* ---------------------------------------------------------------- - * ExecInitFunctionScan - * ---------------------------------------------------------------- - */ -bool -ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent) -{ - FunctionScanState *scanstate; - RangeTblEntry *rte; - Oid funcrettype; - Oid funcrelid; - TupleDesc tupdesc; - - /* - * FunctionScan should not have any children. - */ - Assert(outerPlan((Plan *) node) == NULL); - Assert(innerPlan((Plan *) node) == NULL); - - /* - * assign the node's execution state - */ - node->scan.plan.state = estate; - - /* - * create new ScanState for node - */ - scanstate = makeNode(FunctionScanState); - node->scan.scanstate = &scanstate->csstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &scanstate->csstate.cstate); - -#define FUNCTIONSCAN_NSLOTS 2 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &scanstate->csstate.cstate); - ExecInitScanTupleSlot(estate, &scanstate->csstate); - - /* - * get info about function - */ - rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); - Assert(rte->rtekind == RTE_FUNCTION); - funcrettype = exprType(rte->funcexpr); - funcrelid = typeidTypeRelid(funcrettype); - - /* - * Build a suitable tupledesc representing the output rows - */ - if (OidIsValid(funcrelid)) - { - /* - * Composite data type, i.e. a table's row type - * Same as ordinary relation RTE - */ - Relation rel; - - rel = relation_open(funcrelid, AccessShareLock); - tupdesc = CreateTupleDescCopy(RelationGetDescr(rel)); - relation_close(rel, AccessShareLock); - scanstate->returnsTuple = true; - } - else - { - /* - * Must be a base data type, i.e. scalar - */ - char *attname = strVal(lfirst(rte->eref->colnames)); - - tupdesc = CreateTemplateTupleDesc(1); - TupleDescInitEntry(tupdesc, - (AttrNumber) 1, - attname, - funcrettype, - -1, - 0, - false); - scanstate->returnsTuple = false; - } - scanstate->tupdesc = tupdesc; - ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot, - tupdesc, false); - - /* - * Other node-specific setup - */ - scanstate->tuplestorestate = NULL; - scanstate->funcexpr = rte->funcexpr; - - scanstate->functionmode = get_functionmode(rte->funcexpr); - - scanstate->csstate.cstate.cs_TupFromTlist = false; - - /* - * initialize tuple type - */ - ExecAssignResultTypeFromTL((Plan *) node, &scanstate->csstate.cstate); - ExecAssignProjectionInfo((Plan *) node, &scanstate->csstate.cstate); - - return TRUE; -} - -int -ExecCountSlotsFunctionScan(FunctionScan *node) -{ - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - FUNCTIONSCAN_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndFunctionScan - * - * frees any storage allocated through C routines. - * ---------------------------------------------------------------- - */ -void -ExecEndFunctionScan(FunctionScan *node) -{ - FunctionScanState *scanstate; - EState *estate; - - /* - * get information from node - */ - scanstate = (FunctionScanState *) node->scan.scanstate; - estate = node->scan.plan.state; - - /* - * 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->csstate.cstate); - ExecFreeExprContext(&scanstate->csstate.cstate); - - /* - * clean out the tuple table - */ - ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->csstate.css_ScanTupleSlot); - - /* - * Release tuplestore resources - */ - if (scanstate->tuplestorestate != NULL) - tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate); - scanstate->tuplestorestate = NULL; -} - -/* ---------------------------------------------------------------- - * ExecFunctionMarkPos - * - * Calls tuplestore to save the current position in the stored file. - * ---------------------------------------------------------------- - */ -void -ExecFunctionMarkPos(FunctionScan *node) -{ - FunctionScanState *scanstate; - - scanstate = (FunctionScanState *) node->scan.scanstate; - - /* - * if we haven't materialized yet, just return. - */ - if (!scanstate->tuplestorestate) - return; - - tuplestore_markpos((Tuplestorestate *) scanstate->tuplestorestate); -} - -/* ---------------------------------------------------------------- - * ExecFunctionRestrPos - * - * Calls tuplestore to restore the last saved file position. - * ---------------------------------------------------------------- - */ -void -ExecFunctionRestrPos(FunctionScan *node) -{ - FunctionScanState *scanstate; - - scanstate = (FunctionScanState *) node->scan.scanstate; - - /* - * if we haven't materialized yet, just return. - */ - if (!scanstate->tuplestorestate) - return; - - tuplestore_restorepos((Tuplestorestate *) scanstate->tuplestorestate); -} - -/* ---------------------------------------------------------------- - * ExecFunctionReScan - * - * Rescans the relation. - * ---------------------------------------------------------------- - */ -void -ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent) -{ - FunctionScanState *scanstate; - - /* - * get information from node - */ - scanstate = (FunctionScanState *) node->scan.scanstate; - - ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot); - - /* - * If we haven't materialized yet, just return. - */ - if (!scanstate->tuplestorestate) - return; - - /* - * Here we have a choice whether to drop the tuplestore (and recompute - * the function outputs) or just rescan it. This should depend on - * whether the function expression contains parameters and/or is - * marked volatile. FIXME soon. - */ - if (node->scan.plan.chgParam != NULL) - { - tuplestore_end((Tuplestorestate *) scanstate->tuplestorestate); - scanstate->tuplestorestate = NULL; - } - else - tuplestore_rescan((Tuplestorestate *) scanstate->tuplestorestate); -} - -/* - * Run the underlying function to get the next tuple - */ -static TupleTableSlot * -function_getonetuple(TupleTableSlot *slot, - Node *expr, - ExprContext *econtext, - TupleDesc tupdesc, - bool returnsTuple, - bool *isNull, - ExprDoneCond *isDone) -{ - HeapTuple tuple; - Datum retDatum; - char nullflag; - - /* - * get the next Datum from the function - */ - retDatum = ExecEvalExprSwitchContext(expr, econtext, isNull, isDone); - - /* - * check to see if we're really done - */ - if (*isDone == ExprEndResult) - slot = NULL; - else - { - if (returnsTuple) - { - /* - * Composite data type, i.e. a table's row type - * function returns pointer to tts?? - */ - slot = (TupleTableSlot *) retDatum; - } - else - { - /* - * Must be a base data type, i.e. scalar - * turn it into a tuple - */ - nullflag = *isNull ? 'n' : ' '; - tuple = heap_formtuple(tupdesc, &retDatum, &nullflag); - - /* - * save the tuple in the scan tuple slot and return the slot. - */ - slot = ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* buffer associated with - * this tuple */ - true); /* pfree this pointer */ - } - } - - return slot; -} - -static FunctionMode -get_functionmode(Node *expr) -{ - /* - * for the moment, hardwire this - */ - return PM_REPEATEDCALL; -} diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c deleted file mode 100644 index 5d7f6a69924..00000000000 --- a/src/backend/executor/nodeGroup.c +++ /dev/null @@ -1,502 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeGroup.c - * Routines to handle group nodes (used for queries with GROUP BY clause). - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * DESCRIPTION - * The Group node is designed for handling queries with a GROUP BY clause. - * Its outer plan must deliver tuples that are sorted in the order - * specified by the grouping columns (ie. tuples from the same group are - * consecutive). That way, we just have to compare adjacent tuples to - * locate group boundaries. - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.47 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/pg_operator.h" -#include "executor/executor.h" -#include "executor/nodeGroup.h" -#include "parser/parse_oper.h" -#include "utils/builtins.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" - -static TupleTableSlot *ExecGroupEveryTuple(Group *node); -static TupleTableSlot *ExecGroupOneTuple(Group *node); - -/* --------------------------------------- - * 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 are 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; - TupleDesc tupdesc; - HeapTuple outerTuple = NULL; - HeapTuple firsttuple; - TupleTableSlot *outerslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; - - /* - * get state info from node - */ - grpstate = node->grpstate; - if (grpstate->grp_done) - return NULL; - estate = node->plan.state; - econtext = grpstate->csstate.cstate.cs_ExprContext; - tupdesc = ExecGetScanType(&grpstate->csstate); - - /* - * We need not call ResetExprContext here because execTuplesMatch will - * reset the per-tuple memory context once per input tuple. - */ - - /* if we haven't returned first tuple of a new group yet ... */ - if (grpstate->grp_useFirstTuple) - { - grpstate->grp_useFirstTuple = FALSE; - - /* - * note we rely on subplan to hold ownership of the tuple for as - * long as we need it; we don't copy it. - */ - ExecStoreTuple(grpstate->grp_firstTuple, - grpstate->csstate.css_ScanTupleSlot, - InvalidBuffer, false); - } - else - { - outerslot = ExecProcNode(outerPlan(node), (Plan *) node); - if (TupIsNull(outerslot)) - { - grpstate->grp_done = TRUE; - return NULL; - } - outerTuple = outerslot->val; - - firsttuple = grpstate->grp_firstTuple; - if (firsttuple == NULL) - { - /* this should occur on the first call only */ - grpstate->grp_firstTuple = heap_copytuple(outerTuple); - } - else - { - /* - * Compare with first tuple and see if this tuple is of the - * same group. - */ - if (!execTuplesMatch(firsttuple, outerTuple, - tupdesc, - node->numCols, node->grpColIdx, - grpstate->eqfunctions, - econtext->ecxt_per_tuple_memory)) - { - /* - * No; save the tuple to return it next time, and return - * NULL - */ - grpstate->grp_useFirstTuple = TRUE; - heap_freetuple(firsttuple); - grpstate->grp_firstTuple = heap_copytuple(outerTuple); - - return NULL; /* signifies the end of the group */ - } - } - - /* - * note we rely on subplan to hold ownership of the tuple for as - * long as we need it; we don't copy it. - */ - ExecStoreTuple(outerTuple, - grpstate->csstate.css_ScanTupleSlot, - InvalidBuffer, 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, NULL); - - 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; - TupleDesc tupdesc; - HeapTuple outerTuple = NULL; - HeapTuple firsttuple; - TupleTableSlot *outerslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; - - /* - * 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; - tupdesc = ExecGetScanType(&grpstate->csstate); - - /* - * We need not call ResetExprContext here because execTuplesMatch will - * reset the per-tuple memory context once per input tuple. - */ - - firsttuple = grpstate->grp_firstTuple; - if (firsttuple == NULL) - { - /* this should occur on the first call only */ - outerslot = ExecProcNode(outerPlan(node), (Plan *) node); - if (TupIsNull(outerslot)) - { - grpstate->grp_done = TRUE; - return NULL; - } - grpstate->grp_firstTuple = firsttuple = - heap_copytuple(outerslot->val); - } - - /* - * find all tuples that belong to a group - */ - for (;;) - { - outerslot = ExecProcNode(outerPlan(node), (Plan *) node); - if (TupIsNull(outerslot)) - { - grpstate->grp_done = TRUE; - outerTuple = NULL; - break; - } - outerTuple = outerslot->val; - - /* - * Compare with first tuple and see if this tuple is of the same - * group. - */ - if (!execTuplesMatch(firsttuple, outerTuple, - tupdesc, - node->numCols, node->grpColIdx, - grpstate->eqfunctions, - econtext->ecxt_per_tuple_memory)) - break; - } - - /* - * form a projection tuple, store it in the result tuple slot and - * return it. - */ - projInfo = grpstate->csstate.cstate.cs_ProjInfo; - - /* - * note we rely on subplan to hold ownership of the tuple for as long - * as we need it; we don't copy it. - */ - ExecStoreTuple(firsttuple, - grpstate->csstate.css_ScanTupleSlot, - InvalidBuffer, false); - econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, NULL); - - /* save outerTuple if we are not done yet */ - if (!grpstate->grp_done) - { - heap_freetuple(firsttuple); - grpstate->grp_firstTuple = heap_copytuple(outerTuple); - } - - 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_useFirstTuple = FALSE; - grpstate->grp_done = FALSE; - grpstate->grp_firstTuple = NULL; - - /* - * create expression context - */ - 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); - - /* - * Precompute fmgr lookup data for inner loop - */ - grpstate->eqfunctions = - execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate), - node->numCols, - node->grpColIdx); - - 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); - ExecFreeExprContext(&grpstate->csstate.cstate); - - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan *) node); - - /* clean up tuple table */ - ExecClearTuple(grpstate->csstate.css_ScanTupleSlot); - if (grpstate->grp_firstTuple != NULL) - { - heap_freetuple(grpstate->grp_firstTuple); - grpstate->grp_firstTuple = NULL; - } -} - -void -ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent) -{ - GroupState *grpstate = node->grpstate; - - grpstate->grp_useFirstTuple = FALSE; - grpstate->grp_done = FALSE; - if (grpstate->grp_firstTuple != NULL) - { - heap_freetuple(grpstate->grp_firstTuple); - grpstate->grp_firstTuple = NULL; - } - - if (((Plan *) node)->lefttree && - ((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); -} - -/***************************************************************************** - * Code shared with nodeUnique.c - *****************************************************************************/ - -/* - * execTuplesMatch - * Return true if two tuples match in all the indicated fields. - * This is used to detect group boundaries in nodeGroup, and to - * decide whether two tuples are distinct or not in nodeUnique. - * - * tuple1, tuple2: the tuples to compare - * tupdesc: tuple descriptor applying to both tuples - * numCols: the number of attributes to be examined - * matchColIdx: array of attribute column numbers - * eqFunctions: array of fmgr lookup info for the equality functions to use - * evalContext: short-term memory context for executing the functions - * - * NB: evalContext is reset each time! - */ -bool -execTuplesMatch(HeapTuple tuple1, - HeapTuple tuple2, - TupleDesc tupdesc, - int numCols, - AttrNumber *matchColIdx, - FmgrInfo *eqfunctions, - MemoryContext evalContext) -{ - MemoryContext oldContext; - bool result; - int i; - - /* Reset and switch into the temp context. */ - MemoryContextReset(evalContext); - oldContext = MemoryContextSwitchTo(evalContext); - - /* - * We cannot report a match without checking all the fields, but we - * can report a non-match as soon as we find unequal fields. So, - * start comparing at the last field (least significant sort key). - * That's the most likely to be different... - */ - result = true; - - for (i = numCols; --i >= 0;) - { - AttrNumber att = matchColIdx[i]; - Datum attr1, - attr2; - bool isNull1, - isNull2; - - attr1 = heap_getattr(tuple1, - att, - tupdesc, - &isNull1); - - attr2 = heap_getattr(tuple2, - att, - tupdesc, - &isNull2); - - if (isNull1 != isNull2) - { - result = false; /* one null and one not; they aren't equal */ - break; - } - - if (isNull1) - continue; /* both are null, treat as equal */ - - /* Apply the type-specific equality function */ - - if (!DatumGetBool(FunctionCall2(&eqfunctions[i], - attr1, attr2))) - { - result = false; /* they aren't equal */ - break; - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - -/* - * execTuplesMatchPrepare - * Look up the equality functions needed for execTuplesMatch. - * The result is a palloc'd array. - */ -FmgrInfo * -execTuplesMatchPrepare(TupleDesc tupdesc, - int numCols, - AttrNumber *matchColIdx) -{ - FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo)); - int i; - - for (i = 0; i < numCols; i++) - { - AttrNumber att = matchColIdx[i]; - Oid typid = tupdesc->attrs[att - 1]->atttypid; - Oid eq_function; - - eq_function = compatible_oper_funcid(makeList1(makeString("=")), - typid, typid, true); - if (!OidIsValid(eq_function)) - elog(ERROR, "Unable to identify an equality operator for type %s", - format_type_be(typid)); - fmgr_info(eq_function, &eqfunctions[i]); - } - - return eqfunctions; -} diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c deleted file mode 100644 index b91c70ab10a..00000000000 --- a/src/backend/executor/nodeHash.c +++ /dev/null @@ -1,736 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeHash.c - * Routines to hash relations for hashjoin - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * $Id: nodeHash.c,v 1.63 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecHash - generate an in-memory hash table of the relation - * ExecInitHash - initialize node and subnodes - * ExecEndHash - shutdown node and subnodes - */ -#include "postgres.h" - -#include <sys/types.h> -#include <math.h> - -#include "access/hash.h" -#include "executor/execdebug.h" -#include "executor/nodeHash.h" -#include "executor/nodeHashjoin.h" -#include "miscadmin.h" -#include "parser/parse_expr.h" -#include "utils/memutils.h" -#include "utils/lsyscache.h" - - -static uint32 hashFunc(Datum key, int len, bool byVal); - -/* ---------------------------------------------------------------- - * 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; - Node *hashkey; - HashJoinTable hashtable; - TupleTableSlot *slot; - ExprContext *econtext; - int nbatch; - int i; - - /* - * get state info from node - */ - - hashstate = node->hashstate; - estate = node->plan.state; - outerNode = outerPlan(node); - - hashtable = hashstate->hashtable; - if (hashtable == NULL) - elog(ERROR, "ExecHash: hash table is NULL."); - - nbatch = hashtable->nbatch; - - if (nbatch > 0) - { - /* - * Open temp files for inner batches, if needed. Note that file - * buffers are palloc'd in regular executor context. - */ - for (i = 0; i < nbatch; i++) - hashtable->innerBatchFile[i] = BufFileCreateTemp(); - } - - /* - * set expression context - */ - hashkey = node->hashkey; - econtext = hashstate->cstate.cs_ExprContext; - - /* - * get all inner tuples and insert into the hash table (or temp files) - */ - for (;;) - { - slot = ExecProcNode(outerNode, (Plan *) node); - if (TupIsNull(slot)) - break; - econtext->ecxt_innertuple = slot; - ExecHashTableInsert(hashtable, econtext, hashkey); - ExecClearTuple(slot); - } - - /* - * 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; - - SO_printf("ExecInitHash: initializing hash node\n"); - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - hashstate = makeNode(HashState); - node->hashstate = hashstate; - hashstate->hashtable = NULL; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - 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; - - /* - * get info from the hash state - */ - hashstate = node->hashstate; - - /* - * free projection info. no need to free result type info because - * that came from the outer plan... - */ - ExecFreeProjectionInfo(&hashstate->cstate); - ExecFreeExprContext(&hashstate->cstate); - - /* - * shut down the subplan - */ - outerPlan = outerPlan(node); - ExecEndNode(outerPlan, (Plan *) node); -} - - -/* ---------------------------------------------------------------- - * ExecHashTableCreate - * - * create a hashtable in shared memory for hashjoin. - * ---------------------------------------------------------------- - */ -HashJoinTable -ExecHashTableCreate(Hash *node) -{ - HashJoinTable hashtable; - Plan *outerNode; - int totalbuckets; - int nbuckets; - int nbatch; - int i; - MemoryContext oldcxt; - - /* - * Get information about the size of the relation to be hashed (it's - * the "outer" subtree of this node, but the inner relation of the - * hashjoin). Compute the appropriate size of the hash table. - */ - outerNode = outerPlan(node); - - ExecChooseHashTableSize(outerNode->plan_rows, outerNode->plan_width, - &totalbuckets, &nbuckets, &nbatch); - -#ifdef HJDEBUG - printf("nbatch = %d, totalbuckets = %d, nbuckets = %d\n", - nbatch, totalbuckets, nbuckets); -#endif - - /* - * Initialize the hash table control block. - * - * The hashtable control block is just palloc'd from the executor's - * per-query memory context. - */ - hashtable = (HashJoinTable) palloc(sizeof(HashTableData)); - hashtable->nbuckets = nbuckets; - hashtable->totalbuckets = totalbuckets; - hashtable->buckets = NULL; - hashtable->nbatch = nbatch; - hashtable->curbatch = 0; - hashtable->innerBatchFile = NULL; - hashtable->outerBatchFile = NULL; - hashtable->innerBatchSize = NULL; - hashtable->outerBatchSize = NULL; - - /* - * Get info about the datatype of the hash key. - */ - get_typlenbyval(exprType(node->hashkey), - &hashtable->typLen, - &hashtable->typByVal); - - /* - * Create temporary memory contexts in which to keep the hashtable - * working storage. See notes in executor/hashjoin.h. - */ - hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext, - "HashTableContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt, - "HashBatchContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* Allocate data that will live for the life of the hashjoin */ - - oldcxt = MemoryContextSwitchTo(hashtable->hashCxt); - - if (nbatch > 0) - { - /* - * allocate and initialize the file arrays in hashCxt - */ - hashtable->innerBatchFile = (BufFile **) - palloc(nbatch * sizeof(BufFile *)); - hashtable->outerBatchFile = (BufFile **) - palloc(nbatch * sizeof(BufFile *)); - hashtable->innerBatchSize = (long *) - palloc(nbatch * sizeof(long)); - hashtable->outerBatchSize = (long *) - palloc(nbatch * sizeof(long)); - for (i = 0; i < nbatch; i++) - { - hashtable->innerBatchFile[i] = NULL; - hashtable->outerBatchFile[i] = NULL; - hashtable->innerBatchSize[i] = 0; - hashtable->outerBatchSize[i] = 0; - } - /* The files will not be opened until later... */ - } - - /* - * Prepare context for the first-scan space allocations; allocate the - * hashbucket array therein, and set each bucket "empty". - */ - MemoryContextSwitchTo(hashtable->batchCxt); - - hashtable->buckets = (HashJoinTuple *) - palloc(nbuckets * sizeof(HashJoinTuple)); - - if (hashtable->buckets == NULL) - elog(ERROR, "Insufficient memory for hash table."); - - for (i = 0; i < nbuckets; i++) - hashtable->buckets[i] = NULL; - - MemoryContextSwitchTo(oldcxt); - - return hashtable; -} - - -/* - * Compute appropriate size for hashtable given the estimated size of the - * relation to be hashed (number of rows and average row width). - * - * Caution: the input is only the planner's estimates, and so can't be - * trusted too far. Apply a healthy fudge factor. - * - * This is exported so that the planner's costsize.c can use it. - */ - -/* Target bucket loading (tuples per bucket) */ -#define NTUP_PER_BUCKET 10 -/* Fudge factor to allow for inaccuracy of input estimates */ -#define FUDGE_FAC 2.0 - -void -ExecChooseHashTableSize(double ntuples, int tupwidth, - int *virtualbuckets, - int *physicalbuckets, - int *numbatches) -{ - int tupsize; - double inner_rel_bytes; - double hash_table_bytes; - int nbatch; - int nbuckets; - int totalbuckets; - int bucketsize; - - /* Force a plausible relation size if no info */ - if (ntuples <= 0.0) - ntuples = 1000.0; - - /* - * Estimate tupsize based on footprint of tuple in hashtable... but - * what about palloc overhead? - */ - tupsize = MAXALIGN(tupwidth) + MAXALIGN(sizeof(HashJoinTupleData)); - inner_rel_bytes = ntuples * tupsize * FUDGE_FAC; - - /* - * Target hashtable size is SortMem kilobytes, but not less than - * sqrt(estimated inner rel size), so as to avoid horrible - * performance. - */ - hash_table_bytes = sqrt(inner_rel_bytes); - if (hash_table_bytes < (SortMem * 1024L)) - hash_table_bytes = SortMem * 1024L; - - /* - * Count the number of hash buckets we want for the whole relation, - * for an average bucket load of NTUP_PER_BUCKET (per virtual - * bucket!). - */ - totalbuckets = (int) ceil(ntuples * FUDGE_FAC / NTUP_PER_BUCKET); - - /* - * Count the number of buckets we think will actually fit in the - * target memory size, at a loading of NTUP_PER_BUCKET (physical - * buckets). NOTE: FUDGE_FAC here determines the fraction of the - * hashtable space reserved to allow for nonuniform distribution of - * hash values. Perhaps this should be a different number from the - * other uses of FUDGE_FAC, but since we have no real good way to pick - * either one... - */ - bucketsize = NTUP_PER_BUCKET * tupsize; - nbuckets = (int) (hash_table_bytes / (bucketsize * FUDGE_FAC)); - if (nbuckets <= 0) - nbuckets = 1; - - if (totalbuckets <= nbuckets) - { - /* - * We have enough space, so no batching. In theory we could even - * reduce nbuckets, but since that could lead to poor behavior if - * estimated ntuples is much less than reality, it seems better to - * make more buckets instead of fewer. - */ - totalbuckets = nbuckets; - nbatch = 0; - } - else - { - /* - * Need to batch; compute how many batches we want to use. Note - * that nbatch doesn't have to have anything to do with the ratio - * totalbuckets/nbuckets; in fact, it is the number of groups we - * will use for the part of the data that doesn't fall into the - * first nbuckets hash buckets. - */ - nbatch = (int) ceil((inner_rel_bytes - hash_table_bytes) / - hash_table_bytes); - if (nbatch <= 0) - nbatch = 1; - } - - /* - * Now, totalbuckets is the number of (virtual) hashbuckets for the - * whole relation, and nbuckets is the number of physical hashbuckets - * we will use in the first pass. Data falling into the first - * nbuckets virtual hashbuckets gets handled in the first pass; - * everything else gets divided into nbatch batches to be processed in - * additional passes. - */ - *virtualbuckets = totalbuckets; - *physicalbuckets = nbuckets; - *numbatches = nbatch; -} - - -/* ---------------------------------------------------------------- - * ExecHashTableDestroy - * - * destroy a hash table - * ---------------------------------------------------------------- - */ -void -ExecHashTableDestroy(HashJoinTable hashtable) -{ - int i; - - /* Make sure all the temp files are closed */ - for (i = 0; i < hashtable->nbatch; i++) - { - if (hashtable->innerBatchFile[i]) - BufFileClose(hashtable->innerBatchFile[i]); - if (hashtable->outerBatchFile[i]) - BufFileClose(hashtable->outerBatchFile[i]); - } - - /* Release working memory (batchCxt is a child, so it goes away too) */ - MemoryContextDelete(hashtable->hashCxt); - - /* And drop the control block */ - pfree(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, - Node *hashkey) -{ - int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey); - TupleTableSlot *slot = econtext->ecxt_innertuple; - HeapTuple heapTuple = slot->val; - - /* - * decide whether to put the tuple in the hash table or a tmp file - */ - if (bucketno < hashtable->nbuckets) - { - /* - * put the tuple in hash table - */ - HashJoinTuple hashTuple; - int hashTupleSize; - - hashTupleSize = MAXALIGN(sizeof(*hashTuple)) + heapTuple->t_len; - hashTuple = (HashJoinTuple) MemoryContextAlloc(hashtable->batchCxt, - hashTupleSize); - if (hashTuple == NULL) - elog(ERROR, "Insufficient memory for hash table."); - memcpy((char *) &hashTuple->htup, - (char *) heapTuple, - sizeof(hashTuple->htup)); - hashTuple->htup.t_datamcxt = hashtable->batchCxt; - hashTuple->htup.t_data = (HeapTupleHeader) - (((char *) hashTuple) + MAXALIGN(sizeof(*hashTuple))); - memcpy((char *) hashTuple->htup.t_data, - (char *) heapTuple->t_data, - heapTuple->t_len); - hashTuple->next = hashtable->buckets[bucketno]; - hashtable->buckets[bucketno] = hashTuple; - } - else - { - /* - * put the tuple into a tmp file for other batches - */ - int batchno = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) / - (hashtable->totalbuckets - hashtable->nbuckets); - - hashtable->innerBatchSize[batchno]++; - ExecHashJoinSaveTuple(heapTuple, - hashtable->innerBatchFile[batchno]); - } -} - -/* ---------------------------------------------------------------- - * ExecHashGetBucket - * - * Get the hash value for a tuple - * ---------------------------------------------------------------- - */ -int -ExecHashGetBucket(HashJoinTable hashtable, - ExprContext *econtext, - Node *hashkey) -{ - int bucketno; - Datum keyval; - bool isNull; - MemoryContext oldContext; - - /* - * We reset the eval context each time to reclaim any memory leaked in - * the hashkey expression or hashFunc itself. - */ - ResetExprContext(econtext); - - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Get the join attribute value of the tuple - */ - keyval = ExecEvalExpr(hashkey, econtext, &isNull, NULL); - - /* - * Compute the hash function - */ - if (isNull) - bucketno = 0; - else - { - bucketno = hashFunc(keyval, - (int) hashtable->typLen, - hashtable->typByVal) - % (uint32) hashtable->totalbuckets; - } - -#ifdef HJDEBUG - if (bucketno >= hashtable->nbuckets) - printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno); - else - printf("hash(%ld) = %d\n", (long) keyval, bucketno); -#endif - - MemoryContextSwitchTo(oldContext); - - return bucketno; -} - -/* ---------------------------------------------------------------- - * ExecScanHashBucket - * - * scan a hash bucket of matches - * ---------------------------------------------------------------- - */ -HeapTuple -ExecScanHashBucket(HashJoinState *hjstate, - List *hjclauses, - ExprContext *econtext) -{ - HashJoinTable hashtable = hjstate->hj_HashTable; - HashJoinTuple hashTuple = hjstate->hj_CurTuple; - - /* - * hj_CurTuple is NULL to start scanning a new bucket, or the address - * of the last tuple returned from the current bucket. - */ - if (hashTuple == NULL) - hashTuple = hashtable->buckets[hjstate->hj_CurBucketNo]; - else - hashTuple = hashTuple->next; - - while (hashTuple != NULL) - { - HeapTuple heapTuple = &hashTuple->htup; - TupleTableSlot *inntuple; - - /* insert hashtable's tuple into exec slot so ExecQual sees it */ - inntuple = ExecStoreTuple(heapTuple, /* tuple to store */ - hjstate->hj_HashTupleSlot, /* slot */ - InvalidBuffer, - false); /* do not pfree this tuple */ - econtext->ecxt_innertuple = inntuple; - - /* reset temp memory each time to avoid leaks from qual expression */ - ResetExprContext(econtext); - - if (ExecQual(hjclauses, econtext, false)) - { - hjstate->hj_CurTuple = hashTuple; - return heapTuple; - } - - hashTuple = hashTuple->next; - } - - /* - * no match - */ - return NULL; -} - -/* ---------------------------------------------------------------- - * hashFunc - * - * the hash function for hash joins - * - * XXX this probably ought to be replaced with datatype-specific - * hash functions, such as those already implemented for hash indexes. - * ---------------------------------------------------------------- - */ -static uint32 -hashFunc(Datum key, int len, bool byVal) -{ - unsigned char *k; - - if (byVal) - { - /* - * If it's a by-value data type, just hash the whole Datum value. - * This assumes that datatypes narrower than Datum are consistently - * padded (either zero-extended or sign-extended, but not random - * bits) to fill Datum; see the XXXGetDatum macros in postgres.h. - * NOTE: it would not work to do hash_any(&key, len) since this - * would get the wrong bytes on a big-endian machine. - */ - k = (unsigned char *) &key; - len = sizeof(Datum); - } - else - { - /* - * If this is a variable length type, then 'key' 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). 'key' points to the beginning - * of the varlena struct, so we have to use "VARDATA" to find the - * beginning of the "real" data. Also, we have to be careful to - * detoast the datum if it's toasted. (We don't worry about - * freeing the detoasted copy; that happens for free when the - * per-tuple memory context is reset in ExecHashGetBucket.) - */ - if (len < 0) - { - struct varlena *vkey = PG_DETOAST_DATUM(key); - - len = VARSIZE(vkey) - VARHDRSZ; - k = (unsigned char *) VARDATA(vkey); - } - else - k = (unsigned char *) DatumGetPointer(key); - } - - return DatumGetUInt32(hash_any(k, len)); -} - -/* ---------------------------------------------------------------- - * ExecHashTableReset - * - * reset hash table header for new batch - * - * ntuples is the number of tuples in the inner relation's batch - * (which we currently don't actually use...) - * ---------------------------------------------------------------- - */ -void -ExecHashTableReset(HashJoinTable hashtable, long ntuples) -{ - MemoryContext oldcxt; - int nbuckets = hashtable->nbuckets; - int i; - - /* - * Release all the hash buckets and tuples acquired in the prior pass, - * and reinitialize the context for a new pass. - */ - MemoryContextReset(hashtable->batchCxt); - oldcxt = MemoryContextSwitchTo(hashtable->batchCxt); - - /* - * We still use the same number of physical buckets as in the first - * pass. (It could be different; but we already decided how many - * buckets would be appropriate for the allowed memory, so stick with - * that number.) We MUST set totalbuckets to equal nbuckets, because - * from now on no tuples will go out to temp files; there are no more - * virtual buckets, only real buckets. (This implies that tuples will - * go into different bucket numbers than they did on the first pass, - * but that's OK.) - */ - hashtable->totalbuckets = nbuckets; - - /* Reallocate and reinitialize the hash bucket headers. */ - hashtable->buckets = (HashJoinTuple *) - palloc(nbuckets * sizeof(HashJoinTuple)); - - if (hashtable->buckets == NULL) - elog(ERROR, "Insufficient memory for hash table."); - - for (i = 0; i < nbuckets; i++) - hashtable->buckets[i] = NULL; - - MemoryContextSwitchTo(oldcxt); -} - -void -ExecReScanHash(Hash *node, ExprContext *exprCtxt, Plan *parent) -{ - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); -} diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c deleted file mode 100644 index b741f8983fd..00000000000 --- a/src/backend/executor/nodeHashjoin.c +++ /dev/null @@ -1,713 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeHashjoin.c - * Routines to handle hash join nodes - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.40 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include <sys/types.h> - -#include "postgres.h" - -#include "executor/executor.h" -#include "executor/nodeHash.h" -#include "executor/nodeHashjoin.h" -#include "optimizer/clauses.h" -#include "utils/memutils.h" - - -static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, - HashJoinState *hjstate); -static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate, - BufFile *file, - TupleTableSlot *tupleSlot); -static int ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable); -static int ExecHashJoinNewBatch(HashJoinState *hjstate); - - -/* ---------------------------------------------------------------- - * 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 *joinqual; - List *otherqual; - ScanDirection dir; - TupleTableSlot *inntuple; - Node *outerVar; - ExprContext *econtext; - ExprDoneCond isDone; - HashJoinTable hashtable; - HeapTuple curtuple; - TupleTableSlot *outerTupleSlot; - TupleTableSlot *innerTupleSlot; - int i; - bool hashPhaseDone; - - /* - * get information from HashJoin node - */ - hjstate = node->hashjoinstate; - hjclauses = node->hashclauses; - clause = lfirst(hjclauses); - estate = node->join.plan.state; - joinqual = node->join.joinqual; - otherqual = node->join.plan.qual; - hashNode = (Hash *) innerPlan(node); - outerNode = outerPlan(node); - hashPhaseDone = hjstate->hj_hashdone; - dir = estate->es_direction; - - /* - * get information from HashJoin state - */ - hashtable = hjstate->hj_HashTable; - econtext = hjstate->jstate.cs_ExprContext; - - /* - * Check to see if we're still projecting out tuples from a previous - * join tuple (because there is a function-returning-set in the - * projection expressions). If so, try to project another one. - */ - if (hjstate->jstate.cs_TupFromTlist) - { - TupleTableSlot *result; - - result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - hjstate->jstate.cs_TupFromTlist = false; - } - - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. Note this can't - * happen until we're done projecting out tuples from a join tuple. - */ - ResetExprContext(econtext); - - /* - * if this is the first call, build the hash table for inner relation - */ - if (!hashPhaseDone) - { /* if the hash phase not completed */ - if (hashtable == NULL) - { /* if the hash table has not been created */ - - /* - * create the hash table - */ - hashtable = ExecHashTableCreate(hashNode); - hjstate->hj_HashTable = hashtable; - hjstate->hj_InnerHashKey = hashNode->hashkey; - - /* - * execute the Hash node, to build the hash table - */ - hashNode->hashstate->hashtable = hashtable; - innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node); - } - hjstate->hj_hashdone = true; - - /* - * Open temp files for outer batches, if needed. Note that file - * buffers are palloc'd in regular executor context. - */ - for (i = 0; i < hashtable->nbatch; i++) - hashtable->outerBatchFile[i] = BufFileCreateTemp(); - } - else if (hashtable == NULL) - return NULL; - - /* - * Now get an outer tuple and probe into the hash table for matches - */ - outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot; - outerVar = (Node *) get_leftop(clause); - - for (;;) - { - /* - * If we don't have an outer tuple, get the next one - */ - if (hjstate->hj_NeedNewOuter) - { - outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode, - (Plan *) node, - hjstate); - if (TupIsNull(outerTupleSlot)) - { - /* - * when the last batch runs out, clean up and exit - */ - ExecHashTableDestroy(hashtable); - hjstate->hj_HashTable = NULL; - return NULL; - } - - hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - hjstate->hj_NeedNewOuter = false; - hjstate->hj_MatchedOuter = false; - - /* - * now we have an outer tuple, find the corresponding bucket - * for this tuple from the hash table - */ - hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext, - outerVar); - hjstate->hj_CurTuple = NULL; - - /* - * Now we've got an outer tuple and the corresponding hash - * bucket, but this tuple may not belong to the current batch. - * This need only be checked in the first pass. - */ - if (hashtable->curbatch == 0) - { - int batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo, - hashtable); - - if (batch > 0) - { - /* - * Need to postpone this outer tuple to a later batch. - * Save it in the corresponding outer-batch file. - */ - int batchno = batch - 1; - - hashtable->outerBatchSize[batchno]++; - ExecHashJoinSaveTuple(outerTupleSlot->val, - hashtable->outerBatchFile[batchno]); - hjstate->hj_NeedNewOuter = true; - continue; /* loop around for a new outer tuple */ - } - } - } - - /* - * OK, scan the selected hash bucket for matches - */ - for (;;) - { - curtuple = ExecScanHashBucket(hjstate, - hjclauses, - econtext); - if (curtuple == NULL) - break; /* out of matches */ - - /* - * we've got a match, but still need to test non-hashed quals - */ - inntuple = ExecStoreTuple(curtuple, - hjstate->hj_HashTupleSlot, - InvalidBuffer, - false); /* don't pfree this tuple */ - econtext->ecxt_innertuple = inntuple; - - /* reset temp memory each time to avoid leaks from qual expr */ - ResetExprContext(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. - * - * Only the joinquals determine MatchedOuter status, but all - * quals must pass to actually return the tuple. - */ - if (ExecQual(joinqual, econtext, false)) - { - hjstate->hj_MatchedOuter = true; - - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) - { - TupleTableSlot *result; - - result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - hjstate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - } - - /* - * Now the current outer tuple has run out of matches, so check - * whether to emit a dummy outer-join tuple. If not, loop around - * to get a new outer tuple. - */ - hjstate->hj_NeedNewOuter = true; - - if (!hjstate->hj_MatchedOuter && - node->join.jointype == JOIN_LEFT) - { - /* - * We are doing an outer join and there were no join matches - * for this outer tuple. Generate a fake join tuple with - * nulls for the inner tuple, and return it if it passes the - * non-join quals. - */ - econtext->ecxt_innertuple = hjstate->hj_NullInnerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification was satisfied so we project and return - * the slot containing the result tuple using - * ExecProject(). - */ - TupleTableSlot *result; - - result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - hjstate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - } -} - -/* ---------------------------------------------------------------- - * 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.plan.state = estate; - - /* - * create state structure - */ - hjstate = makeNode(HashJoinState); - node->hashjoinstate = hjstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &hjstate->jstate); - - /* - * initializes child nodes - */ - outerNode = outerPlan((Plan *) node); - hashNode = (Hash *) innerPlan((Plan *) node); - - ExecInitNode(outerNode, estate, (Plan *) node); - ExecInitNode((Plan *) hashNode, estate, (Plan *) node); - -#define HASHJOIN_NSLOTS 3 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &hjstate->jstate); - hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate); - - switch (node->join.jointype) - { - case JOIN_INNER: - break; - case JOIN_LEFT: - hjstate->hj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, - ExecGetTupType((Plan *) hashNode)); - break; - default: - elog(ERROR, "ExecInitHashJoin: unsupported join type %d", - (int) node->join.jointype); - } - - /* - * 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; - } - - /* - * initialize tuple type and projection info - */ - ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate); - ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate); - - ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot, - ExecGetTupType(outerNode), - false); - - /* - * initialize hash-specific info - */ - - hjstate->hj_hashdone = false; - - hjstate->hj_HashTable = (HashJoinTable) NULL; - hjstate->hj_CurBucketNo = 0; - hjstate->hj_CurTuple = (HashJoinTuple) NULL; - hjstate->hj_InnerHashKey = (Node *) NULL; - - hjstate->jstate.cs_OuterTupleSlot = NULL; - hjstate->jstate.cs_TupFromTlist = false; - hjstate->hj_NeedNewOuter = true; - hjstate->hj_MatchedOuter = 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); - ExecFreeExprContext(&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) -{ - HashJoinTable hashtable = hjstate->hj_HashTable; - int curbatch = hashtable->curbatch; - TupleTableSlot *slot; - - if (curbatch == 0) - { /* if it is the first pass */ - slot = ExecProcNode(node, parent); - if (!TupIsNull(slot)) - return slot; - - /* - * We have just reached the end of the first pass. Try to switch - * to a saved batch. - */ - curbatch = ExecHashJoinNewBatch(hjstate); - } - - /* - * Try to read from a temp file. Loop allows us to advance to new - * batch as needed. - */ - while (curbatch <= hashtable->nbatch) - { - slot = ExecHashJoinGetSavedTuple(hjstate, - hashtable->outerBatchFile[curbatch - 1], - hjstate->hj_OuterTupleSlot); - if (!TupIsNull(slot)) - return slot; - curbatch = ExecHashJoinNewBatch(hjstate); - } - - /* Out of batches... */ - return NULL; -} - -/* ---------------------------------------------------------------- - * ExecHashJoinGetSavedTuple - * - * read the next tuple from a tmp file - * ---------------------------------------------------------------- - */ - -static TupleTableSlot * -ExecHashJoinGetSavedTuple(HashJoinState *hjstate, - BufFile *file, - TupleTableSlot *tupleSlot) -{ - HeapTupleData htup; - size_t nread; - HeapTuple heapTuple; - - nread = BufFileRead(file, (void *) &htup, sizeof(HeapTupleData)); - if (nread == 0) - return NULL; /* end of file */ - if (nread != sizeof(HeapTupleData)) - elog(ERROR, "Read from hashjoin temp file failed"); - heapTuple = palloc(HEAPTUPLESIZE + htup.t_len); - memcpy((char *) heapTuple, (char *) &htup, sizeof(HeapTupleData)); - heapTuple->t_datamcxt = CurrentMemoryContext; - heapTuple->t_data = (HeapTupleHeader) - ((char *) heapTuple + HEAPTUPLESIZE); - nread = BufFileRead(file, (void *) heapTuple->t_data, htup.t_len); - if (nread != (size_t) htup.t_len) - elog(ERROR, "Read from hashjoin temp file failed"); - return ExecStoreTuple(heapTuple, tupleSlot, InvalidBuffer, true); -} - -/* ---------------------------------------------------------------- - * ExecHashJoinNewBatch - * - * switch to a new hashjoin batch - * ---------------------------------------------------------------- - */ -static int -ExecHashJoinNewBatch(HashJoinState *hjstate) -{ - HashJoinTable hashtable = hjstate->hj_HashTable; - int nbatch = hashtable->nbatch; - int newbatch = hashtable->curbatch + 1; - long *innerBatchSize = hashtable->innerBatchSize; - long *outerBatchSize = hashtable->outerBatchSize; - BufFile *innerFile; - TupleTableSlot *slot; - ExprContext *econtext; - Node *innerhashkey; - - if (newbatch > 1) - { - /* - * We no longer need the previous outer batch file; close it right - * away to free disk space. - */ - BufFileClose(hashtable->outerBatchFile[newbatch - 2]); - hashtable->outerBatchFile[newbatch - 2] = NULL; - } - - /* - * We can skip over any batches that are empty on either side. Release - * associated temp files right away. - */ - while (newbatch <= nbatch && - (innerBatchSize[newbatch - 1] == 0L || - outerBatchSize[newbatch - 1] == 0L)) - { - BufFileClose(hashtable->innerBatchFile[newbatch - 1]); - hashtable->innerBatchFile[newbatch - 1] = NULL; - BufFileClose(hashtable->outerBatchFile[newbatch - 1]); - hashtable->outerBatchFile[newbatch - 1] = NULL; - newbatch++; - } - - if (newbatch > nbatch) - return newbatch; /* no more batches */ - - /* - * Rewind inner and outer batch files for this batch, so that we can - * start reading them. - */ - if (BufFileSeek(hashtable->outerBatchFile[newbatch - 1], 0, 0L, SEEK_SET)) - elog(ERROR, "Failed to rewind hash temp file"); - - innerFile = hashtable->innerBatchFile[newbatch - 1]; - - if (BufFileSeek(innerFile, 0, 0L, SEEK_SET)) - elog(ERROR, "Failed to rewind hash temp file"); - - /* - * Reload the hash table with the new inner batch - */ - ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]); - - econtext = hjstate->jstate.cs_ExprContext; - innerhashkey = hjstate->hj_InnerHashKey; - - while ((slot = ExecHashJoinGetSavedTuple(hjstate, - innerFile, - hjstate->hj_HashTupleSlot)) - && !TupIsNull(slot)) - { - econtext->ecxt_innertuple = slot; - ExecHashTableInsert(hashtable, econtext, innerhashkey); - } - - /* - * after we build the hash table, the inner batch file is no longer - * needed - */ - BufFileClose(innerFile); - hashtable->innerBatchFile[newbatch - 1] = NULL; - - hashtable->curbatch = newbatch; - return newbatch; -} - -/* ---------------------------------------------------------------- - * ExecHashJoinGetBatch - * - * determine the batch number for a bucketno - * +----------------+-------+-------+ ... +-------+ - * 0 nbuckets totalbuckets - * batch 0 1 2 ... - * ---------------------------------------------------------------- - */ -static int -ExecHashJoinGetBatch(int bucketno, HashJoinTable hashtable) -{ - int b; - - if (bucketno < hashtable->nbuckets || hashtable->nbatch == 0) - return 0; - - b = (hashtable->nbatch * (bucketno - hashtable->nbuckets)) / - (hashtable->totalbuckets - hashtable->nbuckets); - return b + 1; -} - -/* ---------------------------------------------------------------- - * ExecHashJoinSaveTuple - * - * save a tuple to a tmp file. - * - * The data recorded in the file for each tuple is an image of its - * HeapTupleData (with meaningless t_data pointer) followed by the - * HeapTupleHeader and tuple data. - * ---------------------------------------------------------------- - */ - -void -ExecHashJoinSaveTuple(HeapTuple heapTuple, - BufFile *file) -{ - size_t written; - - written = BufFileWrite(file, (void *) heapTuple, sizeof(HeapTupleData)); - if (written != sizeof(HeapTupleData)) - elog(ERROR, "Write to hashjoin temp file failed"); - written = BufFileWrite(file, (void *) heapTuple->t_data, heapTuple->t_len); - if (written != (size_t) heapTuple->t_len) - elog(ERROR, "Write to hashjoin temp file failed"); -} - -void -ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent) -{ - HashJoinState *hjstate = node->hashjoinstate; - - if (!hjstate->hj_hashdone) - return; - - hjstate->hj_hashdone = false; - - /* - * Unfortunately, currently we have to destroy hashtable in all - * cases... - */ - if (hjstate->hj_HashTable) - { - ExecHashTableDestroy(hjstate->hj_HashTable); - hjstate->hj_HashTable = NULL; - } - - hjstate->hj_CurBucketNo = 0; - hjstate->hj_CurTuple = (HashJoinTuple) NULL; - hjstate->hj_InnerHashKey = (Node *) NULL; - - hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL; - hjstate->jstate.cs_TupFromTlist = false; - hjstate->hj_NeedNewOuter = true; - hjstate->hj_MatchedOuter = false; - - /* - * if chgParam of subnodes is not null then plans will be re-scanned - * by first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); - if (((Plan *) node)->righttree->chgParam == NULL) - ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node); -} diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c deleted file mode 100644 index d33147793f8..00000000000 --- a/src/backend/executor/nodeIndexscan.c +++ /dev/null @@ -1,1050 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeIndexscan.c - * Routines to support indexes and indexed scans of relations - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.69 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * 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. - */ -#include "postgres.h" - -#include "access/genam.h" -#include "access/heapam.h" -#include "executor/execdebug.h" -#include "executor/nodeIndexscan.h" -#include "nodes/nodeFuncs.h" -#include "optimizer/clauses.h" -#include "parser/parsetree.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; - ExprContext *econtext; - ScanDirection direction; - IndexScanDescPtr scanDescs; - IndexScanDesc scandesc; - HeapTuple tuple; - TupleTableSlot *slot; - int numIndices; - bool bBackward; - int indexNumber; - - /* - * extract necessary information from index scan node - */ - estate = node->scan.plan.state; - direction = estate->es_direction; - if (ScanDirectionIsBackward(node->indxorderdir)) - { - if (ScanDirectionIsForward(direction)) - direction = BackwardScanDirection; - else if (ScanDirectionIsBackward(direction)) - direction = ForwardScanDirection; - } - scanstate = node->scan.scanstate; - indexstate = node->indxstate; - scanDescs = indexstate->iss_ScanDescs; - numIndices = indexstate->iss_NumIndices; - econtext = scanstate->cstate.cs_ExprContext; - slot = scanstate->css_ScanTupleSlot; - - /* - * Check if we are evaluating PlanQual for tuple of this relation. - * Additional checking is not good, but no other way for now. We could - * introduce new nodes for this case and handle IndexScan --> NewNode - * switching in Init/ReScan plan... - */ - if (estate->es_evTuple != NULL && - estate->es_evTuple[node->scan.scanrelid - 1] != NULL) - { - List *qual; - - ExecClearTuple(slot); - if (estate->es_evTupleNull[node->scan.scanrelid - 1]) - return slot; /* return empty slot */ - - ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1], - slot, InvalidBuffer, false); - - /* Does the tuple meet any of the OR'd indxqual conditions? */ - econtext->ecxt_scantuple = slot; - - ResetExprContext(econtext); - - foreach(qual, node->indxqualorig) - { - if (ExecQual((List *) lfirst(qual), econtext, false)) - break; - } - if (qual == NIL) /* would not be returned by indices */ - slot->val = NULL; - - /* Flag for the next call that no more tuples */ - estate->es_evTupleNull[node->scan.scanrelid - 1] = true; - - return slot; - } - - /* - * ok, now that we have what we need, fetch an index tuple. if - * scanning this index succeeded then return the appropriate heap - * tuple.. else return NULL. - */ - bBackward = ScanDirectionIsBackward(direction); - if (bBackward) - { - indexNumber = numIndices - indexstate->iss_IndexPtr - 1; - if (indexNumber < 0) - { - indexNumber = 0; - indexstate->iss_IndexPtr = numIndices - 1; - } - } - else - { - if ((indexNumber = indexstate->iss_IndexPtr) < 0) - { - indexNumber = 0; - indexstate->iss_IndexPtr = 0; - } - } - while (indexNumber < numIndices) - { - scandesc = scanDescs[indexstate->iss_IndexPtr]; - while ((tuple = index_getnext(scandesc, direction)) != NULL) - { - /* - * store the scanned tuple in the scan tuple slot of the - * scan state. Note: we pass 'false' because tuples - * returned by amgetnext are pointers onto disk pages and - * must not be pfree()'d. - */ - ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - scandesc->xs_cbuf, /* buffer containing tuple */ - false); /* don't pfree */ - - /* - * We must check to see if the current tuple was already - * matched by an earlier index, so we don't double-report - * it. We do this by passing the tuple through ExecQual - * and checking for failure with all previous - * qualifications. - */ - if (indexstate->iss_IndexPtr > 0) - { - bool prev_matches = false; - int prev_index; - List *qual; - - econtext->ecxt_scantuple = slot; - ResetExprContext(econtext); - qual = node->indxqualorig; - for (prev_index = 0; - prev_index < indexstate->iss_IndexPtr; - prev_index++) - { - if (ExecQual((List *) lfirst(qual), econtext, false)) - { - prev_matches = true; - break; - } - qual = lnext(qual); - } - if (prev_matches) - { - /* Duplicate, so drop it and loop back for another */ - ExecClearTuple(slot); - continue; - } - } - - return slot; /* OK to return tuple */ - } - - if (indexNumber < numIndices) - { - indexNumber++; - if (bBackward) - indexstate->iss_IndexPtr--; - else - indexstate->iss_IndexPtr++; - } - } - - /* - * 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) -{ - IndexScanState *indexstate = node->indxstate; - - /* - * If we have runtime keys and they've not already been set up, do it - * now. - */ - if (indexstate->iss_RuntimeKeyInfo && !indexstate->iss_RuntimeKeysReady) - ExecReScan((Plan *) node, NULL, NULL); - - /* - * use IndexNext as access method - */ - return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext); -} - -/* ---------------------------------------------------------------- - * 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; - ExprContext *econtext; - int numIndices; - IndexScanDescPtr scanDescs; - ScanKey *scanKeys; - int **runtimeKeyInfo; - int *numScanKeys; - int i; - int j; - - estate = node->scan.plan.state; - indexstate = node->indxstate; - econtext = indexstate->iss_RuntimeContext; /* context for runtime - * keys */ - numIndices = indexstate->iss_NumIndices; - scanDescs = indexstate->iss_ScanDescs; - scanKeys = indexstate->iss_ScanKeys; - runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo; - numScanKeys = indexstate->iss_NumScanKeys; - - if (econtext) - { - /* - * If we are being passed an outer tuple, save it for runtime key - * calc. We also need to link it into the "regular" per-tuple - * econtext, so it can be used during indexqualorig evaluations. - */ - if (exprCtxt != NULL) - { - ExprContext *stdecontext; - - econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple; - stdecontext = node->scan.scanstate->cstate.cs_ExprContext; - stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple; - } - - /* - * Reset the runtime-key context so we don't leak memory as each - * outer tuple is scanned. Note this assumes that we will - * recalculate *all* runtime keys on each call. - */ - ResetExprContext(econtext); - } - - /* - * If we are doing runtime key calculations (ie, the index keys depend - * on data from an outer scan), compute the new key values - */ - if (runtimeKeyInfo) - { - List *indxqual; - - indxqual = node->indxqual; - for (i = 0; i < numIndices; i++) - { - List *qual = lfirst(indxqual); - int n_keys; - ScanKey scan_keys; - int *run_keys; - List *listscan; - - indxqual = lnext(indxqual); - n_keys = numScanKeys[i]; - scan_keys = scanKeys[i]; - run_keys = runtimeKeyInfo[i]; - - listscan = qual; - for (j = 0; j < n_keys; j++) - { - Expr *clause = lfirst(listscan); - - listscan = lnext(listscan); - - /* - * 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. - * - * Note: the result of the eval could be a pass-by-ref value - * that's stored in the outer scan's tuple, not in - * econtext->ecxt_per_tuple_memory. We assume that the - * outer tuple will stay put throughout our scan. If this - * is wrong, we could copy the result into our context - * explicitly, but I think that's not necessary... - */ - if (run_keys[j] != NO_OP) - { - Node *scanexpr; - Datum scanvalue; - bool isNull; - - scanexpr = (run_keys[j] == RIGHT_OP) ? - (Node *) get_rightop(clause) : - (Node *) get_leftop(clause); - - scanvalue = ExecEvalExprSwitchContext(scanexpr, - econtext, - &isNull, - NULL); - scan_keys[j].sk_argument = scanvalue; - if (isNull) - scan_keys[j].sk_flags |= SK_ISNULL; - else - scan_keys[j].sk_flags &= ~SK_ISNULL; - } - } - } - - indexstate->iss_RuntimeKeysReady = true; - } - - /* If this is re-scanning of PlanQual ... */ - if (estate->es_evTuple != NULL && - estate->es_evTuple[node->scan.scanrelid - 1] != NULL) - { - estate->es_evTupleNull[node->scan.scanrelid - 1] = false; - return; - } - - /* reset index scans */ - if (ScanDirectionIsBackward(node->indxorderdir)) - indexstate->iss_IndexPtr = numIndices; - else - indexstate->iss_IndexPtr = -1; - - for (i = 0; i < numIndices; i++) - { - IndexScanDesc scan = scanDescs[i]; - ScanKey skey = scanKeys[i]; - - index_rescan(scan, skey); - } -} - -/* ---------------------------------------------------------------- - * ExecEndIndexScan - * - * old comments - * Releases any storage allocated through C routines. - * Returns nothing. - * ---------------------------------------------------------------- - */ -void -ExecEndIndexScan(IndexScan *node) -{ - CommonScanState *scanstate; - IndexScanState *indexstate; - int **runtimeKeyInfo; - ScanKey *scanKeys; - List *indxqual; - int *numScanKeys; - int numIndices; - Relation relation; - RelationPtr indexRelationDescs; - IndexScanDescPtr indexScanDescs; - int i; - - scanstate = node->scan.scanstate; - indexstate = node->indxstate; - indxqual = node->indxqual; - runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo; - - /* - * extract information from the node - */ - numIndices = indexstate->iss_NumIndices; - scanKeys = indexstate->iss_ScanKeys; - numScanKeys = indexstate->iss_NumScanKeys; - indexRelationDescs = indexstate->iss_RelationDescs; - indexScanDescs = indexstate->iss_ScanDescs; - relation = scanstate->css_currentRelation; - - /* - * 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); - ExecFreeExprContext(&scanstate->cstate); - if (indexstate->iss_RuntimeContext) - FreeExprContext(indexstate->iss_RuntimeContext); - - /* - * close the index relations - */ - for (i = 0; i < numIndices; i++) - { - if (indexScanDescs[i] != NULL) - index_endscan(indexScanDescs[i]); - - if (indexRelationDescs[i] != NULL) - index_close(indexRelationDescs[i]); - } - - /* - * close the heap relation. - * - * Currently, we do not release the AccessShareLock acquired by - * ExecInitIndexScan. This lock should be held till end of transaction. - * (There is a faction that considers this too much locking, however.) - */ - heap_close(relation, NoLock); - - /* - * free the scan keys used in scanning the indices - */ - for (i = 0; i < numIndices; i++) - { - if (scanKeys[i] != NULL) - pfree(scanKeys[i]); - } - pfree(scanKeys); - pfree(numScanKeys); - - if (runtimeKeyInfo) - { - for (i = 0; i < numIndices; i++) - { - if (runtimeKeyInfo[i] != NULL) - pfree(runtimeKeyInfo[i]); - } - pfree(runtimeKeyInfo); - } - - /* - * clear out tuple table slots - */ - ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->css_ScanTupleSlot); -} - -/* ---------------------------------------------------------------- - * 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_MarkIndexPtr = indexstate->iss_IndexPtr; - indexScanDescs = indexstate->iss_ScanDescs; - scanDesc = indexScanDescs[indexPtr]; - -#ifdef NOT_USED - IndexScanMarkPosition(scanDesc); -#endif - index_markpos(scanDesc); -} - -/* ---------------------------------------------------------------- - * 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 = indexstate->iss_MarkIndexPtr; - indexScanDescs = indexstate->iss_ScanDescs; - scanDesc = indexScanDescs[indexPtr]; - -#ifdef NOT_USED - IndexScanRestorePosition(scanDesc); -#endif - index_restrpos(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 descriptors. - * - * 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; - List *listscan; - int i; - int numIndices; - int indexPtr; - ScanKey *scanKeys; - int *numScanKeys; - RelationPtr indexDescs; - IndexScanDescPtr scanDescs; - int **runtimeKeyInfo; - bool have_runtime_keys; - RangeTblEntry *rtentry; - Index relid; - Oid reloid; - Relation currentRelation; - - /* - * assign execution state to node - */ - node->scan.plan.state = estate; - - /* - * Part 1) initialize scan state - * - * create new CommonScanState for node - */ - scanstate = makeNode(CommonScanState); - node->scan.scanstate = scanstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &scanstate->cstate); - -#define INDEXSCAN_NSLOTS 2 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(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 = -1; - indexstate->iss_ScanKeys = NULL; - indexstate->iss_NumScanKeys = NULL; - indexstate->iss_RuntimeKeyInfo = NULL; - indexstate->iss_RuntimeContext = NULL; - indexstate->iss_RuntimeKeysReady = false; - indexstate->iss_RelationDescs = NULL; - indexstate->iss_ScanDescs = NULL; - - node->indxstate = indexstate; - - /* - * get the index node information - */ - indxid = node->indxid; - numIndices = length(indxid); - indexPtr = -1; - - 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)); - indexDescs = (RelationPtr) palloc(numIndices * sizeof(Relation)); - scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc)); - - /* - * initialize space for runtime key info (may not be needed) - */ - have_runtime_keys = false; - runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *)); - - /* - * build the index scan keys from the index qualification - */ - indxqual = node->indxqual; - for (i = 0; i < numIndices; i++) - { - int j; - List *qual; - int n_keys; - ScanKey scan_keys; - int *run_keys; - - qual = lfirst(indxqual); - indxqual = lnext(indxqual); - n_keys = length(qual); - scan_keys = (n_keys <= 0) ? (ScanKey) NULL : - (ScanKey) palloc(n_keys * sizeof(ScanKeyData)); - run_keys = (n_keys <= 0) ? (int *) NULL : - (int *) palloc(n_keys * sizeof(int)); - - CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext); - - /* - * for each opclause in the given qual, convert each qual's - * opclause into a single scan key - */ - listscan = qual; - for (j = 0; j < n_keys; j++) - { - Expr *clause; /* one clause of index qual */ - Oper *op; /* operator used in clause */ - Node *leftop; /* expr on lhs of operator */ - Node *rightop; /* expr on rhs ... */ - bits16 flags = 0; - - int scanvar; /* which var identifies varattno */ - AttrNumber varattno = 0; /* att number used in scan */ - Oid opid; /* operator id used in scan */ - Datum scanvalue = 0; /* value used in scan (if const) */ - - /* - * extract clause information from the qualification - */ - clause = lfirst(listscan); - listscan = lnext(listscan); - - op = (Oper *) clause->oper; - if (!IsA(clause, Expr) ||!IsA(op, Oper)) - elog(ERROR, "ExecInitIndexScan: indxqual not an opclause!"); - - opid = op->opid; - - /* - * Here we figure out the contents of the index qual. The - * usual case is (var op const) or (const op 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. - * - * XXX Although this code *thinks* it can handle an indexqual - * with the indexkey on either side, in fact it cannot. - * Indexscans only work with quals that have the indexkey on - * the left (the planner/optimizer makes sure it never passes - * anything else). The reason: the scankey machinery has no - * provision for distinguishing which side of the operator is - * the indexed attribute and which is the compared-to - * constant. It just assumes that the attribute is on the left - * :-( - * - * I am leaving this code able to support both ways, even though - * half of it is dead code, on the off chance that someone - * will fix the scankey machinery someday --- tgl 8/11/99. - */ - - scanvar = NO_OP; - run_keys[j] = NO_OP; - - /* - * determine information in leftop - */ - leftop = (Node *) get_leftop(clause); - - if (leftop && IsA(leftop, RelabelType)) - leftop = ((RelabelType *) leftop)->arg; - - Assert(leftop != NULL); - - 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. - */ - scanvalue = ((Const *) leftop)->constvalue; - if (((Const *) leftop)->constisnull) - flags |= SK_ISNULL; - } - else if (IsA(leftop, Param)) - { - bool isnull; - - /* - * if the leftop is a Param node then it means it - * identifies the value to place in our scan key. - */ - - /* Life was so easy before ... subselects */ - if (((Param *) leftop)->paramkind == PARAM_EXEC) - { - /* treat Param as runtime key */ - have_runtime_keys = true; - run_keys[j] = LEFT_OP; - } - else - { - /* treat Param like a constant */ - scanvalue = ExecEvalParam((Param *) leftop, - scanstate->cstate.cs_ExprContext, - &isnull); - if (isnull) - flags |= SK_ISNULL; - } - } - else - { - /* - * otherwise, the leftop contains an expression evaluable - * at runtime to figure out the value to place in our scan - * key. - */ - have_runtime_keys = true; - run_keys[j] = LEFT_OP; - } - - /* - * now determine information in rightop - */ - rightop = (Node *) get_rightop(clause); - - if (rightop && IsA(rightop, RelabelType)) - rightop = ((RelabelType *) rightop)->arg; - - Assert(rightop != NULL); - - 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(ERROR, "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 rightop is a const node then it means it - * identifies the value to place in our scan key. - */ - scanvalue = ((Const *) rightop)->constvalue; - if (((Const *) rightop)->constisnull) - flags |= SK_ISNULL; - } - else if (IsA(rightop, Param)) - { - bool isnull; - - /* - * if the rightop is a Param node then it means it - * identifies the value to place in our scan key. - */ - - /* Life was so easy before ... subselects */ - if (((Param *) rightop)->paramkind == PARAM_EXEC) - { - /* treat Param as runtime key */ - have_runtime_keys = true; - run_keys[j] = RIGHT_OP; - } - else - { - /* treat Param like a constant */ - scanvalue = ExecEvalParam((Param *) rightop, - scanstate->cstate.cs_ExprContext, - &isnull); - if (isnull) - flags |= SK_ISNULL; - } - } - else - { - /* - * otherwise, the rightop contains an expression evaluable - * at runtime to figure out the value to place in our scan - * key. - */ - have_runtime_keys = true; - run_keys[j] = RIGHT_OP; - } - - /* - * now check that at least one op tells us the scan - * attribute... - */ - if (scanvar == NO_OP) - elog(ERROR, "ExecInitIndexScan: %s", - "neither leftop nor rightop refer to scan relation"); - - /* - * initialize the scan key's fields appropriately - */ - ScanKeyEntryInitialize(&scan_keys[j], - flags, - varattno, /* attribute number to - * scan */ - (RegProcedure) opid, /* reg proc to use */ - scanvalue); /* constant */ - } - - /* - * store the key information into our arrays. - */ - numScanKeys[i] = n_keys; - scanKeys[i] = scan_keys; - runtimeKeyInfo[i] = run_keys; - } - - indexstate->iss_NumIndices = numIndices; - if (ScanDirectionIsBackward(node->indxorderdir)) - indexPtr = 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 we do have runtime keys, we need an ExprContext to evaluate them; - * the node's standard context won't do because we want to reset that - * context for every tuple. So, build another context just like the - * other one... -tgl 7/11/00 - */ - if (have_runtime_keys) - { - ExprContext *stdecontext = scanstate->cstate.cs_ExprContext; - - ExecAssignExprContext(estate, &scanstate->cstate); - indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo; - indexstate->iss_RuntimeContext = scanstate->cstate.cs_ExprContext; - scanstate->cstate.cs_ExprContext = stdecontext; - } - else - { - indexstate->iss_RuntimeKeyInfo = NULL; - indexstate->iss_RuntimeContext = NULL; - /* Get rid of the speculatively-allocated flag arrays, too */ - for (i = 0; i < numIndices; i++) - { - if (runtimeKeyInfo[i] != NULL) - pfree(runtimeKeyInfo[i]); - } - pfree(runtimeKeyInfo); - } - - /* - * open the base relation and acquire AccessShareLock on it. - */ - relid = node->scan.scanrelid; - rtentry = rt_fetch(relid, estate->es_range_table); - reloid = rtentry->relid; - - currentRelation = heap_open(reloid, AccessShareLock); - - if (!RelationGetForm(currentRelation)->relhasindex) - elog(ERROR, "indexes of the relation %u was inactivated", reloid); - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = NULL; /* no heap scan here */ - - /* - * get the scan type from the relation descriptor. - */ - ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false); - ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); - - /* - * open the index relations and initialize relation and scan - * descriptors. Note we acquire no locks here; the index machinery - * does its own locks and unlocks. (We rely on having AccessShareLock - * on the parent table to ensure the index won't go away!) - */ - listscan = indxid; - for (i = 0; i < numIndices; i++) - { - Oid indexOid = (Oid) lfirsti(listscan); - - indexDescs[i] = index_open(indexOid); - scanDescs[i] = index_beginscan(currentRelation, - indexDescs[i], - estate->es_snapshot, - numScanKeys[i], - scanKeys[i]); - listscan = lnext(listscan); - } - - indexstate->iss_RelationDescs = indexDescs; - indexstate->iss_ScanDescs = scanDescs; - - /* - * 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/nodeLimit.c b/src/backend/executor/nodeLimit.c deleted file mode 100644 index 2e8c444c500..00000000000 --- a/src/backend/executor/nodeLimit.c +++ /dev/null @@ -1,311 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeLimit.c - * Routines to handle limiting of query results where appropriate - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.10 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecLimit - extract a limited range of tuples - * ExecInitLimit - initialize node and subnodes.. - * ExecEndLimit - shutdown node and subnodes - */ - -#include "postgres.h" - -#include "executor/executor.h" -#include "executor/nodeLimit.h" - -static void recompute_limits(Limit *node); - - -/* ---------------------------------------------------------------- - * ExecLimit - * - * This is a very simple node which just performs LIMIT/OFFSET - * filtering on the stream of tuples returned by a subplan. - * ---------------------------------------------------------------- - */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecLimit(Limit *node) -{ - LimitState *limitstate; - ScanDirection direction; - TupleTableSlot *resultTupleSlot; - TupleTableSlot *slot; - Plan *outerPlan; - long netlimit; - - /* - * get information from the node - */ - limitstate = node->limitstate; - direction = node->plan.state->es_direction; - outerPlan = outerPlan((Plan *) node); - resultTupleSlot = limitstate->cstate.cs_ResultTupleSlot; - - /* - * If first call for this scan, compute limit/offset. (We can't do - * this any earlier, because parameters from upper nodes may not be - * set until now.) - */ - if (!limitstate->parmsSet) - recompute_limits(node); - netlimit = limitstate->offset + limitstate->count; - - /* - * now loop, returning only desired tuples. - */ - for (;;) - { - /* - * If we have reached the subplan EOF or the limit, just quit. - * - * NOTE: when scanning forwards, we must fetch one tuple beyond the - * COUNT limit before we can return NULL, else the subplan won't - * be properly positioned to start going backwards. Hence test - * here is for position > netlimit not position >= netlimit. - * - * Similarly, when scanning backwards, we must re-fetch the last - * tuple in the offset region before we can return NULL. Otherwise - * we won't be correctly aligned to start going forward again. So, - * although you might think we can quit when position equals - * offset + 1, we have to fetch a subplan tuple first, and then - * exit when position = offset. - */ - if (ScanDirectionIsForward(direction)) - { - if (limitstate->atEnd) - return NULL; - if (!limitstate->noCount && limitstate->position > netlimit) - return NULL; - } - else - { - if (limitstate->position <= limitstate->offset) - return NULL; - } - - /* - * fetch a tuple from the outer subplan - */ - slot = ExecProcNode(outerPlan, (Plan *) node); - if (TupIsNull(slot)) - { - /* - * We are at start or end of the subplan. Update local state - * appropriately, but always return NULL. - */ - if (ScanDirectionIsForward(direction)) - { - Assert(!limitstate->atEnd); - /* must bump position to stay in sync for backwards fetch */ - limitstate->position++; - limitstate->atEnd = true; - } - else - { - limitstate->position = 0; - limitstate->atEnd = false; - } - return NULL; - } - - /* - * We got the next subplan tuple successfully, so adjust state. - */ - if (ScanDirectionIsForward(direction)) - limitstate->position++; - else - { - limitstate->position--; - Assert(limitstate->position > 0); - } - limitstate->atEnd = false; - - /* - * Now, is this a tuple we want? If not, loop around to fetch - * another tuple from the subplan. - */ - if (limitstate->position > limitstate->offset && - (limitstate->noCount || limitstate->position <= netlimit)) - break; - } - - ExecStoreTuple(slot->val, - resultTupleSlot, - InvalidBuffer, - false); /* tuple does not belong to slot */ - - return resultTupleSlot; -} - -/* - * Evaluate the limit/offset expressions --- done at start of each scan. - * - * This is also a handy place to reset the current-position state info. - */ -static void -recompute_limits(Limit *node) -{ - LimitState *limitstate = node->limitstate; - ExprContext *econtext = limitstate->cstate.cs_ExprContext; - bool isNull; - - if (node->limitOffset) - { - limitstate->offset = - DatumGetInt32(ExecEvalExprSwitchContext(node->limitOffset, - econtext, - &isNull, - NULL)); - /* Interpret NULL offset as no offset */ - if (isNull) - limitstate->offset = 0; - else if (limitstate->offset < 0) - limitstate->offset = 0; - } - else - { - /* No OFFSET supplied */ - limitstate->offset = 0; - } - - if (node->limitCount) - { - limitstate->count = - DatumGetInt32(ExecEvalExprSwitchContext(node->limitCount, - econtext, - &isNull, - NULL)); - /* Interpret NULL count as no count (LIMIT ALL) */ - if (isNull) - limitstate->noCount = true; - else if (limitstate->count < 0) - limitstate->count = 0; - } - else - { - /* No COUNT supplied */ - limitstate->count = 0; - limitstate->noCount = true; - } - - /* Reset position data to start-of-scan */ - limitstate->position = 0; - limitstate->atEnd = false; - - /* Set flag that params are computed */ - limitstate->parmsSet = true; -} - -/* ---------------------------------------------------------------- - * ExecInitLimit - * - * This initializes the limit node state structures and - * the node's subplan. - * ---------------------------------------------------------------- - */ -bool /* return: initialization status */ -ExecInitLimit(Limit *node, EState *estate, Plan *parent) -{ - LimitState *limitstate; - Plan *outerPlan; - - /* - * assign execution state to node - */ - node->plan.state = estate; - - /* - * create new LimitState for node - */ - limitstate = makeNode(LimitState); - node->limitstate = limitstate; - limitstate->parmsSet = false; - - /* - * Miscellaneous initialization - * - * Limit nodes never call ExecQual or ExecProject, but they need an - * exprcontext anyway to evaluate the limit/offset parameters in. - */ - ExecAssignExprContext(estate, &limitstate->cstate); - -#define LIMIT_NSLOTS 1 - - /* - * Tuple table initialization - */ - ExecInitResultTupleSlot(estate, &limitstate->cstate); - - /* - * then initialize outer plan - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* - * limit nodes do no projections, so initialize projection info for - * this node appropriately - */ - ExecAssignResultTypeFromOuterPlan((Plan *) node, &limitstate->cstate); - limitstate->cstate.cs_ProjInfo = NULL; - - return TRUE; -} - -int -ExecCountSlotsLimit(Limit *node) -{ - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - LIMIT_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndLimit - * - * This shuts down the subplan and frees resources allocated - * to this node. - * ---------------------------------------------------------------- - */ -void -ExecEndLimit(Limit *node) -{ - LimitState *limitstate = node->limitstate; - - ExecFreeExprContext(&limitstate->cstate); - - ExecEndNode(outerPlan((Plan *) node), (Plan *) node); - - /* clean up tuple table */ - ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot); -} - - -void -ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent) -{ - LimitState *limitstate = node->limitstate; - - ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot); - - /* force recalculation of limit expressions on first call */ - limitstate->parmsSet = false; - - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); -} diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c deleted file mode 100644 index 30eb9a285b6..00000000000 --- a/src/backend/executor/nodeMaterial.c +++ /dev/null @@ -1,303 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeMaterial.c - * Routines to handle materialization nodes. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.38 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecMaterial - materialize the result of a subplan - * ExecInitMaterial - initialize node and subnodes - * ExecEndMaterial - shutdown node and subnodes - * - */ -#include "postgres.h" - -#include "executor/executor.h" -#include "executor/nodeMaterial.h" -#include "miscadmin.h" -#include "utils/tuplestore.h" - -/* ---------------------------------------------------------------- - * ExecMaterial - * - * The first time this is called, ExecMaterial retrieves tuples - * from this node's outer subplan and inserts them into a tuplestore - * (a temporary tuple storage structure). The first tuple is then - * returned. Successive calls to ExecMaterial return successive - * tuples from the tuplestore. - * - * Initial State: - * - * matstate->tuplestorestate is initially NULL, indicating we - * haven't yet collected the results of the subplan. - * - * ---------------------------------------------------------------- - */ -TupleTableSlot * /* result tuple from subplan */ -ExecMaterial(Material *node) -{ - EState *estate; - MaterialState *matstate; - ScanDirection dir; - Tuplestorestate *tuplestorestate; - HeapTuple heapTuple; - TupleTableSlot *slot; - bool should_free; - - /* - * get state info from node - */ - matstate = node->matstate; - estate = node->plan.state; - dir = estate->es_direction; - tuplestorestate = (Tuplestorestate *) matstate->tuplestorestate; - - /* - * If first time through, read all tuples from outer plan and pass - * them to tuplestore.c. Subsequent calls just fetch tuples from - * tuplestore. - */ - - if (tuplestorestate == NULL) - { - Plan *outerNode; - - /* - * Want to scan subplan in the forward direction while creating - * the stored data. (Does setting my direction actually affect - * the subplan? I bet this is useless code...) - */ - estate->es_direction = ForwardScanDirection; - - /* - * Initialize tuplestore module. - */ - tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */ - SortMem); - - matstate->tuplestorestate = (void *) tuplestorestate; - - /* - * Scan the subplan and feed all the tuples to tuplestore. - */ - outerNode = outerPlan((Plan *) node); - - for (;;) - { - slot = ExecProcNode(outerNode, (Plan *) node); - - if (TupIsNull(slot)) - break; - - tuplestore_puttuple(tuplestorestate, (void *) slot->val); - ExecClearTuple(slot); - } - - /* - * Complete the store. - */ - tuplestore_donestoring(tuplestorestate); - - /* - * restore to user specified direction - */ - estate->es_direction = dir; - } - - /* - * Get the first or next tuple from tuplestore. Returns NULL if no - * more tuples. - */ - slot = (TupleTableSlot *) matstate->csstate.cstate.cs_ResultTupleSlot; - heapTuple = tuplestore_getheaptuple(tuplestorestate, - ScanDirectionIsForward(dir), - &should_free); - - return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); -} - -/* ---------------------------------------------------------------- - * ExecInitMaterial - * ---------------------------------------------------------------- - */ -bool /* initialization status */ -ExecInitMaterial(Material *node, EState *estate, Plan *parent) -{ - MaterialState *matstate; - Plan *outerPlan; - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create state structure - */ - matstate = makeNode(MaterialState); - matstate->tuplestorestate = NULL; - node->matstate = matstate; - - /* - * Miscellaneous initialization - * - * Materialization nodes don't need ExprContexts because they never call - * ExecQual or ExecProject. - */ - -#define MATERIAL_NSLOTS 2 - - /* - * tuple table initialization - * - * material nodes only return tuples from their materialized relation. - */ - ExecInitResultTupleSlot(estate, &matstate->csstate.cstate); - ExecInitScanTupleSlot(estate, &matstate->csstate); - - /* - * initializes child nodes - */ - outerPlan = outerPlan((Plan *) 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, &matstate->csstate.cstate); - ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate); - matstate->csstate.cstate.cs_ProjInfo = NULL; - - return TRUE; -} - -int -ExecCountSlotsMaterial(Material *node) -{ - return ExecCountSlotsNode(outerPlan((Plan *) node)) + - ExecCountSlotsNode(innerPlan((Plan *) node)) + - MATERIAL_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndMaterial - * ---------------------------------------------------------------- - */ -void -ExecEndMaterial(Material *node) -{ - MaterialState *matstate; - Plan *outerPlan; - - /* - * get info from the material state - */ - matstate = node->matstate; - - /* - * shut down the subplan - */ - outerPlan = outerPlan((Plan *) node); - ExecEndNode(outerPlan, (Plan *) node); - - /* - * clean out the tuple table - */ - ExecClearTuple(matstate->csstate.css_ScanTupleSlot); - - /* - * Release tuplestore resources - */ - if (matstate->tuplestorestate != NULL) - tuplestore_end((Tuplestorestate *) matstate->tuplestorestate); - matstate->tuplestorestate = NULL; -} - -/* ---------------------------------------------------------------- - * ExecMaterialMarkPos - * - * Calls tuplestore to save the current position in the stored file. - * ---------------------------------------------------------------- - */ -void -ExecMaterialMarkPos(Material *node) -{ - MaterialState *matstate = node->matstate; - - /* - * if we haven't materialized yet, just return. - */ - if (!matstate->tuplestorestate) - return; - - tuplestore_markpos((Tuplestorestate *) matstate->tuplestorestate); -} - -/* ---------------------------------------------------------------- - * ExecMaterialRestrPos - * - * Calls tuplestore to restore the last saved file position. - * ---------------------------------------------------------------- - */ -void -ExecMaterialRestrPos(Material *node) -{ - MaterialState *matstate = node->matstate; - - /* - * if we haven't materialized yet, just return. - */ - if (!matstate->tuplestorestate) - return; - - /* - * restore the scan to the previously marked position - */ - tuplestore_restorepos((Tuplestorestate *) matstate->tuplestorestate); -} - -/* ---------------------------------------------------------------- - * ExecMaterialReScan - * - * Rescans the materialized relation. - * ---------------------------------------------------------------- - */ -void -ExecMaterialReScan(Material *node, ExprContext *exprCtxt, Plan *parent) -{ - MaterialState *matstate = node->matstate; - - /* - * If we haven't materialized yet, just return. If outerplan' chgParam - * is not NULL then it will be re-scanned by ExecProcNode, else - no - * reason to re-scan it at all. - */ - if (!matstate->tuplestorestate) - return; - - ExecClearTuple(matstate->csstate.cstate.cs_ResultTupleSlot); - - /* - * If subnode is to be rescanned then we forget previous stored - * results; we have to re-read the subplan and re-store. - * - * Otherwise we can just rewind and rescan the stored output. - */ - if (((Plan *) node)->lefttree->chgParam != NULL) - { - tuplestore_end((Tuplestorestate *) matstate->tuplestorestate); - matstate->tuplestorestate = NULL; - } - else - tuplestore_rescan((Tuplestorestate *) matstate->tuplestorestate); -} diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c deleted file mode 100644 index 4467fef9e12..00000000000 --- a/src/backend/executor/nodeMergejoin.c +++ /dev/null @@ -1,1595 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeMergejoin.c - * routines supporting merge joins - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.50 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecMergeJoin mergejoin outer and inner relations. - * ExecInitMergeJoin creates and initializes run time states - * ExecEndMergeJoin cleans up the node. - * - * NOTES - * Essential operation of the merge join algorithm is as follows: - * - * 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_BEGIN - * if (inner == outer) Join Tuples JOINTUPLES - * while (outer < inner) SKIPOUTER_TEST - * advance outer SKIPOUTER_ADVANCE - * if (outer > inner) SKIPOUTER_TEST - * Skip Inner SKIPINNER - * } - - * - * Skip Inner { SKIPINNER_BEGIN - * if (inner == outer) Join Tuples JOINTUPLES - * while (outer > inner) SKIPINNER_TEST - * advance inner SKIPINNER_ADVANCE - * if (outer < inner) SKIPINNER_TEST - * Skip Outer SKIPOUTER - * } - - * - * 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 - * - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "access/printtup.h" -#include "catalog/pg_operator.h" -#include "executor/execdebug.h" -#include "executor/execdefs.h" -#include "executor/nodeMergejoin.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" - - -static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext); - -#define MarkInnerTuple(innerTupleSlot, mergestate) \ -( \ - ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \ - (mergestate)->mj_MarkedTupleSlot, \ - InvalidBuffer, \ - true) \ -) - - -/* ---------------------------------------------------------------- - * MJFormSkipQuals - * - * This takes the mergeclause which is a qualification of the - * form ((= expr expr) (= expr expr) ...) and forms new lists - * of the forms ((< expr expr) (< expr expr) ...) and - * ((> expr expr) (> expr expr) ...). These lists will be used - * by ExecMergeJoin() to determine if we should skip tuples. - * (We expect there to be suitable operators because the "=" operators - * were marked mergejoinable; however, there might be a different - * one needed in each qual clause.) - * ---------------------------------------------------------------- - */ -static void -MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals) -{ - List *ltcdr, - *gtcdr; - - /* - * Make modifiable copies of the qualList. - */ - *ltQuals = (List *) copyObject((Node *) qualList); - *gtQuals = (List *) copyObject((Node *) qualList); - - /* - * Scan both lists in parallel, so that we can update the operators - * with the minimum number of syscache searches. - */ - ltcdr = *ltQuals; - foreach(gtcdr, *gtQuals) - { - Expr *ltqual = (Expr *) lfirst(ltcdr); - Expr *gtqual = (Expr *) lfirst(gtcdr); - Oper *ltop = (Oper *) ltqual->oper; - Oper *gtop = (Oper *) gtqual->oper; - - /* - * The two ops should be identical, so use either one for lookup. - */ - if (!IsA(ltop, Oper)) - elog(ERROR, "MJFormSkipQuals: op not an Oper!"); - - /* - * Lookup the operators, and replace the data in the copied - * operator nodes. - */ - op_mergejoin_crossops(ltop->opno, - <op->opno, - >op->opno, - <op->opid, - >op->opid); - ltop->op_fcache = NULL; - gtop->op_fcache = NULL; - - ltcdr = lnext(ltcdr); - } -} - -/* ---------------------------------------------------------------- - * MergeCompare - * - * Compare the keys according to 'compareQual' which is of the - * form: { (key1a > key2a) (key1b > key2b) ... }. - * - * (actually, it could also be of the form (key1a < key2a)...) - * - * This is different from calling ExecQual because ExecQual returns - * true only if ALL the comparison 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. - * We use the original mergeclause items to detect equality. - * ---------------------------------------------------------------- - */ -static bool -MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) -{ - bool result; - MemoryContext oldContext; - List *clause; - List *eqclause; - - /* - * Do expression eval in short-lived context. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * for each pair of clauses, test them until our compare conditions - * are satisfied. if we reach the end of the list, none of our key - * greater-than conditions were satisfied so we return false. - */ - result = false; /* assume 'false' result */ - - eqclause = eqQual; - foreach(clause, compareQual) - { - Datum const_value; - bool isNull; - - /* - * first test if our compare clause is satisfied. if so then - * return true. - * - * A NULL result is considered false. - */ - const_value = ExecEvalExpr((Node *) lfirst(clause), econtext, - &isNull, NULL); - - if (DatumGetBool(const_value) && !isNull) - { - result = true; - break; - } - - /*----------- - * 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. - *----------- - */ - const_value = ExecEvalExpr((Node *) lfirst(eqclause), - econtext, - &isNull, - NULL); - - if (!DatumGetBool(const_value) || isNull) - break; /* return false */ - - eqclause = lnext(eqclause); - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecMergeTupleDump - * - * This function is called through the MJ_dump() macro - * when EXEC_MERGEJOINDEBUG is defined - * ---------------------------------------------------------------- - */ -#ifdef EXEC_MERGEJOINDEBUG - -static void -ExecMergeTupleDumpOuter(MergeJoinState *mergestate) -{ - TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot; - - printf("==== outer tuple ====\n"); - if (TupIsNull(outerSlot)) - printf("(nil)\n"); - else - MJ_debugtup(outerSlot->val, - outerSlot->ttc_tupleDescriptor); -} - -static void -ExecMergeTupleDumpInner(MergeJoinState *mergestate) -{ - TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot; - - printf("==== inner tuple ====\n"); - if (TupIsNull(innerSlot)) - printf("(nil)\n"); - else - MJ_debugtup(innerSlot->val, - innerSlot->ttc_tupleDescriptor); -} - -static void -ExecMergeTupleDumpMarked(MergeJoinState *mergestate) -{ - TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot; - - printf("==== marked tuple ====\n"); - if (TupIsNull(markedSlot)) - printf("(nil)\n"); - else - MJ_debugtup(markedSlot->val, - markedSlot->ttc_tupleDescriptor); -} - -static void -ExecMergeTupleDump(MergeJoinState *mergestate) -{ - printf("******** ExecMergeTupleDump ********\n"); - - ExecMergeTupleDumpOuter(mergestate); - ExecMergeTupleDumpInner(mergestate); - ExecMergeTupleDumpMarked(mergestate); - - printf("******** \n"); -} -#endif - -/* ---------------------------------------------------------------- - * 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 corresponding ">" operator. - * The opposite "smaller/greater" clause is formed by substituting "<". - * - * Note: prior to v6.5, the relational clauses were formed using the - * sort op used to sort the inner relation, which of course would fail - * if the outer and inner keys were of different data types. - * In the current code, we instead assume that operators named "<" and ">" - * will do the right thing. This should be true since the mergejoin "=" - * operator's pg_operator entry will have told the planner to sort by - * "<" for each of the left and right sides. - * - * (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 *joinqual; - List *otherqual; - bool qualResult; - bool compareResult; - Plan *innerPlan; - TupleTableSlot *innerTupleSlot; - Plan *outerPlan; - TupleTableSlot *outerTupleSlot; - ExprContext *econtext; - bool doFillOuter; - bool doFillInner; - - /* - * get information from node - */ - mergestate = node->mergestate; - estate = node->join.plan.state; - direction = estate->es_direction; - innerPlan = innerPlan((Plan *) node); - outerPlan = outerPlan((Plan *) node); - econtext = mergestate->jstate.cs_ExprContext; - mergeclauses = node->mergeclauses; - joinqual = node->join.joinqual; - otherqual = node->join.plan.qual; - - switch (node->join.jointype) - { - case JOIN_INNER: - doFillOuter = false; - doFillInner = false; - break; - case JOIN_LEFT: - doFillOuter = true; - doFillInner = false; - break; - case JOIN_FULL: - doFillOuter = true; - doFillInner = true; - break; - case JOIN_RIGHT: - doFillOuter = false; - doFillInner = true; - break; - default: - elog(ERROR, "ExecMergeJoin: unsupported join type %d", - (int) node->join.jointype); - doFillOuter = false; /* keep compiler quiet */ - doFillInner = false; - break; - } - - if (ScanDirectionIsForward(direction)) - { - outerSkipQual = mergestate->mj_OuterSkipQual; - innerSkipQual = mergestate->mj_InnerSkipQual; - } - else - { - outerSkipQual = mergestate->mj_InnerSkipQual; - innerSkipQual = mergestate->mj_OuterSkipQual; - } - - /* - * Check to see if we're still projecting out tuples from a previous - * join tuple (because there is a function-returning-set in the - * projection expressions). If so, try to project another one. - */ - if (mergestate->jstate.cs_TupFromTlist) - { - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - mergestate->jstate.cs_TupFromTlist = false; - } - - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. Note this can't - * happen until we're done projecting out tuples from a join tuple. - */ - ResetExprContext(econtext); - - /* - * ok, everything is setup.. let's go to work - */ - 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(mergestate); - - switch (mergestate->mj_JoinState) - { - /* - * EXEC_MJ_INITIALIZE means that this is the first time - * ExecMergeJoin() has been called and so we have to fetch - * the first tuple for both outer and inner subplans. If - * we fail to get a tuple here, then that subplan is - * empty, and we either end the join or go to one of the - * fill-remaining-tuples states. - */ - case EXEC_MJ_INITIALIZE: - MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n"); - - outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); - mergestate->mj_OuterTupleSlot = outerTupleSlot; - if (TupIsNull(outerTupleSlot)) - { - MJ_printf("ExecMergeJoin: outer subplan is empty\n"); - if (doFillInner) - { - /* - * Need to emit right-join tuples for remaining - * inner tuples. We set MatchedInner = true to - * force the ENDOUTER state to advance inner. - */ - mergestate->mj_JoinState = EXEC_MJ_ENDOUTER; - mergestate->mj_MatchedInner = true; - break; - } - /* Otherwise we're done. */ - return NULL; - } - - innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); - mergestate->mj_InnerTupleSlot = innerTupleSlot; - if (TupIsNull(innerTupleSlot)) - { - MJ_printf("ExecMergeJoin: inner subplan is empty\n"); - if (doFillOuter) - { - /* - * Need to emit left-join tuples for all outer - * tuples, including the one we just fetched. We - * set MatchedOuter = false to force the ENDINNER - * state to emit this tuple before advancing - * outer. - */ - mergestate->mj_JoinState = EXEC_MJ_ENDINNER; - mergestate->mj_MatchedOuter = false; - break; - } - /* Otherwise we're done. */ - return NULL; - } - - /* - * OK, we have the initial tuples. Begin by skipping - * unmatched inner tuples. - */ - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN; - 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); - - MarkInnerTuple(mergestate->mj_InnerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTEST; - break; - - /* - * EXEC_MJ_JOINTEST means we have two tuples which might - * satisfy the merge clause, so we test them. - * - * If they do satisfy, then we join them and move on to the - * next inner tuple (EXEC_MJ_JOINTUPLES). - * - * If they do not satisfy then advance to next outer tuple. - */ - case EXEC_MJ_JOINTEST: - MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n"); - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - qualResult = ExecQual(mergeclauses, econtext, false); - 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 - * satisfied 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; - - /* - * Check the extra qual conditions to see if we actually - * want to return this join tuple. If not, can proceed - * with merge. We must distinguish the additional - * joinquals (which must pass to consider the tuples - * "matched" for outer-join logic) from the otherquals - * (which must pass before we actually return the tuple). - * - * We don't bother with a ResetExprContext here, on the - * assumption that we just did one before checking the - * merge qual. One per tuple should be sufficient. Also, - * the econtext's tuple pointers were set up before - * checking the merge qual, so we needn't do it again. - */ - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); - MJ_DEBUG_QUAL(joinqual, qualResult); - - if (qualResult) - { - mergestate->mj_MatchedOuter = true; - mergestate->mj_MatchedInner = true; - - qualResult = (otherqual == NIL || - ExecQual(otherqual, econtext, false)); - MJ_DEBUG_QUAL(otherqual, qualResult); - - if (qualResult) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - 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. - * - * Before advancing, we check to see if we must emit an - * outer-join fill tuple for this inner tuple. - */ - case EXEC_MJ_NEXTINNER: - MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n"); - - if (doFillInner && !mergestate->mj_MatchedInner) - { - /* - * Generate a fake join tuple with nulls for the outer - * tuple, and return it if it passes the non-join - * quals. - */ - mergestate->mj_MatchedInner = true; /* do it only once */ - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_NullOuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning fill tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * now we get the next inner tuple, if any - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); - mergestate->mj_InnerTupleSlot = innerTupleSlot; - MJ_DEBUG_PROC_NODE(innerTupleSlot); - mergestate->mj_MatchedInner = false; - - 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) - * - * Before advancing, we check to see if we must emit an - * outer-join fill tuple for this outer tuple. - *------------------------------------------------ - */ - case EXEC_MJ_NEXTOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n"); - - if (doFillOuter && !mergestate->mj_MatchedOuter) - { - /* - * Generate a fake join tuple with nulls for the inner - * tuple, and return it if it passes the non-join - * quals. - */ - mergestate->mj_MatchedOuter = true; /* do it only once */ - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_NullInnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning fill tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * now we get the next outer tuple, if any - */ - outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); - mergestate->mj_OuterTupleSlot = outerTupleSlot; - MJ_DEBUG_PROC_NODE(outerTupleSlot); - mergestate->mj_MatchedOuter = false; - - /* - * if the outer tuple is null then we are done with the - * join, unless we have inner tuples we need to null-fill. - */ - if (TupIsNull(outerTupleSlot)) - { - MJ_printf("ExecMergeJoin: end of outer subplan\n"); - innerTupleSlot = mergestate->mj_InnerTupleSlot; - if (doFillInner && !TupIsNull(innerTupleSlot)) - { - /* - * Need to emit right-join tuples for remaining - * inner tuples. - */ - mergestate->mj_JoinState = EXEC_MJ_ENDOUTER; - break; - } - /* Otherwise we're done. */ - return NULL; - } - - mergestate->mj_JoinState = EXEC_MJ_TESTOUTER; - break; - - /*-------------------------------------------------------- - * EXEC_MJ_TESTOUTER If the new outer tuple and the marked - * tuple satisfy 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 - */ - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_MarkedTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - qualResult = ExecQual(mergeclauses, econtext, false); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) - { - /* - * the merge clause matched so now we restore the - * inner scan position to the first mark, and loop - * back to JOINTEST. Actually, since we know the - * mergeclause matches, we can skip JOINTEST and go - * straight to JOINTUPLES. - * - * NOTE: we do not need to worry about the MatchedInner - * state for the rescanned inner tuples. We know all - * of them will match this new outer tuple and - * therefore won't be emitted as fill tuples. This - * works *only* because we require the extra joinquals - * to be nil when doing a right or full join --- - * otherwise some of the rescanned tuples might fail - * the extra joinquals. - */ - ExecRestrPos(innerPlan); - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - } - else - { - /* ---------------- - * if the inner tuple was nil and the new outer - * tuple didn't match the marked outer tuple then - * we 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 marked inner tuples. So we're done. - * ---------------- - */ - innerTupleSlot = mergestate->mj_InnerTupleSlot; - if (TupIsNull(innerTupleSlot)) - { - if (doFillOuter) - { - /* - * Need to emit left-join tuples for remaining - * outer tuples. - */ - mergestate->mj_JoinState = EXEC_MJ_ENDINNER; - break; - } - /* Otherwise we're done. */ - return NULL; - } - - /* continue on to skip outer tuples */ - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN; - } - 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. - * - * To avoid redundant tests, we divide this into three - * sub-states: BEGIN, TEST, ADVANCE. - *---------------------------------------------------------- - */ - case EXEC_MJ_SKIPOUTER_BEGIN: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_BEGIN\n"); - - /* - * before we advance, make sure the current tuples do not - * satisfy the mergeclauses. If they do, then we update - * the marked tuple and go join them. - */ - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - qualResult = ExecQual(mergeclauses, econtext, false); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) - { - ExecMarkPos(innerPlan); - - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - break; - } - - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST; - break; - - case EXEC_MJ_SKIPOUTER_TEST: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_TEST\n"); - - /* - * ok, now test the skip qualification - */ - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - compareResult = MergeCompare(mergeclauses, - outerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); - - /* - * compareResult is true as long as we should continue - * skipping outer tuples. - */ - if (compareResult) - { - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE; - 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_BEGIN; - else - mergestate->mj_JoinState = EXEC_MJ_JOINMARK; - break; - - /* - * Before advancing, we check to see if we must emit an - * outer-join fill tuple for this outer tuple. - */ - case EXEC_MJ_SKIPOUTER_ADVANCE: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n"); - - if (doFillOuter && !mergestate->mj_MatchedOuter) - { - /* - * Generate a fake join tuple with nulls for the inner - * tuple, and return it if it passes the non-join - * quals. - */ - mergestate->mj_MatchedOuter = true; /* do it only once */ - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_NullInnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning fill tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * now we get the next outer tuple, if any - */ - outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); - mergestate->mj_OuterTupleSlot = outerTupleSlot; - MJ_DEBUG_PROC_NODE(outerTupleSlot); - mergestate->mj_MatchedOuter = false; - - /* - * if the outer tuple is null then we are done with the - * join, unless we have inner tuples we need to null-fill. - */ - if (TupIsNull(outerTupleSlot)) - { - MJ_printf("ExecMergeJoin: end of outer subplan\n"); - innerTupleSlot = mergestate->mj_InnerTupleSlot; - if (doFillInner && !TupIsNull(innerTupleSlot)) - { - /* - * Need to emit right-join tuples for remaining - * inner tuples. - */ - mergestate->mj_JoinState = EXEC_MJ_ENDOUTER; - break; - } - /* Otherwise we're done. */ - return NULL; - } - - /* - * otherwise test the new tuple against the skip qual. - */ - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST; - 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. - * - * To avoid redundant tests, we divide this into three - * sub-states: BEGIN, TEST, ADVANCE. - *------------------------------------------------------- - */ - case EXEC_MJ_SKIPINNER_BEGIN: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_BEGIN\n"); - - /* - * before we advance, make sure the current tuples do not - * satisfy the mergeclauses. If they do, then we update - * the marked tuple and go join them. - */ - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - qualResult = ExecQual(mergeclauses, econtext, false); - MJ_DEBUG_QUAL(mergeclauses, qualResult); - - if (qualResult) - { - ExecMarkPos(innerPlan); - - MarkInnerTuple(innerTupleSlot, mergestate); - - mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES; - break; - } - - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST; - break; - - case EXEC_MJ_SKIPINNER_TEST: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_TEST\n"); - - /* - * ok, now test the skip qualification - */ - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - compareResult = MergeCompare(mergeclauses, - innerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult); - - /* - * compareResult is true as long as we should continue - * skipping inner tuples. - */ - if (compareResult) - { - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE; - break; - } - - /* - * now check the outer skip qual to see if we should now - * skip outer tuples... if we fail the outer skip qual, - * then we know we have a new pair of matching tuples. - */ - compareResult = MergeCompare(mergeclauses, - outerSkipQual, - econtext); - - MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult); - - if (compareResult) - mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN; - else - mergestate->mj_JoinState = EXEC_MJ_JOINMARK; - break; - - /* - * Before advancing, we check to see if we must emit an - * outer-join fill tuple for this inner tuple. - */ - case EXEC_MJ_SKIPINNER_ADVANCE: - MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n"); - - if (doFillInner && !mergestate->mj_MatchedInner) - { - /* - * Generate a fake join tuple with nulls for the outer - * tuple, and return it if it passes the non-join - * quals. - */ - mergestate->mj_MatchedInner = true; /* do it only once */ - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_NullOuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning fill tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * now we get the next inner tuple, if any - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); - mergestate->mj_InnerTupleSlot = innerTupleSlot; - MJ_DEBUG_PROC_NODE(innerTupleSlot); - mergestate->mj_MatchedInner = false; - - /* - * if the inner tuple is null then we are done with the - * join, unless we have outer tuples we need to null-fill. - */ - if (TupIsNull(innerTupleSlot)) - { - MJ_printf("ExecMergeJoin: end of inner subplan\n"); - outerTupleSlot = mergestate->mj_OuterTupleSlot; - if (doFillOuter && !TupIsNull(outerTupleSlot)) - { - /* - * Need to emit left-join tuples for remaining - * outer tuples. - */ - mergestate->mj_JoinState = EXEC_MJ_ENDINNER; - break; - } - /* Otherwise we're done. */ - return NULL; - } - - /* - * otherwise test the new tuple against the skip qual. - */ - mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST; - break; - - /* - * EXEC_MJ_ENDOUTER means we have run out of outer tuples, - * but are doing a right/full join and therefore must - * null- fill any remaing unmatched inner tuples. - */ - case EXEC_MJ_ENDOUTER: - MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n"); - - Assert(doFillInner); - - if (!mergestate->mj_MatchedInner) - { - /* - * Generate a fake join tuple with nulls for the outer - * tuple, and return it if it passes the non-join - * quals. - */ - mergestate->mj_MatchedInner = true; /* do it only once */ - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_NullOuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_InnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning fill tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * now we get the next inner tuple, if any - */ - innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node); - mergestate->mj_InnerTupleSlot = innerTupleSlot; - MJ_DEBUG_PROC_NODE(innerTupleSlot); - mergestate->mj_MatchedInner = false; - - if (TupIsNull(innerTupleSlot)) - { - MJ_printf("ExecMergeJoin: end of inner subplan\n"); - return NULL; - } - - /* Else remain in ENDOUTER state and process next tuple. */ - break; - - /* - * EXEC_MJ_ENDINNER means we have run out of inner tuples, - * but are doing a left/full join and therefore must null- - * fill any remaing unmatched outer tuples. - */ - case EXEC_MJ_ENDINNER: - MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n"); - - Assert(doFillOuter); - - if (!mergestate->mj_MatchedOuter) - { - /* - * Generate a fake join tuple with nulls for the inner - * tuple, and return it if it passes the non-join - * quals. - */ - mergestate->mj_MatchedOuter = true; /* do it only once */ - - ResetExprContext(econtext); - - outerTupleSlot = mergestate->mj_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - innerTupleSlot = mergestate->mj_NullInnerTupleSlot; - econtext->ecxt_innertuple = innerTupleSlot; - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification succeeded. now form the desired - * projection tuple and return the slot containing - * it. - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - MJ_printf("ExecMergeJoin: returning fill tuple\n"); - - result = ExecProject(mergestate->jstate.cs_ProjInfo, - &isDone); - - if (isDone != ExprEndResult) - { - mergestate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * now we get the next outer tuple, if any - */ - outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); - mergestate->mj_OuterTupleSlot = outerTupleSlot; - MJ_DEBUG_PROC_NODE(outerTupleSlot); - mergestate->mj_MatchedOuter = false; - - if (TupIsNull(outerTupleSlot)) - { - MJ_printf("ExecMergeJoin: end of outer subplan\n"); - return NULL; - } - - /* Else remain in ENDINNER state and process next tuple. */ - break; - - /* - * if we get here it means our code is fouled up and so we - * just end the join prematurely. - */ - default: - elog(WARNING, "ExecMergeJoin: invalid join state %d, aborting", - mergestate->mj_JoinState); - 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; - - MJ1_printf("ExecInitMergeJoin: %s\n", - "initializing node"); - - /* - * assign the node's execution state and get the range table and - * direction from it - */ - node->join.plan.state = estate; - - /* - * create new merge state for node - */ - mergestate = makeNode(MergeJoinState); - node->mergestate = mergestate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &mergestate->jstate); - - /* - * initialize subplans - */ - ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); - ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); - -#define MERGEJOIN_NSLOTS 4 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &mergestate->jstate); - - mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate); - ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot, - ExecGetTupType(innerPlan((Plan *) node)), - false); - - switch (node->join.jointype) - { - case JOIN_INNER: - break; - case JOIN_LEFT: - mergestate->mj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, - ExecGetTupType(innerPlan((Plan *) node))); - break; - case JOIN_RIGHT: - mergestate->mj_NullOuterTupleSlot = - ExecInitNullTupleSlot(estate, - ExecGetTupType(outerPlan((Plan *) node))); - - /* - * Can't handle right or full join with non-nil extra - * joinclauses. - */ - if (node->join.joinqual != NIL) - elog(ERROR, "RIGHT JOIN is only supported with mergejoinable join conditions"); - break; - case JOIN_FULL: - mergestate->mj_NullOuterTupleSlot = - ExecInitNullTupleSlot(estate, - ExecGetTupType(outerPlan((Plan *) node))); - mergestate->mj_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, - ExecGetTupType(innerPlan((Plan *) node))); - - /* - * Can't handle right or full join with non-nil extra - * joinclauses. - */ - if (node->join.joinqual != NIL) - elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions"); - break; - default: - elog(ERROR, "ExecInitMergeJoin: unsupported join type %d", - (int) node->join.jointype); - } - - /* - * initialize tuple type and projection info - */ - ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate); - ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate); - - /* - * form merge skip qualifications - */ - MJFormSkipQuals(node->mergeclauses, - &mergestate->mj_OuterSkipQual, - &mergestate->mj_InnerSkipQual); - - MJ_printf("\nExecInitMergeJoin: OuterSkipQual is "); - MJ_nodeDisplay(mergestate->mj_OuterSkipQual); - MJ_printf("\nExecInitMergeJoin: InnerSkipQual is "); - MJ_nodeDisplay(mergestate->mj_InnerSkipQual); - MJ_printf("\n"); - - /* - * initialize join state - */ - mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; - mergestate->jstate.cs_TupFromTlist = false; - mergestate->mj_MatchedOuter = false; - mergestate->mj_MatchedInner = false; - mergestate->mj_OuterTupleSlot = NULL; - mergestate->mj_InnerTupleSlot = NULL; - - /* - * 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); - ExecFreeExprContext(&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 - */ - ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot); - ExecClearTuple(mergestate->mj_MarkedTupleSlot); - - MJ1_printf("ExecEndMergeJoin: %s\n", - "node processing ended"); -} - -void -ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent) -{ - MergeJoinState *mergestate = node->mergestate; - - ExecClearTuple(mergestate->mj_MarkedTupleSlot); - - mergestate->mj_JoinState = EXEC_MJ_INITIALIZE; - mergestate->jstate.cs_TupFromTlist = false; - mergestate->mj_MatchedOuter = false; - mergestate->mj_MatchedInner = false; - mergestate->mj_OuterTupleSlot = NULL; - mergestate->mj_InnerTupleSlot = NULL; - - /* - * if chgParam of subnodes is not null then plans will be re-scanned - * by first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); - if (((Plan *) node)->righttree->chgParam == NULL) - ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node); - -} diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c deleted file mode 100644 index 2bd26938fcc..00000000000 --- a/src/backend/executor/nodeNestloop.c +++ /dev/null @@ -1,418 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeNestloop.c - * routines to support nest-loop joins - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.26 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecNestLoop - process a nestloop join of two plans - * ExecInitNestLoop - initialize the join - * ExecEndNestLoop - shut down the join - */ - -#include "postgres.h" - -#include "executor/execdebug.h" -#include "executor/nodeNestloop.h" -#include "utils/memutils.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 from the outer relation is retrieved - * and the inner relation is scanned from the beginning again to join - * with the outer tuple. - * - * NULL is returned if all the remaining outer tuples are tried and - * all fail to join with the inner tuples. - * - * NULL is also returned if there is no tuple from inner relation. - * - * Conditions: - * -- outerTuple contains current tuple from outer relation and - * the right son(inner relation) 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) -{ - NestLoopState *nlstate; - Plan *innerPlan; - Plan *outerPlan; - TupleTableSlot *outerTupleSlot; - TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; - ExprContext *econtext; - - /* - * get information from the node - */ - ENL1_printf("getting info from node"); - - nlstate = node->nlstate; - joinqual = node->join.joinqual; - otherqual = node->join.plan.qual; - outerPlan = outerPlan((Plan *) node); - innerPlan = innerPlan((Plan *) node); - econtext = nlstate->jstate.cs_ExprContext; - - /* - * get the current outer tuple - */ - outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - - /* - * Check to see if we're still projecting out tuples from a previous - * join tuple (because there is a function-returning-set in the - * projection expressions). If so, try to project another one. - */ - if (nlstate->jstate.cs_TupFromTlist) - { - TupleTableSlot *result; - ExprDoneCond isDone; - - result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return result; - /* Done with that source tuple... */ - nlstate->jstate.cs_TupFromTlist = false; - } - - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. Note this can't - * happen until we're done projecting out tuples from a join tuple. - */ - ResetExprContext(econtext); - - /* - * Ok, everything is setup for the join so now loop until we return a - * qualifying join tuple. - */ - ENL1_printf("entering main loop"); - - for (;;) - { - /* - * If we don't have an outer tuple, get the next one and reset the - * inner scan. - */ - if (nlstate->nl_NeedNewOuter) - { - ENL1_printf("getting new outer tuple"); - outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); - - /* - * if there are no more outer tuples, then the join is - * complete.. - */ - if (TupIsNull(outerTupleSlot)) - { - ENL1_printf("no outer tuple, ending join"); - return NULL; - } - - ENL1_printf("saving new outer tuple information"); - nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - econtext->ecxt_outertuple = outerTupleSlot; - nlstate->nl_NeedNewOuter = false; - nlstate->nl_MatchedOuter = false; - - /* - * now rescan the inner plan - */ - 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, (Plan *) node); - } - - /* - * we have an outerTuple, try to get the next inner tuple. - */ - 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"); - - nlstate->nl_NeedNewOuter = true; - - if (!nlstate->nl_MatchedOuter && - node->join.jointype == JOIN_LEFT) - { - /* - * We are doing an outer join and there were no join - * matches for this outer tuple. Generate a fake join - * tuple with nulls for the inner tuple, and return it if - * it passes the non-join quals. - */ - econtext->ecxt_innertuple = nlstate->nl_NullInnerTupleSlot; - - ENL1_printf("testing qualification for outer-join tuple"); - - if (ExecQual(otherqual, econtext, false)) - { - /* - * qualification was satisfied so we project and - * return the slot containing the result tuple using - * ExecProject(). - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - ENL1_printf("qualification succeeded, projecting tuple"); - - result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - nlstate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * Otherwise just return to top of loop for a new outer tuple. - */ - continue; - } - - /* - * 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 satisfy the - * node's qualification. - * - * Only the joinquals determine MatchedOuter status, but all quals - * must pass to actually return the tuple. - */ - ENL1_printf("testing qualification"); - - if (ExecQual(joinqual, econtext, false)) - { - nlstate->nl_MatchedOuter = true; - - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) - { - /* - * qualification was satisfied so we project and return - * the slot containing the result tuple using - * ExecProject(). - */ - TupleTableSlot *result; - ExprDoneCond isDone; - - ENL1_printf("qualification succeeded, projecting tuple"); - - result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - nlstate->jstate.cs_TupFromTlist = - (isDone == ExprMultipleResult); - return result; - } - } - } - - /* - * Tuple fails qual, so free per-tuple memory and try again. - */ - ResetExprContext(econtext); - - 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.plan.state = estate; - - /* - * create new nest loop state - */ - nlstate = makeNode(NestLoopState); - node->nlstate = nlstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &nlstate->jstate); - - /* - * now initialize children - */ - ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node); - ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node); - -#define NESTLOOP_NSLOTS 2 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &nlstate->jstate); - - switch (node->join.jointype) - { - case JOIN_INNER: - break; - case JOIN_LEFT: - nlstate->nl_NullInnerTupleSlot = - ExecInitNullTupleSlot(estate, - ExecGetTupType(innerPlan((Plan *) node))); - break; - default: - elog(ERROR, "ExecInitNestLoop: unsupported join type %d", - (int) node->join.jointype); - } - - /* - * 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; - nlstate->nl_NeedNewOuter = true; - nlstate->nl_MatchedOuter = 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); - ExecFreeExprContext(&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"); -} - -/* ---------------------------------------------------------------- - * ExecReScanNestLoop - * ---------------------------------------------------------------- - */ -void -ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent) -{ - NestLoopState *nlstate = node->nlstate; - Plan *outerPlan = outerPlan((Plan *) node); - - /* - * If outerPlan->chgParam is not null then plan will be automatically - * re-scanned by first ExecProcNode. innerPlan is re-scanned for each - * new outer tuple and MUST NOT be re-scanned from here or you'll get - * troubles from inner index scans when outer Vars are used as - * run-time keys... - */ - if (outerPlan->chgParam == NULL) - ExecReScan(outerPlan, exprCtxt, (Plan *) node); - - /* let outerPlan to free its result tuple ... */ - nlstate->jstate.cs_OuterTupleSlot = NULL; - nlstate->jstate.cs_TupFromTlist = false; - nlstate->nl_NeedNewOuter = true; - nlstate->nl_MatchedOuter = false; -} diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c deleted file mode 100644 index d9d1608a81d..00000000000 --- a/src/backend/executor/nodeResult.c +++ /dev/null @@ -1,289 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeResult.c - * support for constant nodes needing special code. - * - * DESCRIPTION - * - * Result nodes are used in queries where no relations are scanned. - * Examples of such queries are: - * - * retrieve (x = 1) - * and - * append emp (name = "mike", salary = 15000) - * - * Result nodes are also used to optimise queries with constant - * qualifications (ie, quals that do not depend on the scanned data), - * such as: - * - * retrieve (emp.all) where 2 > 1 - * - * In this case, the plan generated is - * - * Result (with 2 > 1 qual) - * / - * SeqScan (emp.all) - * - * At runtime, the Result node evaluates the constant qual once. - * If it's false, we can return an empty result set without running - * the controlled plan at all. If it's true, we run the controlled - * plan normally and pass back the results. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.21 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "executor/executor.h" -#include "executor/nodeResult.h" -#include "utils/memutils.h" - - -/* ---------------------------------------------------------------- - * ExecResult(node) - * - * returns the tuples from the outer plan which satisfy 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; - ExprDoneCond isDone; - - /* - * initialize the result node's state - */ - resstate = node->resstate; - econtext = resstate->cstate.cs_ExprContext; - - /* - * check constant qualifications like (2 > 1), if not already done - */ - if (resstate->rs_checkqual) - { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); - - resstate->rs_checkqual = false; - if (qualResult == false) - { - resstate->rs_done = true; - return NULL; - } - } - - /* - * Check to see if we're still projecting out tuples from a previous - * scan tuple (because there is a function-returning-set in the - * projection expressions). If so, try to project another one. - */ - if (resstate->cstate.cs_TupFromTlist) - { - resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone); - if (isDone == ExprMultipleResult) - return resultSlot; - /* Done with that source tuple... */ - resstate->cstate.cs_TupFromTlist = false; - } - - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. Note this can't - * happen until we're done projecting out tuples from a scan tuple. - */ - ResetExprContext(econtext); - - /* - * if rs_done is true then it means that we were asked to return a - * constant tuple and we already did the last time ExecResult() was - * called, OR that we failed the constant qual check. Either way, now - * we are through. - */ - while (!resstate->rs_done) - { - outerPlan = outerPlan(node); - - if (outerPlan != NULL) - { - /* - * retrieve tuples from the outer plan until there are no - * more. - */ - outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node); - - if (TupIsNull(outerTupleSlot)) - return NULL; - - resstate->cstate.cs_OuterTupleSlot = outerTupleSlot; - - /* - * XXX gross hack. use outer tuple as scan tuple for - * projection - */ - econtext->ecxt_outertuple = outerTupleSlot; - econtext->ecxt_scantuple = outerTupleSlot; - } - else - { - /* - * if we don't have an outer plan, then we are just generating - * the results from a constant target list. Do it only once. - */ - resstate->rs_done = true; - } - - /* - * form the result tuple using ExecProject(), and return it --- - * unless the projection produces an empty set, in which case we - * must loop back to see if there are more outerPlan tuples. - */ - resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone); - - if (isDone != ExprEndResult) - { - resstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult); - 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 = false; - resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true; - node->resstate = resstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - 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); - - return TRUE; -} - -int -ExecCountSlotsResult(Result *node) -{ - return ExecCountSlotsNode(outerPlan(node)) + RESULT_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndResult - * - * frees 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); - ExecFreeExprContext(&resstate->cstate); - - /* - * shut down subplans - */ - ExecEndNode(outerPlan(node), (Plan *) node); - - /* - * clean out the tuple table - */ - ExecClearTuple(resstate->cstate.cs_ResultTupleSlot); - pfree(resstate); - node->resstate = NULL; /* XXX - new for us - er1p */ -} - -void -ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent) -{ - ResultState *resstate = node->resstate; - - resstate->rs_done = false; - resstate->cstate.cs_TupFromTlist = false; - resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true; - - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (((Plan *) node)->lefttree && - ((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); -} diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c deleted file mode 100644 index ddfcd3b8dad..00000000000 --- a/src/backend/executor/nodeSeqscan.c +++ /dev/null @@ -1,366 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeSeqscan.c - * Support routines for sequential scans of relations. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.36 2002/06/20 20:29:28 momjian 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 "postgres.h" - -#include "access/heapam.h" -#include "executor/execdebug.h" -#include "executor/nodeSeqscan.h" -#include "parser/parsetree.h" - -static Oid InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate); -static TupleTableSlot *SeqNext(SeqScan *node); - -/* ---------------------------------------------------------------- - * Scan Support - * ---------------------------------------------------------------- - */ -/* ---------------------------------------------------------------- - * SeqNext - * - * This is a workhorse for ExecSeqScan - * ---------------------------------------------------------------- - */ -static TupleTableSlot * -SeqNext(SeqScan *node) -{ - HeapTuple tuple; - HeapScanDesc scandesc; - CommonScanState *scanstate; - EState *estate; - ScanDirection direction; - TupleTableSlot *slot; - - /* - * get information from the estate and scan state - */ - estate = node->plan.state; - scanstate = node->scanstate; - scandesc = scanstate->css_currentScanDesc; - direction = estate->es_direction; - slot = scanstate->css_ScanTupleSlot; - - /* - * Check if we are evaluating PlanQual for tuple of this relation. - * Additional checking is not good, but no other way for now. We could - * introduce new nodes for this case and handle SeqScan --> NewNode - * switching in Init/ReScan plan... - */ - if (estate->es_evTuple != NULL && - estate->es_evTuple[node->scanrelid - 1] != NULL) - { - ExecClearTuple(slot); - if (estate->es_evTupleNull[node->scanrelid - 1]) - return slot; /* return empty slot */ - - ExecStoreTuple(estate->es_evTuple[node->scanrelid - 1], - slot, InvalidBuffer, false); - - /* - * Note that unlike IndexScan, SeqScan never use keys in - * heap_beginscan (and this is very bad) - so, here we do not - * check are keys ok or not. - */ - - /* Flag for the next call that no more tuples */ - estate->es_evTupleNull[node->scanrelid - 1] = true; - return (slot); - } - - /* - * get the next tuple from the access methods - */ - tuple = heap_getnext(scandesc, direction); - - /* - * 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. Note also that ExecStoreTuple will increment the - * refcount of the buffer; the refcount will not be dropped until the - * tuple table slot is cleared. - */ - - slot = ExecStoreTuple(tuple, /* tuple to store */ - slot, /* slot to store in */ - scandesc->rs_cbuf, /* buffer associated with - * this tuple */ - false); /* don't pfree this pointer */ - - 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) -{ - /* - * use SeqNext as access method - */ - return ExecScan(node, (ExecScanAccessMtd) SeqNext); -} - -/* ---------------------------------------------------------------- - * InitScanRelation - * - * This does the initialization for scan relations and - * subplans of scans. - * ---------------------------------------------------------------- - */ -static Oid -InitScanRelation(SeqScan *node, EState *estate, - CommonScanState *scanstate) -{ - Index relid; - List *rangeTable; - RangeTblEntry *rtentry; - Oid reloid; - Relation currentRelation; - HeapScanDesc currentScanDesc; - - /* - * get the relation object id from the relid'th entry in the range - * table, open that relation and initialize the scan state. - * - * We acquire AccessShareLock for the duration of the scan. - */ - relid = node->scanrelid; - rangeTable = estate->es_range_table; - rtentry = rt_fetch(relid, rangeTable); - reloid = rtentry->relid; - - currentRelation = heap_open(reloid, AccessShareLock); - - currentScanDesc = heap_beginscan(currentRelation, - estate->es_snapshot, - 0, - NULL); - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = currentScanDesc; - - ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false); - - return reloid; -} - - -/* ---------------------------------------------------------------- - * ExecInitSeqScan - * ---------------------------------------------------------------- - */ -bool -ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent) -{ - CommonScanState *scanstate; - Oid reloid; - - /* - * Once upon a time it was possible to have an outerPlan of a SeqScan, - * but not any more. - */ - Assert(outerPlan((Plan *) node) == NULL); - Assert(innerPlan((Plan *) node) == NULL); - - /* - * assign the node's execution state - */ - node->plan.state = estate; - - /* - * create new CommonScanState for node - */ - scanstate = makeNode(CommonScanState); - node->scanstate = scanstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &scanstate->cstate); - -#define SEQSCAN_NSLOTS 2 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(estate, scanstate); - - /* - * initialize scan relation - */ - reloid = InitScanRelation(node, estate, scanstate); - - 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. - * ---------------------------------------------------------------- - */ -void -ExecEndSeqScan(SeqScan *node) -{ - CommonScanState *scanstate; - Relation relation; - HeapScanDesc scanDesc; - - /* - * get information from node - */ - scanstate = node->scanstate; - relation = scanstate->css_currentRelation; - scanDesc = scanstate->css_currentScanDesc; - - /* - * 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); - ExecFreeExprContext(&scanstate->cstate); - - /* - * close heap scan - */ - heap_endscan(scanDesc); - - /* - * close the heap relation. - * - * Currently, we do not release the AccessShareLock acquired by - * InitScanRelation. This lock should be held till end of transaction. - * (There is a faction that considers this too much locking, however.) - */ - heap_close(relation, NoLock); - - /* - * 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; - HeapScanDesc scan; - - scanstate = node->scanstate; - estate = node->plan.state; - - /* If this is re-scanning of PlanQual ... */ - if (estate->es_evTuple != NULL && - estate->es_evTuple[node->scanrelid - 1] != NULL) - { - estate->es_evTupleNull[node->scanrelid - 1] = false; - return; - } - - scan = scanstate->css_currentScanDesc; - - heap_rescan(scan, /* scan desc */ - NULL); /* new scan keys */ -} - -/* ---------------------------------------------------------------- - * ExecSeqMarkPos(node) - * - * Marks scan position. - * ---------------------------------------------------------------- - */ -void -ExecSeqMarkPos(SeqScan *node) -{ - CommonScanState *scanstate; - HeapScanDesc scan; - - scanstate = node->scanstate; - scan = scanstate->css_currentScanDesc; - heap_markpos(scan); -} - -/* ---------------------------------------------------------------- - * ExecSeqRestrPos - * - * Restores scan position. - * ---------------------------------------------------------------- - */ -void -ExecSeqRestrPos(SeqScan *node) -{ - CommonScanState *scanstate; - HeapScanDesc scan; - - scanstate = node->scanstate; - scan = scanstate->css_currentScanDesc; - heap_restrpos(scan); -} diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c deleted file mode 100644 index 3d1cf2c8efa..00000000000 --- a/src/backend/executor/nodeSetOp.c +++ /dev/null @@ -1,334 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeSetOp.c - * Routines to handle INTERSECT and EXCEPT selection - * - * The input of a SetOp node consists of tuples from two relations, - * which have been combined into one dataset and sorted on all the nonjunk - * attributes. In addition there is a junk attribute that shows which - * relation each tuple came from. The SetOp node scans each group of - * identical tuples to determine how many came from each input relation. - * Then it is a simple matter to emit the output demanded by the SQL spec - * for INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL. - * - * This node type is not used for UNION or UNION ALL, since those can be - * implemented more cheaply (there's no need for the junk attribute to - * identify the source relation). - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.6 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecSetOp - filter input to generate INTERSECT/EXCEPT results - * ExecInitSetOp - initialize node and subnodes.. - * ExecEndSetOp - shutdown node and subnodes - */ - -#include "postgres.h" - -#include "access/heapam.h" -#include "executor/executor.h" -#include "executor/nodeGroup.h" -#include "executor/nodeSetOp.h" - -/* ---------------------------------------------------------------- - * ExecSetOp - * ---------------------------------------------------------------- - */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecSetOp(SetOp *node) -{ - SetOpState *setopstate; - TupleTableSlot *resultTupleSlot; - Plan *outerPlan; - TupleDesc tupDesc; - - /* - * get information from the node - */ - setopstate = node->setopstate; - outerPlan = outerPlan((Plan *) node); - resultTupleSlot = setopstate->cstate.cs_ResultTupleSlot; - tupDesc = ExecGetResultType(&setopstate->cstate); - - /* - * If the previously-returned tuple needs to be returned more than - * once, keep returning it. - */ - if (setopstate->numOutput > 0) - { - setopstate->numOutput--; - return resultTupleSlot; - } - - /* Flag that we have no current tuple */ - ExecClearTuple(resultTupleSlot); - - /* - * Absorb groups of duplicate tuples, counting them, and saving the - * first of each group as a possible return value. At the end of each - * group, decide whether to return anything. - * - * We assume that the tuples arrive in sorted order so we can detect - * duplicates easily. - */ - for (;;) - { - TupleTableSlot *inputTupleSlot; - bool endOfGroup; - - /* - * fetch a tuple from the outer subplan, unless we already did. - */ - if (setopstate->cstate.cs_OuterTupleSlot == NULL && - !setopstate->subplan_done) - { - setopstate->cstate.cs_OuterTupleSlot = - ExecProcNode(outerPlan, (Plan *) node); - if (TupIsNull(setopstate->cstate.cs_OuterTupleSlot)) - setopstate->subplan_done = true; - } - inputTupleSlot = setopstate->cstate.cs_OuterTupleSlot; - - if (TupIsNull(resultTupleSlot)) - { - /* - * First of group: save a copy in result slot, and reset - * duplicate-counters for new group. - */ - if (setopstate->subplan_done) - return NULL; /* no more tuples */ - ExecStoreTuple(heap_copytuple(inputTupleSlot->val), - resultTupleSlot, - InvalidBuffer, - true); /* free copied tuple at - * ExecClearTuple */ - setopstate->numLeft = 0; - setopstate->numRight = 0; - endOfGroup = false; - } - else if (setopstate->subplan_done) - { - /* - * Reached end of input, so finish processing final group - */ - endOfGroup = true; - } - else - { - /* - * Else test if the new tuple and the previously saved tuple - * match. - */ - if (execTuplesMatch(inputTupleSlot->val, - resultTupleSlot->val, - tupDesc, - node->numCols, node->dupColIdx, - setopstate->eqfunctions, - setopstate->tempContext)) - endOfGroup = false; - else - endOfGroup = true; - } - - if (endOfGroup) - { - /* - * We've reached the end of the group containing resultTuple. - * Decide how many copies (if any) to emit. This logic is - * straight from the SQL92 specification. - */ - switch (node->cmd) - { - case SETOPCMD_INTERSECT: - if (setopstate->numLeft > 0 && setopstate->numRight > 0) - setopstate->numOutput = 1; - else - setopstate->numOutput = 0; - break; - case SETOPCMD_INTERSECT_ALL: - setopstate->numOutput = - (setopstate->numLeft < setopstate->numRight) ? - setopstate->numLeft : setopstate->numRight; - break; - case SETOPCMD_EXCEPT: - if (setopstate->numLeft > 0 && setopstate->numRight == 0) - setopstate->numOutput = 1; - else - setopstate->numOutput = 0; - break; - case SETOPCMD_EXCEPT_ALL: - setopstate->numOutput = - (setopstate->numLeft < setopstate->numRight) ? - 0 : (setopstate->numLeft - setopstate->numRight); - break; - default: - elog(ERROR, "ExecSetOp: bogus command code %d", - (int) node->cmd); - break; - } - /* Fall out of for-loop if we have tuples to emit */ - if (setopstate->numOutput > 0) - break; - /* Else flag that we have no current tuple, and loop around */ - ExecClearTuple(resultTupleSlot); - } - else - { - /* - * Current tuple is member of same group as resultTuple. Count - * it in the appropriate counter. - */ - int flag; - bool isNull; - - flag = DatumGetInt32(heap_getattr(inputTupleSlot->val, - node->flagColIdx, - tupDesc, - &isNull)); - Assert(!isNull); - if (flag) - setopstate->numRight++; - else - setopstate->numLeft++; - /* Set flag to fetch a new input tuple, and loop around */ - setopstate->cstate.cs_OuterTupleSlot = NULL; - } - } - - /* - * If we fall out of loop, then we need to emit at least one copy of - * resultTuple. - */ - Assert(setopstate->numOutput > 0); - setopstate->numOutput--; - return resultTupleSlot; -} - -/* ---------------------------------------------------------------- - * ExecInitSetOp - * - * This initializes the setop node state structures and - * the node's subplan. - * ---------------------------------------------------------------- - */ -bool /* return: initialization status */ -ExecInitSetOp(SetOp *node, EState *estate, Plan *parent) -{ - SetOpState *setopstate; - Plan *outerPlan; - - /* - * assign execution state to node - */ - node->plan.state = estate; - - /* - * create new SetOpState for node - */ - setopstate = makeNode(SetOpState); - node->setopstate = setopstate; - setopstate->cstate.cs_OuterTupleSlot = NULL; - setopstate->subplan_done = false; - setopstate->numOutput = 0; - - /* - * Miscellaneous initialization - * - * SetOp nodes have no ExprContext initialization because they never call - * ExecQual or ExecProject. But they do need a per-tuple memory - * context anyway for calling execTuplesMatch. - */ - setopstate->tempContext = - AllocSetContextCreate(CurrentMemoryContext, - "SetOp", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - -#define SETOP_NSLOTS 1 - - /* - * Tuple table initialization - */ - ExecInitResultTupleSlot(estate, &setopstate->cstate); - - /* - * then initialize outer plan - */ - outerPlan = outerPlan((Plan *) node); - ExecInitNode(outerPlan, estate, (Plan *) node); - - /* - * setop nodes do no projections, so initialize projection info for - * this node appropriately - */ - ExecAssignResultTypeFromOuterPlan((Plan *) node, &setopstate->cstate); - setopstate->cstate.cs_ProjInfo = NULL; - - /* - * Precompute fmgr lookup data for inner loop - */ - setopstate->eqfunctions = - execTuplesMatchPrepare(ExecGetResultType(&setopstate->cstate), - node->numCols, - node->dupColIdx); - - return TRUE; -} - -int -ExecCountSlotsSetOp(SetOp *node) -{ - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - SETOP_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndSetOp - * - * This shuts down the subplan and frees resources allocated - * to this node. - * ---------------------------------------------------------------- - */ -void -ExecEndSetOp(SetOp *node) -{ - SetOpState *setopstate = node->setopstate; - - ExecEndNode(outerPlan((Plan *) node), (Plan *) node); - - MemoryContextDelete(setopstate->tempContext); - - /* clean up tuple table */ - ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot); - setopstate->cstate.cs_OuterTupleSlot = NULL; -} - - -void -ExecReScanSetOp(SetOp *node, ExprContext *exprCtxt, Plan *parent) -{ - SetOpState *setopstate = node->setopstate; - - ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot); - setopstate->cstate.cs_OuterTupleSlot = NULL; - setopstate->subplan_done = false; - setopstate->numOutput = 0; - - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); -} diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c deleted file mode 100644 index 6ce648e2b24..00000000000 --- a/src/backend/executor/nodeSort.c +++ /dev/null @@ -1,397 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeSort.c - * Routines to handle sorting of relations. - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.36 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "executor/execdebug.h" -#include "executor/nodeSort.h" -#include "utils/tuplesort.h" - -/* ---------------------------------------------------------------- - * ExtractSortKeys - * - * Extract the sorting key information from the plan node. - * - * Returns two palloc'd arrays, one of sort operator OIDs and - * one of attribute numbers. - * ---------------------------------------------------------------- - */ -static void -ExtractSortKeys(Sort *sortnode, - Oid **sortOperators, - AttrNumber **attNums) -{ - List *targetList; - int keycount; - Oid *sortOps; - AttrNumber *attNos; - List *tl; - - /* - * get information from the node - */ - targetList = sortnode->plan.targetlist; - keycount = sortnode->keycount; - - /* - * first allocate space for results - */ - if (keycount <= 0) - elog(ERROR, "ExtractSortKeys: keycount <= 0"); - sortOps = (Oid *) palloc(keycount * sizeof(Oid)); - MemSet(sortOps, 0, keycount * sizeof(Oid)); - *sortOperators = sortOps; - attNos = (AttrNumber *) palloc(keycount * sizeof(AttrNumber)); - MemSet(attNos, 0, keycount * sizeof(AttrNumber)); - *attNums = attNos; - - /* - * extract info from the resdom nodes in the target list - */ - foreach(tl, targetList) - { - TargetEntry *target = (TargetEntry *) lfirst(tl); - Resdom *resdom = target->resdom; - Index reskey = resdom->reskey; - - if (reskey > 0) /* ignore TLEs that are not sort keys */ - { - Assert(reskey <= keycount); - sortOps[reskey - 1] = resdom->reskeyop; - attNos[reskey - 1] = resdom->resno; - } - } -} - -/* ---------------------------------------------------------------- - * ExecSort - * - * Sorts tuples from the outer subtree of the node using tuplesort, - * which saves the results in a temporary file or memory. After the - * initial call, returns a tuple from the file with each call. - * - * Conditions: - * -- none. - * - * Initial States: - * -- the outer child is prepared to return the first tuple. - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecSort(Sort *node) -{ - EState *estate; - SortState *sortstate; - ScanDirection dir; - Tuplesortstate *tuplesortstate; - HeapTuple heapTuple; - TupleTableSlot *slot; - bool should_free; - - /* - * get state info from node - */ - SO1_printf("ExecSort: %s\n", - "entering routine"); - - sortstate = node->sortstate; - estate = node->plan.state; - dir = estate->es_direction; - tuplesortstate = (Tuplesortstate *) sortstate->tuplesortstate; - - /* - * If first time through, read all tuples from outer plan and pass - * them to tuplesort.c. Subsequent calls just fetch tuples from - * tuplesort. - */ - - if (!sortstate->sort_Done) - { - Plan *outerNode; - TupleDesc tupDesc; - Oid *sortOperators; - AttrNumber *attNums; - - SO1_printf("ExecSort: %s\n", - "sorting subplan"); - - /* - * Want to scan subplan in the forward direction while creating - * the sorted data. (Does setting my direction actually affect - * the subplan? I bet this is useless code...) - */ - estate->es_direction = ForwardScanDirection; - - /* - * Initialize tuplesort module. - */ - SO1_printf("ExecSort: %s\n", - "calling tuplesort_begin"); - - outerNode = outerPlan((Plan *) node); - tupDesc = ExecGetTupType(outerNode); - - ExtractSortKeys(node, &sortOperators, &attNums); - - tuplesortstate = tuplesort_begin_heap(tupDesc, node->keycount, - sortOperators, attNums, - true /* randomAccess */ ); - sortstate->tuplesortstate = (void *) tuplesortstate; - - pfree(sortOperators); - pfree(attNums); - - /* - * Scan the subplan and feed all the tuples to tuplesort. - */ - - for (;;) - { - slot = ExecProcNode(outerNode, (Plan *) node); - - if (TupIsNull(slot)) - break; - - tuplesort_puttuple(tuplesortstate, (void *) slot->val); - } - - /* - * Complete the sort. - */ - tuplesort_performsort(tuplesortstate); - - /* - * restore to user specified direction - */ - estate->es_direction = dir; - - /* - * make sure the tuple descriptor is up to date (is this needed?) - */ - ExecAssignResultType(&sortstate->csstate.cstate, tupDesc, false); - - /* - * finally set the sorted flag to true - */ - sortstate->sort_Done = true; - SO1_printf(stderr, "ExecSort: sorting done.\n"); - } - - SO1_printf("ExecSort: %s\n", - "retrieving tuple from tuplesort"); - - /* - * Get the first or next tuple from tuplesort. Returns NULL if no more - * tuples. - */ - heapTuple = tuplesort_getheaptuple(tuplesortstate, - ScanDirectionIsForward(dir), - &should_free); - - slot = sortstate->csstate.cstate.cs_ResultTupleSlot; - return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); -} - -/* ---------------------------------------------------------------- - * ExecInitSort - * - * 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; - - 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_Done = false; - sortstate->tuplesortstate = NULL; - - node->sortstate = sortstate; - - /* - * Miscellaneous initialization - * - * Sort nodes don't initialize their ExprContexts because they never call - * ExecQual or ExecProject. - */ - -#define SORT_NSLOTS 2 - - /* - * tuple table initialization - * - * sort nodes only return scan tuples from their sorted relation. - */ - ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate); - ExecInitScanTupleSlot(estate, &sortstate->csstate); - - /* - * initializes child nodes - */ - outerPlan = outerPlan((Plan *) 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, &sortstate->csstate.cstate); - ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate); - sortstate->csstate.cstate.cs_ProjInfo = NULL; - - SO1_printf("ExecInitSort: %s\n", - "sort node initialized"); - - return TRUE; -} - -int -ExecCountSlotsSort(Sort *node) -{ - return ExecCountSlotsNode(outerPlan((Plan *) node)) + - ExecCountSlotsNode(innerPlan((Plan *) node)) + - SORT_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndSort(node) - * ---------------------------------------------------------------- - */ -void -ExecEndSort(Sort *node) -{ - SortState *sortstate; - Plan *outerPlan; - - /* - * get info from the sort state - */ - SO1_printf("ExecEndSort: %s\n", - "shutting down sort node"); - - sortstate = node->sortstate; - - /* - * shut down the subplan - */ - outerPlan = outerPlan((Plan *) node); - ExecEndNode(outerPlan, (Plan *) node); - - /* - * clean out the tuple table - */ - ExecClearTuple(sortstate->csstate.css_ScanTupleSlot); - - /* - * Release tuplesort resources - */ - if (sortstate->tuplesortstate != NULL) - tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate); - sortstate->tuplesortstate = NULL; - - pfree(sortstate); - node->sortstate = NULL; - - SO1_printf("ExecEndSort: %s\n", - "sort node shutdown"); -} - -/* ---------------------------------------------------------------- - * ExecSortMarkPos - * - * Calls tuplesort to save the current position in the sorted file. - * ---------------------------------------------------------------- - */ -void -ExecSortMarkPos(Sort *node) -{ - SortState *sortstate = node->sortstate; - - /* - * if we haven't sorted yet, just return - */ - if (!sortstate->sort_Done) - return; - - tuplesort_markpos((Tuplesortstate *) sortstate->tuplesortstate); -} - -/* ---------------------------------------------------------------- - * ExecSortRestrPos - * - * Calls tuplesort to restore the last saved sort file position. - * ---------------------------------------------------------------- - */ -void -ExecSortRestrPos(Sort *node) -{ - SortState *sortstate = node->sortstate; - - /* - * if we haven't sorted yet, just return. - */ - if (!sortstate->sort_Done) - return; - - /* - * restore the scan to the previously marked position - */ - tuplesort_restorepos((Tuplesortstate *) sortstate->tuplesortstate); -} - -void -ExecReScanSort(Sort *node, ExprContext *exprCtxt, Plan *parent) -{ - SortState *sortstate = node->sortstate; - - /* - * If we haven't sorted yet, just return. If outerplan' chgParam is - * not NULL then it will be re-scanned by ExecProcNode, else - no - * reason to re-scan it at all. - */ - if (!sortstate->sort_Done) - return; - - ExecClearTuple(sortstate->csstate.cstate.cs_ResultTupleSlot); - - /* - * If subnode is to be rescanned then we forget previous sort results; - * we have to re-read the subplan and re-sort. - * - * Otherwise we can just rewind and rescan the sorted output. - */ - if (((Plan *) node)->lefttree->chgParam != NULL) - { - sortstate->sort_Done = false; - tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate); - sortstate->tuplesortstate = NULL; - } - else - tuplesort_rescan((Tuplesortstate *) sortstate->tuplesortstate); -} diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c deleted file mode 100644 index 0c7b3557cff..00000000000 --- a/src/backend/executor/nodeSubplan.c +++ /dev/null @@ -1,491 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeSubplan.c - * routines to support subselects - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.33 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecSubPlan - process a subselect - * ExecInitSubPlan - initialize a subselect - * ExecEndSubPlan - shut down a subselect - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "executor/executor.h" -#include "executor/nodeSubplan.h" -#include "tcop/pquery.h" - - -/* ---------------------------------------------------------------- - * ExecSubPlan(node) - * - * ---------------------------------------------------------------- - */ -Datum -ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) -{ - Plan *plan = node->plan; - SubLink *sublink = node->sublink; - SubLinkType subLinkType = sublink->subLinkType; - bool useor = sublink->useor; - MemoryContext oldcontext; - TupleTableSlot *slot; - Datum result; - bool found = false; /* TRUE if got at least one subplan tuple */ - List *lst; - - /* - * We are probably in a short-lived expression-evaluation context. - * Switch to longer-lived per-query context. - */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - - if (node->setParam != NIL) - elog(ERROR, "ExecSubPlan: can't set parent params from subquery"); - - /* - * Set Params of this plan from parent plan correlation Vars - */ - if (node->parParam != NIL) - { - foreach(lst, node->parParam) - { - ParamExecData *prm; - - prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]); - Assert(pvar != NIL); - prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar), - econtext, - &(prm->isnull), - NULL); - pvar = lnext(pvar); - } - plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam)); - } - Assert(pvar == NIL); - - ExecReScan(plan, NULL, NULL); - - /* - * For all sublink types except EXPR_SUBLINK, the result is boolean as - * are the results of the combining operators. We combine results - * within a tuple (if there are multiple columns) using OR semantics - * if "useor" is true, AND semantics if not. We then combine results - * across tuples (if the subplan produces more than one) using OR - * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK. - * (MULTIEXPR_SUBLINK doesn't allow multiple tuples from the subplan.) - * NULL results from the combining operators are handled according to - * the usual SQL semantics for OR and AND. The result for no input - * tuples is FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for - * MULTIEXPR_SUBLINK. - * - * For EXPR_SUBLINK we require the subplan to produce no more than one - * tuple, else an error is raised. If zero tuples are produced, we - * return NULL. Assuming we get a tuple, we just return its first - * column (there can be only one non-junk column in this case). - */ - result = BoolGetDatum(subLinkType == ALL_SUBLINK); - *isNull = false; - - for (slot = ExecProcNode(plan, NULL); - !TupIsNull(slot); - slot = ExecProcNode(plan, NULL)) - { - HeapTuple tup = slot->val; - TupleDesc tdesc = slot->ttc_tupleDescriptor; - Datum rowresult = BoolGetDatum(!useor); - bool rownull = false; - int col = 1; - - if (subLinkType == EXISTS_SUBLINK) - { - found = true; - result = BoolGetDatum(true); - break; - } - - if (subLinkType == EXPR_SUBLINK) - { - /* cannot allow multiple input tuples for EXPR sublink */ - if (found) - elog(ERROR, "More than one tuple returned by a subselect used as an expression."); - found = true; - - /* - * We need to copy the subplan's tuple in case the result is - * of pass-by-ref type --- our return value will point into - * this copied tuple! Can't use the subplan's instance of the - * tuple since it won't still be valid after next - * ExecProcNode() call. node->curTuple keeps track of the - * copied tuple for eventual freeing. - */ - tup = heap_copytuple(tup); - if (node->curTuple) - heap_freetuple(node->curTuple); - node->curTuple = tup; - result = heap_getattr(tup, col, tdesc, isNull); - /* keep scanning subplan to make sure there's only one tuple */ - continue; - } - - /* cannot allow multiple input tuples for MULTIEXPR sublink either */ - if (subLinkType == MULTIEXPR_SUBLINK && found) - elog(ERROR, "More than one tuple returned by a subselect used as an expression."); - - found = true; - - /* - * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining - * operators for columns of tuple. - */ - foreach(lst, sublink->oper) - { - Expr *expr = (Expr *) lfirst(lst); - Const *con = lsecond(expr->args); - Datum expresult; - bool expnull; - - /* - * The righthand side of the expression should be either a - * Const or a function call or RelabelType node taking a Const - * as arg (these nodes represent run-time type coercions - * inserted by the parser to get to the input type needed by - * the operator). Find the Const node and insert the actual - * righthand-side value into it. - */ - if (!IsA(con, Const)) - { - switch (con->type) - { - case T_Expr: - con = lfirst(((Expr *) con)->args); - break; - case T_RelabelType: - con = (Const *) (((RelabelType *) con)->arg); - break; - default: - /* will fail below */ - break; - } - if (!IsA(con, Const)) - elog(ERROR, "ExecSubPlan: failed to find placeholder for subplan result"); - } - con->constvalue = heap_getattr(tup, col, tdesc, - &(con->constisnull)); - - /* - * Now we can eval the combining operator for this column. - */ - expresult = ExecEvalExprSwitchContext((Node *) expr, econtext, - &expnull, NULL); - - /* - * Combine the result into the row result as appropriate. - */ - if (col == 1) - { - rowresult = expresult; - rownull = expnull; - } - else if (useor) - { - /* combine within row per OR semantics */ - if (expnull) - rownull = true; - else if (DatumGetBool(expresult)) - { - rowresult = BoolGetDatum(true); - rownull = false; - break; /* needn't look at any more columns */ - } - } - else - { - /* combine within row per AND semantics */ - if (expnull) - rownull = true; - else if (!DatumGetBool(expresult)) - { - rowresult = BoolGetDatum(false); - rownull = false; - break; /* needn't look at any more columns */ - } - } - col++; - } - - if (subLinkType == ANY_SUBLINK) - { - /* combine across rows per OR semantics */ - if (rownull) - *isNull = true; - else if (DatumGetBool(rowresult)) - { - result = BoolGetDatum(true); - *isNull = false; - break; /* needn't look at any more rows */ - } - } - else if (subLinkType == ALL_SUBLINK) - { - /* combine across rows per AND semantics */ - if (rownull) - *isNull = true; - else if (!DatumGetBool(rowresult)) - { - result = BoolGetDatum(false); - *isNull = false; - break; /* needn't look at any more rows */ - } - } - else - { - /* must be MULTIEXPR_SUBLINK */ - result = rowresult; - *isNull = rownull; - } - } - - if (!found) - { - /* - * deal with empty subplan result. result/isNull were previously - * initialized correctly for all sublink types except EXPR and - * MULTIEXPR; for those, return NULL. - */ - if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK) - { - result = (Datum) 0; - *isNull = true; - } - } - - MemoryContextSwitchTo(oldcontext); - - return result; -} - -/* ---------------------------------------------------------------- - * ExecInitSubPlan - * - * ---------------------------------------------------------------- - */ -bool -ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent) -{ - EState *sp_estate = CreateExecutorState(); - - sp_estate->es_range_table = node->rtable; - sp_estate->es_param_list_info = estate->es_param_list_info; - sp_estate->es_param_exec_vals = estate->es_param_exec_vals; - sp_estate->es_tupleTable = - ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10); - sp_estate->es_snapshot = estate->es_snapshot; - - node->needShutdown = false; - node->curTuple = NULL; - - if (!ExecInitNode(node->plan, sp_estate, parent)) - return false; - - node->needShutdown = true; /* now we need to shutdown the subplan */ - - /* - * If this plan is un-correlated or undirect correlated one and want - * to set params for parent plan then prepare parameters. - */ - if (node->setParam != NIL) - { - List *lst; - - foreach(lst, node->setParam) - { - ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]); - - prm->execPlan = node; - } - - /* - * Note that in the case of un-correlated subqueries we don't care - * about setting parent->chgParam here: indices take care about - * it, for others - it doesn't matter... - */ - } - - return true; -} - -/* ---------------------------------------------------------------- - * ExecSetParamPlan - * - * Executes an InitPlan subplan and sets its output parameters. - * - * This is called from ExecEvalParam() when the value of a PARAM_EXEC - * parameter is requested and the param's execPlan field is set (indicating - * that the param has not yet been evaluated). This allows lazy evaluation - * of initplans: we don't run the subplan until/unless we need its output. - * Note that this routine MUST clear the execPlan fields of the plan's - * output parameters after evaluating them! - * ---------------------------------------------------------------- - */ -void -ExecSetParamPlan(SubPlan *node, ExprContext *econtext) -{ - Plan *plan = node->plan; - SubLink *sublink = node->sublink; - MemoryContext oldcontext; - TupleTableSlot *slot; - List *lst; - bool found = false; - - /* - * We are probably in a short-lived expression-evaluation context. - * Switch to longer-lived per-query context. - */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - - if (sublink->subLinkType == ANY_SUBLINK || - sublink->subLinkType == ALL_SUBLINK) - elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported"); - - if (plan->chgParam != NULL) - ExecReScan(plan, NULL, NULL); - - for (slot = ExecProcNode(plan, NULL); - !TupIsNull(slot); - slot = ExecProcNode(plan, NULL)) - { - HeapTuple tup = slot->val; - TupleDesc tdesc = slot->ttc_tupleDescriptor; - int i = 1; - - if (sublink->subLinkType == EXISTS_SUBLINK) - { - ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]); - - prm->execPlan = NULL; - prm->value = BoolGetDatum(true); - prm->isnull = false; - found = true; - break; - } - - if (found && - (sublink->subLinkType == EXPR_SUBLINK || - sublink->subLinkType == MULTIEXPR_SUBLINK)) - elog(ERROR, "More than one tuple returned by a subselect used as an expression."); - - found = true; - - /* - * We need to copy the subplan's tuple in case any of the params - * are pass-by-ref type --- the pointers stored in the param - * structs will point at this copied tuple! node->curTuple keeps - * track of the copied tuple for eventual freeing. - */ - tup = heap_copytuple(tup); - if (node->curTuple) - heap_freetuple(node->curTuple); - node->curTuple = tup; - - foreach(lst, node->setParam) - { - ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]); - - prm->execPlan = NULL; - prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull)); - i++; - } - } - - if (!found) - { - if (sublink->subLinkType == EXISTS_SUBLINK) - { - ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]); - - prm->execPlan = NULL; - prm->value = BoolGetDatum(false); - prm->isnull = false; - } - else - { - foreach(lst, node->setParam) - { - ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]); - - prm->execPlan = NULL; - prm->value = (Datum) 0; - prm->isnull = true; - } - } - } - - if (plan->extParam == NULL) /* un-correlated ... */ - { - ExecEndNode(plan, NULL); - node->needShutdown = false; - } - - MemoryContextSwitchTo(oldcontext); -} - -/* ---------------------------------------------------------------- - * ExecEndSubPlan - * ---------------------------------------------------------------- - */ -void -ExecEndSubPlan(SubPlan *node) -{ - if (node->needShutdown) - { - ExecEndNode(node->plan, NULL); - node->needShutdown = false; - } - if (node->curTuple) - { - heap_freetuple(node->curTuple); - node->curTuple = NULL; - } -} - -void -ExecReScanSetParamPlan(SubPlan *node, Plan *parent) -{ - Plan *plan = node->plan; - List *lst; - - if (node->parParam != NULL) - elog(ERROR, "ExecReScanSetParamPlan: direct correlated subquery unsupported, yet"); - if (node->setParam == NULL) - elog(ERROR, "ExecReScanSetParamPlan: setParam list is NULL"); - if (plan->extParam == NULL) - elog(ERROR, "ExecReScanSetParamPlan: extParam list of plan is NULL"); - - /* - * Don't actual re-scan: ExecSetParamPlan does re-scan if - * node->plan->chgParam is not NULL... ExecReScan (plan, NULL, NULL); - */ - - /* - * Mark this subplan's output parameters as needing recalculation - */ - foreach(lst, node->setParam) - { - ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]); - - prm->execPlan = node; - } - - parent->chgParam = nconc(parent->chgParam, listCopy(node->setParam)); - -} diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c deleted file mode 100644 index 982dd0236ca..00000000000 --- a/src/backend/executor/nodeSubqueryscan.c +++ /dev/null @@ -1,262 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeSubqueryscan.c - * Support routines for scanning subqueries (subselects in rangetable). - * - * This is just enough different from sublinks (nodeSubplan.c) to mean that - * we need two sets of code. Ought to look at trying to unify the cases. - * - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.13 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * ExecSubqueryScan scans a subquery. - * ExecSubqueryNext retrieve next tuple in sequential order. - * ExecInitSubqueryScan creates and initializes a subqueryscan node. - * ExecEndSubqueryScan releases any storage allocated. - * ExecSubqueryReScan rescans the relation - * - */ -#include "postgres.h" - -#include "catalog/pg_type.h" -#include "executor/execdebug.h" -#include "executor/execdefs.h" -#include "executor/execdesc.h" -#include "executor/nodeSubqueryscan.h" -#include "parser/parsetree.h" -#include "tcop/pquery.h" - -static TupleTableSlot *SubqueryNext(SubqueryScan *node); - -/* ---------------------------------------------------------------- - * Scan Support - * ---------------------------------------------------------------- - */ -/* ---------------------------------------------------------------- - * SubqueryNext - * - * This is a workhorse for ExecSubqueryScan - * ---------------------------------------------------------------- - */ -static TupleTableSlot * -SubqueryNext(SubqueryScan *node) -{ - SubqueryScanState *subquerystate; - EState *estate; - ScanDirection direction; - TupleTableSlot *slot; - - /* - * get information from the estate and scan state - */ - estate = node->scan.plan.state; - subquerystate = (SubqueryScanState *) node->scan.scanstate; - direction = estate->es_direction; - - /* - * We need not support EvalPlanQual here, since we are not scanning a - * real relation. - */ - - /* - * get the next tuple from the sub-query - */ - subquerystate->sss_SubEState->es_direction = direction; - - slot = ExecProcNode(node->subplan, (Plan *) node); - - subquerystate->csstate.css_ScanTupleSlot = slot; - - return slot; -} - -/* ---------------------------------------------------------------- - * ExecSubqueryScan(node) - * - * Scans the subquery sequentially and returns the next qualifying - * tuple. - * It calls the ExecScan() routine and passes it the access method - * which retrieve tuples sequentially. - * - */ - -TupleTableSlot * -ExecSubqueryScan(SubqueryScan *node) -{ - /* - * use SubqueryNext as access method - */ - return ExecScan(&node->scan, (ExecScanAccessMtd) SubqueryNext); -} - -/* ---------------------------------------------------------------- - * ExecInitSubqueryScan - * ---------------------------------------------------------------- - */ -bool -ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent) -{ - SubqueryScanState *subquerystate; - RangeTblEntry *rte; - EState *sp_estate; - - /* - * SubqueryScan should not have any "normal" children. - */ - Assert(outerPlan((Plan *) node) == NULL); - Assert(innerPlan((Plan *) node) == NULL); - - /* - * assign the node's execution state - */ - node->scan.plan.state = estate; - - /* - * create new SubqueryScanState for node - */ - subquerystate = makeNode(SubqueryScanState); - node->scan.scanstate = (CommonScanState *) subquerystate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &subquerystate->csstate.cstate); - -#define SUBQUERYSCAN_NSLOTS 1 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &subquerystate->csstate.cstate); - - /* - * initialize subquery - * - * This should agree with ExecInitSubPlan - */ - rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); - Assert(rte->rtekind == RTE_SUBQUERY); - - sp_estate = CreateExecutorState(); - subquerystate->sss_SubEState = sp_estate; - - sp_estate->es_range_table = rte->subquery->rtable; - sp_estate->es_param_list_info = estate->es_param_list_info; - sp_estate->es_param_exec_vals = estate->es_param_exec_vals; - sp_estate->es_tupleTable = - ExecCreateTupleTable(ExecCountSlotsNode(node->subplan) + 10); - sp_estate->es_snapshot = estate->es_snapshot; - - if (!ExecInitNode(node->subplan, sp_estate, (Plan *) node)) - return false; - - subquerystate->csstate.css_ScanTupleSlot = NULL; - subquerystate->csstate.cstate.cs_TupFromTlist = false; - - /* - * initialize tuple type - */ - ExecAssignResultTypeFromTL((Plan *) node, &subquerystate->csstate.cstate); - ExecAssignProjectionInfo((Plan *) node, &subquerystate->csstate.cstate); - - return TRUE; -} - -int -ExecCountSlotsSubqueryScan(SubqueryScan *node) -{ - /* - * The subplan has its own tuple table and must not be counted here! - */ - return ExecCountSlotsNode(outerPlan(node)) + - ExecCountSlotsNode(innerPlan(node)) + - SUBQUERYSCAN_NSLOTS; -} - -/* ---------------------------------------------------------------- - * ExecEndSubqueryScan - * - * frees any storage allocated through C routines. - * ---------------------------------------------------------------- - */ -void -ExecEndSubqueryScan(SubqueryScan *node) -{ - SubqueryScanState *subquerystate; - - /* - * get information from node - */ - subquerystate = (SubqueryScanState *) node->scan.scanstate; - - /* - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(subquerystate) 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(&subquerystate->csstate.cstate); - ExecFreeExprContext(&subquerystate->csstate.cstate); - - /* - * close down subquery - */ - ExecEndNode(node->subplan, (Plan *) node); - - /* - * clean up subquery's tuple table - */ - subquerystate->csstate.css_ScanTupleSlot = NULL; - ExecDropTupleTable(subquerystate->sss_SubEState->es_tupleTable, true); - - /* XXX we seem to be leaking the sub-EState... */ - - /* - * clean out the upper tuple table - */ - ExecClearTuple(subquerystate->csstate.cstate.cs_ResultTupleSlot); -} - -/* ---------------------------------------------------------------- - * ExecSubqueryReScan - * - * Rescans the relation. - * ---------------------------------------------------------------- - */ -void -ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent) -{ - SubqueryScanState *subquerystate; - EState *estate; - - subquerystate = (SubqueryScanState *) node->scan.scanstate; - estate = node->scan.plan.state; - - /* - * ExecReScan doesn't know about my subplan, so I have to do - * changed-parameter signaling myself. - */ - if (node->scan.plan.chgParam != NULL) - SetChangedParamList(node->subplan, node->scan.plan.chgParam); - - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (node->subplan->chgParam == NULL) - ExecReScan(node->subplan, NULL, (Plan *) node); - - subquerystate->csstate.css_ScanTupleSlot = NULL; -} diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c deleted file mode 100644 index 300735fff06..00000000000 --- a/src/backend/executor/nodeTidscan.c +++ /dev/null @@ -1,496 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeTidscan.c - * Routines to support direct tid scans of relations - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.25 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -/* - * INTERFACE ROUTINES - * - * ExecTidScan scans a relation using tids - * ExecInitTidScan creates and initializes state info. - * ExecTidReScan rescans the tid relation. - * ExecEndTidScan releases all storage. - * ExecTidMarkPos marks scan position. - * - */ -#include "postgres.h" - -#include "executor/execdebug.h" -#include "executor/nodeTidscan.h" -#include "access/heapam.h" -#include "parser/parsetree.h" - -static int TidListCreate(List *, ExprContext *, ItemPointerData[]); -static TupleTableSlot *TidNext(TidScan *node); - -static int -TidListCreate(List *evalList, ExprContext *econtext, ItemPointerData tidList[]) -{ - List *lst; - ItemPointer itemptr; - bool isNull; - int numTids = 0; - - foreach(lst, evalList) - { - itemptr = (ItemPointer) - DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst), - econtext, - &isNull, - NULL)); - if (!isNull && itemptr && ItemPointerIsValid(itemptr)) - { - tidList[numTids] = *itemptr; - numTids++; - } - } - return numTids; -} - -/* ---------------------------------------------------------------- - * TidNext - * - * Retrieve a tuple from the TidScan node's currentRelation - * using the tids in the TidScanState information. - * - * ---------------------------------------------------------------- - */ -static TupleTableSlot * -TidNext(TidScan *node) -{ - EState *estate; - CommonScanState *scanstate; - TidScanState *tidstate; - ScanDirection direction; - Snapshot snapshot; - Relation heapRelation; - HeapTuple tuple; - TupleTableSlot *slot; - Buffer buffer = InvalidBuffer; - int numTids; - - bool bBackward; - int tidNumber; - ItemPointerData *tidList; - - /* - * extract necessary information from tid scan node - */ - estate = node->scan.plan.state; - direction = estate->es_direction; - snapshot = estate->es_snapshot; - scanstate = node->scan.scanstate; - tidstate = node->tidstate; - heapRelation = scanstate->css_currentRelation; - numTids = tidstate->tss_NumTids; - tidList = tidstate->tss_TidList; - slot = scanstate->css_ScanTupleSlot; - - /* - * Check if we are evaluating PlanQual for tuple of this relation. - * Additional checking is not good, but no other way for now. We could - * introduce new nodes for this case and handle TidScan --> NewNode - * switching in Init/ReScan plan... - */ - if (estate->es_evTuple != NULL && - estate->es_evTuple[node->scan.scanrelid - 1] != NULL) - { - ExecClearTuple(slot); - if (estate->es_evTupleNull[node->scan.scanrelid - 1]) - return slot; /* return empty slot */ - - /* - * XXX shouldn't we check here to make sure tuple matches TID list? - * In runtime-key case this is not certain, is it? - */ - - ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1], - slot, InvalidBuffer, false); - - /* Flag for the next call that no more tuples */ - estate->es_evTupleNull[node->scan.scanrelid - 1] = true; - return (slot); - } - - tuple = &(tidstate->tss_htup); - - /* - * ok, now that we have what we need, fetch an tid tuple. if scanning - * this tid succeeded then return the appropriate heap tuple.. else - * return NULL. - */ - bBackward = ScanDirectionIsBackward(direction); - if (bBackward) - { - tidNumber = numTids - tidstate->tss_TidPtr - 1; - if (tidNumber < 0) - { - tidNumber = 0; - tidstate->tss_TidPtr = numTids - 1; - } - } - else - { - if ((tidNumber = tidstate->tss_TidPtr) < 0) - { - tidNumber = 0; - tidstate->tss_TidPtr = 0; - } - } - while (tidNumber < numTids) - { - bool slot_is_valid = false; - - tuple->t_self = tidList[tidstate->tss_TidPtr]; - if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL)) - { - bool prev_matches = false; - int prev_tid; - - /* - * 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 */ - - /* - * At this point we have an extra pin on the buffer, because - * ExecStoreTuple incremented the pin count. Drop our local - * pin. - */ - ReleaseBuffer(buffer); - - /* - * We must check to see if the current tuple would have been - * matched by an earlier tid, so we don't double report it. We - * do this by passing the tuple through ExecQual and look for - * failure with all previous qualifications. - */ - for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr; - prev_tid++) - { - if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self)) - { - prev_matches = true; - break; - } - } - if (!prev_matches) - slot_is_valid = true; - else - ExecClearTuple(slot); - } - tidNumber++; - if (bBackward) - tidstate->tss_TidPtr--; - else - tidstate->tss_TidPtr++; - if (slot_is_valid) - return slot; - } - - /* - * if we get here it means the tid scan failed so we are at the end of - * the scan.. - */ - return ExecClearTuple(slot); -} - -/* ---------------------------------------------------------------- - * ExecTidScan(node) - * - * Scans the relation using tids 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 tids. - * - * 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. - * -- tidPtr points to the first tid. - * -- state variable ruleFlag = nil. - * ---------------------------------------------------------------- - */ -TupleTableSlot * -ExecTidScan(TidScan *node) -{ - /* - * use TidNext as access method - */ - return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext); -} - -/* ---------------------------------------------------------------- - * ExecTidReScan(node) - * ---------------------------------------------------------------- - */ -void -ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent) -{ - EState *estate; - TidScanState *tidstate; - ItemPointerData *tidList; - - estate = node->scan.plan.state; - tidstate = node->tidstate; - tidList = tidstate->tss_TidList; - - /* If we are being passed an outer tuple, save it for runtime key calc */ - if (exprCtxt != NULL) - node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple = - exprCtxt->ecxt_outertuple; - - /* do runtime calc of target TIDs, if needed */ - if (node->needRescan) - tidstate->tss_NumTids = - TidListCreate(node->tideval, - node->scan.scanstate->cstate.cs_ExprContext, - tidList); - - /* If this is re-scanning of PlanQual ... */ - if (estate->es_evTuple != NULL && - estate->es_evTuple[node->scan.scanrelid - 1] != NULL) - { - estate->es_evTupleNull[node->scan.scanrelid - 1] = false; - return; - } - - tidstate->tss_TidPtr = -1; - - /* - * perhaps return something meaningful - */ - return; -} - -/* ---------------------------------------------------------------- - * ExecEndTidScan - * - * Releases any storage allocated through C routines. - * Returns nothing. - * ---------------------------------------------------------------- - */ -void -ExecEndTidScan(TidScan *node) -{ - CommonScanState *scanstate; - TidScanState *tidstate; - - /* - * extract information from the node - */ - scanstate = node->scan.scanstate; - tidstate = node->tidstate; - if (tidstate && tidstate->tss_TidList) - pfree(tidstate->tss_TidList); - - /* - * 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); - ExecFreeExprContext(&scanstate->cstate); - - /* - * close the heap relation. - * - * Currently, we do not release the AccessShareLock acquired by - * ExecInitTidScan. This lock should be held till end of transaction. - * (There is a faction that considers this too much locking, however.) - */ - heap_close(scanstate->css_currentRelation, NoLock); - - /* - * clear out tuple table slots - */ - ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot); - ExecClearTuple(scanstate->css_ScanTupleSlot); -} - -/* ---------------------------------------------------------------- - * ExecTidMarkPos - * - * Marks scan position by marking the current tid. - * Returns nothing. - * ---------------------------------------------------------------- - */ -void -ExecTidMarkPos(TidScan *node) -{ - TidScanState *tidstate; - - tidstate = node->tidstate; - tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr; -} - -#ifdef NOT_USED -/* ---------------------------------------------------------------- - * ExecTidRestrPos - * - * Restores scan position by restoring the current tid. - * Returns nothing. - * - * XXX Assumes previously marked scan position belongs to current tid - * ---------------------------------------------------------------- - */ -void -ExecTidRestrPos(TidScan *node) -{ - TidScanState *tidstate; - - tidstate = node->tidstate; - tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr; -} -#endif - -/* ---------------------------------------------------------------- - * ExecInitTidScan - * - * Initializes the tid scan's state information, creates - * scan keys, and opens the base and tid relations. - * - * Parameters: - * node: TidNode node produced by the planner. - * estate: the execution state initialized in InitPlan. - * ---------------------------------------------------------------- - */ -bool -ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) -{ - TidScanState *tidstate; - CommonScanState *scanstate; - ItemPointerData *tidList; - int numTids; - int tidPtr; - List *rangeTable; - RangeTblEntry *rtentry; - Oid relid; - Oid reloid; - Relation currentRelation; - List *execParam = NIL; - - /* - * assign execution state to node - */ - node->scan.plan.state = estate; - - /* - * Part 1) initialize scan state - * - * create new CommonScanState for node - */ - scanstate = makeNode(CommonScanState); - node->scan.scanstate = scanstate; - - /* - * Miscellaneous initialization - * - * create expression context for node - */ - ExecAssignExprContext(estate, &scanstate->cstate); - -#define TIDSCAN_NSLOTS 2 - - /* - * tuple table initialization - */ - ExecInitResultTupleSlot(estate, &scanstate->cstate); - ExecInitScanTupleSlot(estate, scanstate); - - /* - * initialize projection info. result type comes from scan desc - * below.. - */ - ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate); - - /* - * Part 2) initialize tid scan state - * - * create new TidScanState for node - */ - tidstate = makeNode(TidScanState); - node->tidstate = tidstate; - - /* - * get the tid node information - */ - tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData)); - numTids = 0; - if (!node->needRescan) - numTids = TidListCreate(node->tideval, - scanstate->cstate.cs_ExprContext, - tidList); - tidPtr = -1; - - CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext); - - tidstate->tss_NumTids = numTids; - tidstate->tss_TidPtr = tidPtr; - tidstate->tss_TidList = tidList; - - /* - * get the range table and direction information from the execution - * state (these are needed to open the relations). - */ - rangeTable = estate->es_range_table; - - /* - * open the base relation - * - * We acquire AccessShareLock for the duration of the scan. - */ - relid = node->scan.scanrelid; - rtentry = rt_fetch(relid, rangeTable); - reloid = rtentry->relid; - - currentRelation = heap_open(reloid, AccessShareLock); - - scanstate->css_currentRelation = currentRelation; - scanstate->css_currentScanDesc = NULL; /* no heap scan here */ - - /* - * get the scan type from the relation descriptor. - */ - ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false); - ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate); - - /* - * if there are some PARAM_EXEC in skankeys then force tid rescan on - * first scan. - */ - ((Plan *) node)->chgParam = execParam; - - /* - * all done. - */ - return TRUE; -} - -int -ExecCountSlotsTidScan(TidScan *node) -{ - return ExecCountSlotsNode(outerPlan((Plan *) node)) + - ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS; -} diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c deleted file mode 100644 index b71403de0b8..00000000000 --- a/src/backend/executor/nodeUnique.c +++ /dev/null @@ -1,237 +0,0 @@ -/*------------------------------------------------------------------------- - * - * nodeUnique.c - * Routines to handle unique'ing of queries where appropriate - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.34 2002/06/20 20:29:28 momjian 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 "postgres.h" - -#include "access/heapam.h" -#include "executor/executor.h" -#include "executor/nodeGroup.h" -#include "executor/nodeUnique.h" - -/* ---------------------------------------------------------------- - * ExecUnique - * - * This is a very simple node which filters out duplicate - * tuples from a stream of sorted tuples from a subplan. - * ---------------------------------------------------------------- - */ -TupleTableSlot * /* return: a tuple or NULL */ -ExecUnique(Unique *node) -{ - UniqueState *uniquestate; - TupleTableSlot *resultTupleSlot; - TupleTableSlot *slot; - Plan *outerPlan; - TupleDesc tupDesc; - - /* - * get information from the node - */ - uniquestate = node->uniquestate; - outerPlan = outerPlan((Plan *) node); - resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot; - tupDesc = ExecGetResultType(&uniquestate->cstate); - - /* - * 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; - - /* - * Always return the first tuple from the subplan. - */ - if (uniquestate->priorTuple == NULL) - break; - - /* - * Else test if the new tuple and the previously returned tuple - * match. If so then we loop back and fetch another new tuple - * from the subplan. - */ - if (!execTuplesMatch(slot->val, uniquestate->priorTuple, - tupDesc, - node->numCols, node->uniqColIdx, - uniquestate->eqfunctions, - uniquestate->tempContext)) - break; - } - - /* - * We have a new tuple different from the previous saved tuple (if - * any). Save it and return it. We must copy it because the source - * subplan won't guarantee that this source tuple is still accessible - * after fetching the next source tuple. - * - * Note that we manage the copy ourselves. We can't rely on the result - * tuple slot to maintain the tuple reference because our caller may - * replace the slot contents with a different tuple (see junk filter - * handling in execMain.c). We assume that the caller will no longer - * be interested in the current tuple after he next calls us. - */ - if (uniquestate->priorTuple != NULL) - heap_freetuple(uniquestate->priorTuple); - uniquestate->priorTuple = heap_copytuple(slot->val); - - ExecStoreTuple(uniquestate->priorTuple, - resultTupleSlot, - InvalidBuffer, - false); /* tuple does not belong to slot */ - - 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; - - /* - * assign execution state to node - */ - node->plan.state = estate; - - /* - * create new UniqueState for node - */ - uniquestate = makeNode(UniqueState); - node->uniquestate = uniquestate; - uniquestate->priorTuple = NULL; - - /* - * Miscellaneous initialization - * - * Unique nodes have no ExprContext initialization because they never - * call ExecQual or ExecProject. But they do need a per-tuple memory - * context anyway for calling execTuplesMatch. - */ - uniquestate->tempContext = - AllocSetContextCreate(CurrentMemoryContext, - "Unique", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - -#define UNIQUE_NSLOTS 1 - - /* - * Tuple table initialization - */ - ExecInitResultTupleSlot(estate, &uniquestate->cstate); - - /* - * 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->cstate); - uniquestate->cstate.cs_ProjInfo = NULL; - - /* - * Precompute fmgr lookup data for inner loop - */ - uniquestate->eqfunctions = - execTuplesMatchPrepare(ExecGetResultType(&uniquestate->cstate), - node->numCols, - node->uniqColIdx); - - 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 = node->uniquestate; - - ExecEndNode(outerPlan((Plan *) node), (Plan *) node); - - MemoryContextDelete(uniquestate->tempContext); - - /* clean up tuple table */ - ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot); - if (uniquestate->priorTuple != NULL) - { - heap_freetuple(uniquestate->priorTuple); - uniquestate->priorTuple = NULL; - } -} - - -void -ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent) -{ - UniqueState *uniquestate = node->uniquestate; - - ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot); - if (uniquestate->priorTuple != NULL) - { - heap_freetuple(uniquestate->priorTuple); - uniquestate->priorTuple = NULL; - } - - /* - * if chgParam of subnode is not null then plan will be re-scanned by - * first ExecProcNode. - */ - if (((Plan *) node)->lefttree->chgParam == NULL) - ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node); - -} diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c deleted file mode 100644 index 60b39768d53..00000000000 --- a/src/backend/executor/spi.c +++ /dev/null @@ -1,1424 +0,0 @@ -/*------------------------------------------------------------------------- - * - * spi.c - * Server Programming Interface - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.71 2002/06/20 20:29:28 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/printtup.h" -#include "catalog/heap.h" -#include "commands/portalcmds.h" -#include "executor/spi_priv.h" -#include "tcop/tcopprot.h" -#include "utils/lsyscache.h" - - -uint32 SPI_processed = 0; -Oid SPI_lastoid = InvalidOid; -SPITupleTable *SPI_tuptable = NULL; -int SPI_result; - -static _SPI_connection *_SPI_stack = NULL; -static _SPI_connection *_SPI_current = NULL; -static int _SPI_connected = -1; -static int _SPI_curid = -1; - -static int _SPI_execute(char *src, int tcount, _SPI_plan *plan); -static int _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount); - -static int _SPI_execute_plan(_SPI_plan *plan, - Datum *Values, char *Nulls, int tcount); - -static void _SPI_cursor_operation(Portal portal, bool forward, int count, - CommandDest dest); - -static _SPI_plan *_SPI_copy_plan(_SPI_plan *plan, int location); - -static int _SPI_begin_call(bool execmem); -static int _SPI_end_call(bool procmem); -static MemoryContext _SPI_execmem(void); -static MemoryContext _SPI_procmem(void); -static bool _SPI_checktuples(void); - - -/* =================== interface functions =================== */ - -int -SPI_connect(void) -{ - _SPI_connection *new_SPI_stack; - - /* - * When procedure called by Executor _SPI_curid expected to be equal - * to _SPI_connected - */ - if (_SPI_curid != _SPI_connected) - return SPI_ERROR_CONNECT; - - if (_SPI_stack == NULL) - { - if (_SPI_connected != -1) - elog(FATAL, "SPI_connect: no connection(s) expected"); - new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection)); - } - else - { - if (_SPI_connected <= -1) - elog(FATAL, "SPI_connect: some connection(s) expected"); - new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack, - (_SPI_connected + 2) * sizeof(_SPI_connection)); - } - - if (new_SPI_stack == NULL) - elog(ERROR, "Memory exhausted in SPI_connect"); - - /* - * We' returning to procedure where _SPI_curid == _SPI_connected - 1 - */ - _SPI_stack = new_SPI_stack; - _SPI_connected++; - - _SPI_current = &(_SPI_stack[_SPI_connected]); - _SPI_current->qtlist = NULL; - _SPI_current->processed = 0; - _SPI_current->tuptable = NULL; - - /* Create memory contexts for this procedure */ - _SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext, - "SPI Proc", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - _SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext, - "SPI Exec", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - /* ... and switch to procedure's context */ - _SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt); - - return SPI_OK_CONNECT; -} - -int -SPI_finish(void) -{ - int res; - - res = _SPI_begin_call(false); /* live in procedure memory */ - if (res < 0) - return res; - - /* Restore memory context as it was before procedure call */ - MemoryContextSwitchTo(_SPI_current->savedcxt); - - /* Release memory used in procedure call */ - MemoryContextDelete(_SPI_current->execCxt); - MemoryContextDelete(_SPI_current->procCxt); - - /* - * After _SPI_begin_call _SPI_connected == _SPI_curid. Now we are - * closing connection to SPI and returning to upper Executor and so - * _SPI_connected must be equal to _SPI_curid. - */ - _SPI_connected--; - _SPI_curid--; - if (_SPI_connected == -1) - { - free(_SPI_stack); - _SPI_stack = NULL; - _SPI_current = NULL; - } - else - { - _SPI_connection *new_SPI_stack; - - new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack, - (_SPI_connected + 1) * sizeof(_SPI_connection)); - /* This could only fail with a pretty stupid malloc package ... */ - if (new_SPI_stack == NULL) - elog(ERROR, "Memory exhausted in SPI_finish"); - _SPI_stack = new_SPI_stack; - _SPI_current = &(_SPI_stack[_SPI_connected]); - } - - return SPI_OK_FINISH; - -} - -/* - * Clean up SPI state at transaction commit or abort (we don't care which). - */ -void -AtEOXact_SPI(void) -{ - /* - * Note that memory contexts belonging to SPI stack entries will be - * freed automatically, so we can ignore them here. We just need to - * restore our static variables to initial state. - */ - if (_SPI_stack != NULL) /* there was abort */ - free(_SPI_stack); - _SPI_current = _SPI_stack = NULL; - _SPI_connected = _SPI_curid = -1; - SPI_processed = 0; - SPI_lastoid = InvalidOid; - SPI_tuptable = NULL; -} - -void -SPI_push(void) -{ - _SPI_curid++; -} - -void -SPI_pop(void) -{ - _SPI_curid--; -} - -int -SPI_exec(char *src, int tcount) -{ - int res; - - if (src == NULL || tcount < 0) - return SPI_ERROR_ARGUMENT; - - res = _SPI_begin_call(true); - if (res < 0) - return res; - - res = _SPI_execute(src, tcount, NULL); - - _SPI_end_call(true); - return res; -} - -int -SPI_execp(void *plan, Datum *Values, char *Nulls, int tcount) -{ - int res; - - if (plan == NULL || tcount < 0) - return SPI_ERROR_ARGUMENT; - - if (((_SPI_plan *) plan)->nargs > 0 && Values == NULL) - return SPI_ERROR_PARAM; - - res = _SPI_begin_call(true); - if (res < 0) - return res; - - /* copy plan to current (executor) context */ - plan = (void *) _SPI_copy_plan(plan, _SPI_CPLAN_CURCXT); - - res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, tcount); - - _SPI_end_call(true); - return res; -} - -void * -SPI_prepare(char *src, int nargs, Oid *argtypes) -{ - _SPI_plan *plan; - - if (src == NULL || nargs < 0 || (nargs > 0 && argtypes == NULL)) - { - SPI_result = SPI_ERROR_ARGUMENT; - return NULL; - } - - SPI_result = _SPI_begin_call(true); - if (SPI_result < 0) - return NULL; - - plan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); /* Executor context */ - plan->argtypes = argtypes; - plan->nargs = nargs; - - SPI_result = _SPI_execute(src, 0, plan); - - if (SPI_result >= 0) /* copy plan to procedure context */ - plan = _SPI_copy_plan(plan, _SPI_CPLAN_PROCXT); - else - plan = NULL; - - _SPI_end_call(true); - - return (void *) plan; - -} - -void * -SPI_saveplan(void *plan) -{ - _SPI_plan *newplan; - - if (plan == NULL) - { - SPI_result = SPI_ERROR_ARGUMENT; - return NULL; - } - - SPI_result = _SPI_begin_call(false); /* don't change context */ - if (SPI_result < 0) - return NULL; - - newplan = _SPI_copy_plan((_SPI_plan *) plan, _SPI_CPLAN_TOPCXT); - - _SPI_curid--; - SPI_result = 0; - - return (void *) newplan; - -} - -int -SPI_freeplan(void *plan) -{ - _SPI_plan *spiplan = (_SPI_plan *) plan; - - if (plan == NULL) - return SPI_ERROR_ARGUMENT; - - MemoryContextDelete(spiplan->plancxt); - return 0; -} - -HeapTuple -SPI_copytuple(HeapTuple tuple) -{ - MemoryContext oldcxt = NULL; - HeapTuple ctuple; - - if (tuple == NULL) - { - SPI_result = SPI_ERROR_ARGUMENT; - return NULL; - } - - if (_SPI_curid + 1 == _SPI_connected) /* connected */ - { - if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) - elog(FATAL, "SPI: stack corrupted"); - oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); - } - - ctuple = heap_copytuple(tuple); - - if (oldcxt) - MemoryContextSwitchTo(oldcxt); - - return ctuple; -} - -TupleDesc -SPI_copytupledesc(TupleDesc tupdesc) -{ - MemoryContext oldcxt = NULL; - TupleDesc ctupdesc; - - if (tupdesc == NULL) - { - SPI_result = SPI_ERROR_ARGUMENT; - return NULL; - } - - if (_SPI_curid + 1 == _SPI_connected) /* connected */ - { - if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) - elog(FATAL, "SPI: stack corrupted"); - oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); - } - - ctupdesc = CreateTupleDescCopy(tupdesc); - - if (oldcxt) - MemoryContextSwitchTo(oldcxt); - - return ctupdesc; -} - -TupleTableSlot * -SPI_copytupleintoslot(HeapTuple tuple, TupleDesc tupdesc) -{ - MemoryContext oldcxt = NULL; - TupleTableSlot *cslot; - HeapTuple ctuple; - TupleDesc ctupdesc; - - if (tuple == NULL || tupdesc == NULL) - { - SPI_result = SPI_ERROR_ARGUMENT; - return NULL; - } - - if (_SPI_curid + 1 == _SPI_connected) /* connected */ - { - if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) - elog(FATAL, "SPI: stack corrupted"); - oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); - } - - ctuple = heap_copytuple(tuple); - ctupdesc = CreateTupleDescCopy(tupdesc); - - cslot = MakeTupleTableSlot(); - ExecSetSlotDescriptor(cslot, ctupdesc, true); - cslot = ExecStoreTuple(ctuple, cslot, InvalidBuffer, true); - - if (oldcxt) - MemoryContextSwitchTo(oldcxt); - - return cslot; -} - -HeapTuple -SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, - Datum *Values, char *Nulls) -{ - MemoryContext oldcxt = NULL; - HeapTuple mtuple; - int numberOfAttributes; - uint8 infomask; - Datum *v; - char *n; - bool isnull; - int i; - - if (rel == NULL || tuple == NULL || natts <= 0 || attnum == NULL || Values == NULL) - { - SPI_result = SPI_ERROR_ARGUMENT; - return NULL; - } - - if (_SPI_curid + 1 == _SPI_connected) /* connected */ - { - if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) - elog(FATAL, "SPI: stack corrupted"); - oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); - } - SPI_result = 0; - numberOfAttributes = rel->rd_att->natts; - v = (Datum *) palloc(numberOfAttributes * sizeof(Datum)); - n = (char *) palloc(numberOfAttributes * sizeof(char)); - - /* fetch old values and nulls */ - for (i = 0; i < numberOfAttributes; i++) - { - v[i] = heap_getattr(tuple, i + 1, rel->rd_att, &isnull); - n[i] = (isnull) ? 'n' : ' '; - } - - /* replace values and nulls */ - for (i = 0; i < natts; i++) - { - if (attnum[i] <= 0 || attnum[i] > numberOfAttributes) - break; - v[attnum[i] - 1] = Values[i]; - n[attnum[i] - 1] = (Nulls && Nulls[i] == 'n') ? 'n' : ' '; - } - - if (i == natts) /* no errors in *attnum */ - { - mtuple = heap_formtuple(rel->rd_att, v, n); - infomask = mtuple->t_data->t_infomask; - memmove(&(mtuple->t_data->t_oid), &(tuple->t_data->t_oid), - ((char *) &(tuple->t_data->t_hoff) - - (char *) &(tuple->t_data->t_oid))); - mtuple->t_data->t_infomask = infomask; - mtuple->t_data->t_natts = numberOfAttributes; - } - else - { - mtuple = NULL; - SPI_result = SPI_ERROR_NOATTRIBUTE; - } - - pfree(v); - pfree(n); - - if (oldcxt) - MemoryContextSwitchTo(oldcxt); - - return mtuple; -} - -int -SPI_fnumber(TupleDesc tupdesc, char *fname) -{ - int res; - Form_pg_attribute sysatt; - - for (res = 0; res < tupdesc->natts; res++) - { - if (namestrcmp(&tupdesc->attrs[res]->attname, fname) == 0) - return res + 1; - } - - sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ ); - if (sysatt != NULL) - return sysatt->attnum; - - /* SPI_ERROR_NOATTRIBUTE is different from all sys column numbers */ - return SPI_ERROR_NOATTRIBUTE; -} - -char * -SPI_fname(TupleDesc tupdesc, int fnumber) -{ - Form_pg_attribute att; - - SPI_result = 0; - - if (fnumber > tupdesc->natts || fnumber == 0 || - fnumber <= FirstLowInvalidHeapAttributeNumber) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return NULL; - } - - if (fnumber > 0) - att = tupdesc->attrs[fnumber - 1]; - else - att = SystemAttributeDefinition(fnumber, true); - - return pstrdup(NameStr(att->attname)); -} - -char * -SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) -{ - Datum origval, - val, - result; - bool isnull; - Oid typoid, - foutoid, - typelem; - int32 typmod; - bool typisvarlena; - - SPI_result = 0; - - if (fnumber > tuple->t_data->t_natts || fnumber == 0 || - fnumber <= FirstLowInvalidHeapAttributeNumber) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return NULL; - } - - origval = heap_getattr(tuple, fnumber, tupdesc, &isnull); - if (isnull) - return NULL; - - if (fnumber > 0) - { - typoid = tupdesc->attrs[fnumber - 1]->atttypid; - typmod = tupdesc->attrs[fnumber - 1]->atttypmod; - } - else - { - typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; - typmod = -1; - } - - if (!getTypeOutputInfo(typoid, &foutoid, &typelem, &typisvarlena)) - { - SPI_result = SPI_ERROR_NOOUTFUNC; - return NULL; - } - - /* - * If we have a toasted datum, forcibly detoast it here to avoid - * memory leakage inside the type's output routine. - */ - if (typisvarlena) - val = PointerGetDatum(PG_DETOAST_DATUM(origval)); - else - val = origval; - - result = OidFunctionCall3(foutoid, - val, - ObjectIdGetDatum(typelem), - Int32GetDatum(typmod)); - - /* Clean up detoasted copy, if any */ - if (val != origval) - pfree(DatumGetPointer(val)); - - return DatumGetCString(result); -} - -Datum -SPI_getbinval(HeapTuple tuple, TupleDesc tupdesc, int fnumber, bool *isnull) -{ - SPI_result = 0; - - if (fnumber > tuple->t_data->t_natts || fnumber == 0 || - fnumber <= FirstLowInvalidHeapAttributeNumber) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - *isnull = true; - return (Datum) NULL; - } - - return heap_getattr(tuple, fnumber, tupdesc, isnull); -} - -char * -SPI_gettype(TupleDesc tupdesc, int fnumber) -{ - Oid typoid; - HeapTuple typeTuple; - char *result; - - SPI_result = 0; - - if (fnumber > tupdesc->natts || fnumber == 0 || - fnumber <= FirstLowInvalidHeapAttributeNumber) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return NULL; - } - - if (fnumber > 0) - typoid = tupdesc->attrs[fnumber - 1]->atttypid; - else - typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; - - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typoid), - 0, 0, 0); - - if (!HeapTupleIsValid(typeTuple)) - { - SPI_result = SPI_ERROR_TYPUNKNOWN; - return NULL; - } - - result = pstrdup(NameStr(((Form_pg_type) GETSTRUCT(typeTuple))->typname)); - ReleaseSysCache(typeTuple); - return result; -} - -Oid -SPI_gettypeid(TupleDesc tupdesc, int fnumber) -{ - SPI_result = 0; - - if (fnumber > tupdesc->natts || fnumber == 0 || - fnumber <= FirstLowInvalidHeapAttributeNumber) - { - SPI_result = SPI_ERROR_NOATTRIBUTE; - return InvalidOid; - } - - if (fnumber > 0) - return tupdesc->attrs[fnumber - 1]->atttypid; - else - return (SystemAttributeDefinition(fnumber, true))->atttypid; -} - -char * -SPI_getrelname(Relation rel) -{ - return pstrdup(RelationGetRelationName(rel)); -} - -void * -SPI_palloc(Size size) -{ - MemoryContext oldcxt = NULL; - void *pointer; - - if (_SPI_curid + 1 == _SPI_connected) /* connected */ - { - if (_SPI_current != &(_SPI_stack[_SPI_curid + 1])) - elog(FATAL, "SPI: stack corrupted"); - oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt); - } - - pointer = palloc(size); - - if (oldcxt) - MemoryContextSwitchTo(oldcxt); - - return pointer; -} - -void * -SPI_repalloc(void *pointer, Size size) -{ - /* No longer need to worry which context chunk was in... */ - return repalloc(pointer, size); -} - -void -SPI_pfree(void *pointer) -{ - /* No longer need to worry which context chunk was in... */ - pfree(pointer); -} - -void -SPI_freetuple(HeapTuple tuple) -{ - /* No longer need to worry which context tuple was in... */ - heap_freetuple(tuple); -} - -void -SPI_freetuptable(SPITupleTable *tuptable) -{ - if (tuptable != NULL) - MemoryContextDelete(tuptable->tuptabcxt); -} - - - -/* - * SPI_cursor_open() - * - * Open a prepared SPI plan as a portal - */ -Portal -SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls) -{ - static int unnamed_portal_count = 0; - - _SPI_plan *spiplan = (_SPI_plan *) plan; - List *qtlist = spiplan->qtlist; - List *ptlist = spiplan->ptlist; - Query *queryTree; - Plan *planTree; - QueryDesc *queryDesc; - EState *eState; - TupleDesc attinfo; - MemoryContext oldcontext; - Portal portal; - char portalname[64]; - int k; - - /* Ensure that the plan contains only one regular SELECT query */ - if (length(ptlist) != 1) - elog(ERROR, "cannot open multi-query plan as cursor"); - queryTree = (Query *) lfirst(qtlist); - planTree = (Plan *) lfirst(ptlist); - - if (queryTree->commandType != CMD_SELECT) - elog(ERROR, "plan in SPI_cursor_open() is not a SELECT"); - if (queryTree->isPortal) - elog(ERROR, "plan in SPI_cursor_open() must NOT be a DECLARE already"); - else if (queryTree->into != NULL) - elog(ERROR, "plan in SPI_cursor_open() must NOT be a SELECT INTO"); - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement(); - - /* Reset SPI result */ - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_current->processed = 0; - _SPI_current->tuptable = NULL; - - if (name == NULL) - { - /* Make up a portal name if none given */ - for (;;) - { - unnamed_portal_count++; - if (unnamed_portal_count < 0) - unnamed_portal_count = 0; - sprintf(portalname, "<unnamed cursor %d>", unnamed_portal_count); - if (GetPortalByName(portalname) == NULL) - break; - } - - name = portalname; - } - else - { - /* Ensure the portal doesn't exist already */ - portal = GetPortalByName(name); - if (portal != NULL) - elog(ERROR, "cursor \"%s\" already in use", name); - } - - /* Create the portal */ - portal = CreatePortal(name); - if (portal == NULL) - elog(ERROR, "failed to create portal \"%s\"", name); - - /* Switch to portals memory and copy the parsetree and plan to there */ - oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - queryTree = copyObject(queryTree); - planTree = copyObject(planTree); - - /* Modify the parsetree to be a cursor */ - queryTree->isPortal = true; - queryTree->into = makeNode(RangeVar); - queryTree->into->relname = pstrdup(name); - queryTree->isBinary = false; - - /* Create the QueryDesc object and the executor state */ - queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL); - eState = CreateExecutorState(); - - /* If the plan has parameters, put them into the executor state */ - if (spiplan->nargs > 0) - { - ParamListInfo paramLI; - - paramLI = (ParamListInfo) palloc((spiplan->nargs + 1) * - sizeof(ParamListInfoData)); - MemSet(paramLI, 0, (spiplan->nargs + 1) * sizeof(ParamListInfoData)); - - eState->es_param_list_info = paramLI; - for (k = 0; k < spiplan->nargs; paramLI++, k++) - { - paramLI->kind = PARAM_NUM; - paramLI->id = k + 1; - paramLI->isnull = (Nulls && Nulls[k] == 'n'); - if (paramLI->isnull) - { - /* nulls just copy */ - paramLI->value = Values[k]; - } - else - { - /* pass-by-ref values must be copied into portal context */ - int16 paramTypLen; - bool paramTypByVal; - - get_typlenbyval(spiplan->argtypes[k], - ¶mTypLen, ¶mTypByVal); - paramLI->value = datumCopy(Values[k], - paramTypByVal, paramTypLen); - } - } - paramLI->kind = PARAM_INVALID; - } - else - eState->es_param_list_info = NULL; - - /* Start the executor */ - attinfo = ExecutorStart(queryDesc, eState); - - /* Put all the objects into the portal */ - PortalSetQuery(portal, queryDesc, attinfo, eState, PortalCleanup); - - /* Switch back to the callers memory context */ - MemoryContextSwitchTo(oldcontext); - - /* Return the created portal */ - return portal; -} - - -/* - * SPI_cursor_find() - * - * Find the portal of an existing open cursor - */ -Portal -SPI_cursor_find(char *name) -{ - return GetPortalByName(name); -} - - -/* - * SPI_cursor_fetch() - * - * Fetch rows in a cursor - */ -void -SPI_cursor_fetch(Portal portal, bool forward, int count) -{ - _SPI_cursor_operation(portal, forward, count, SPI); -} - - -/* - * SPI_cursor_move() - * - * Move in a cursor - */ -void -SPI_cursor_move(Portal portal, bool forward, int count) -{ - _SPI_cursor_operation(portal, forward, count, None); -} - - -/* - * SPI_cursor_close() - * - * Close a cursor - */ -void -SPI_cursor_close(Portal portal) -{ - if (!PortalIsValid(portal)) - elog(ERROR, "invalid portal in SPI cursor operation"); - - PortalDrop(portal); -} - -/* =================== private functions =================== */ - -/* - * spi_printtup - * store tuple retrieved by Executor into SPITupleTable - * of current SPI procedure - * - */ -void -spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self) -{ - SPITupleTable *tuptable; - MemoryContext oldcxt; - MemoryContext tuptabcxt; - - /* - * When called by Executor _SPI_curid expected to be equal to - * _SPI_connected - */ - if (_SPI_curid != _SPI_connected || _SPI_connected < 0) - elog(FATAL, "SPI: improper call to spi_printtup"); - if (_SPI_current != &(_SPI_stack[_SPI_curid])) - elog(FATAL, "SPI: stack corrupted in spi_printtup"); - - oldcxt = _SPI_procmem(); /* switch to procedure memory context */ - - tuptable = _SPI_current->tuptable; - if (tuptable == NULL) - { - tuptabcxt = AllocSetContextCreate(CurrentMemoryContext, - "SPI TupTable", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - MemoryContextSwitchTo(tuptabcxt); - - _SPI_current->tuptable = tuptable = (SPITupleTable *) - palloc(sizeof(SPITupleTable)); - tuptable->tuptabcxt = tuptabcxt; - tuptable->alloced = tuptable->free = 128; - tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple)); - tuptable->tupdesc = CreateTupleDescCopy(tupdesc); - } - else - { - MemoryContextSwitchTo(tuptable->tuptabcxt); - - if (tuptable->free == 0) - { - tuptable->free = 256; - tuptable->alloced += tuptable->free; - tuptable->vals = (HeapTuple *) repalloc(tuptable->vals, - tuptable->alloced * sizeof(HeapTuple)); - } - } - - tuptable->vals[tuptable->alloced - tuptable->free] = heap_copytuple(tuple); - (tuptable->free)--; - - MemoryContextSwitchTo(oldcxt); - return; -} - -/* - * Static functions - */ - -static int -_SPI_execute(char *src, int tcount, _SPI_plan *plan) -{ - List *queryTree_list; - List *planTree_list; - List *queryTree_list_item; - QueryDesc *qdesc; - Query *queryTree; - Plan *planTree; - EState *state; - int nargs = 0; - Oid *argtypes = NULL; - int res = 0; - bool islastquery; - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement(); - - SPI_processed = 0; - SPI_lastoid = InvalidOid; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - _SPI_current->qtlist = NULL; - - if (plan) - { - nargs = plan->nargs; - argtypes = plan->argtypes; - } - - queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs); - - _SPI_current->qtlist = queryTree_list; - - planTree_list = NIL; - - foreach(queryTree_list_item, queryTree_list) - { - queryTree = (Query *) lfirst(queryTree_list_item); - islastquery = (lnext(queryTree_list_item) == NIL); - - planTree = pg_plan_query(queryTree); - planTree_list = lappend(planTree_list, planTree); - - if (queryTree->commandType == CMD_UTILITY) - { - if (nodeTag(queryTree->utilityStmt) == T_CopyStmt) - { - CopyStmt *stmt = (CopyStmt *) (queryTree->utilityStmt); - - if (stmt->filename == NULL) - return SPI_ERROR_COPY; - } - else if (nodeTag(queryTree->utilityStmt) == T_ClosePortalStmt || - nodeTag(queryTree->utilityStmt) == T_FetchStmt) - return SPI_ERROR_CURSOR; - else if (nodeTag(queryTree->utilityStmt) == T_TransactionStmt) - return SPI_ERROR_TRANSACTION; - res = SPI_OK_UTILITY; - if (plan == NULL) - { - ProcessUtility(queryTree->utilityStmt, None, NULL); - if (!islastquery) - CommandCounterIncrement(); - else - return res; - } - else if (islastquery) - break; - } - else if (plan == NULL) - { - qdesc = CreateQueryDesc(queryTree, planTree, - islastquery ? SPI : None, NULL); - state = CreateExecutorState(); - res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0); - if (res < 0 || islastquery) - return res; - CommandCounterIncrement(); - } - else - { - qdesc = CreateQueryDesc(queryTree, planTree, - islastquery ? SPI : None, NULL); - res = _SPI_pquery(qdesc, NULL, islastquery ? tcount : 0); - if (res < 0) - return res; - if (islastquery) - break; - } - } - - if (plan) - { - plan->qtlist = queryTree_list; - plan->ptlist = planTree_list; - } - - return res; -} - -static int -_SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount) -{ - List *queryTree_list = plan->qtlist; - List *planTree_list = plan->ptlist; - List *queryTree_list_item; - QueryDesc *qdesc; - Query *queryTree; - Plan *planTree; - EState *state; - int nargs = plan->nargs; - int res = 0; - bool islastquery; - int k; - - /* Increment CommandCounter to see changes made by now */ - CommandCounterIncrement(); - - SPI_processed = 0; - SPI_lastoid = InvalidOid; - SPI_tuptable = NULL; - _SPI_current->tuptable = NULL; - _SPI_current->qtlist = NULL; - - foreach(queryTree_list_item, queryTree_list) - { - queryTree = (Query *) lfirst(queryTree_list_item); - planTree = lfirst(planTree_list); - planTree_list = lnext(planTree_list); - islastquery = (planTree_list == NIL); /* assume lists are same - * len */ - - if (queryTree->commandType == CMD_UTILITY) - { - ProcessUtility(queryTree->utilityStmt, None, NULL); - if (!islastquery) - CommandCounterIncrement(); - else - return SPI_OK_UTILITY; - } - else - { - qdesc = CreateQueryDesc(queryTree, planTree, - islastquery ? SPI : None, NULL); - state = CreateExecutorState(); - if (nargs > 0) - { - ParamListInfo paramLI; - - paramLI = (ParamListInfo) palloc((nargs + 1) * - sizeof(ParamListInfoData)); - MemSet(paramLI, 0, (nargs + 1) * sizeof(ParamListInfoData)); - - state->es_param_list_info = paramLI; - for (k = 0; k < plan->nargs; paramLI++, k++) - { - paramLI->kind = PARAM_NUM; - paramLI->id = k + 1; - paramLI->isnull = (Nulls && Nulls[k] == 'n'); - paramLI->value = Values[k]; - } - paramLI->kind = PARAM_INVALID; - } - else - state->es_param_list_info = NULL; - res = _SPI_pquery(qdesc, state, islastquery ? tcount : 0); - if (res < 0 || islastquery) - return res; - CommandCounterIncrement(); - } - } - - return res; -} - -static int -_SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount) -{ - Query *parseTree = queryDesc->parsetree; - int operation = queryDesc->operation; - CommandDest dest = queryDesc->dest; - bool isRetrieveIntoPortal = false; - bool isRetrieveIntoRelation = false; - char *intoName = NULL; - int res; - Oid save_lastoid; - - switch (operation) - { - case CMD_SELECT: - res = SPI_OK_SELECT; - if (parseTree->isPortal) - { - isRetrieveIntoPortal = true; - intoName = parseTree->into->relname; - parseTree->isBinary = false; /* */ - - return SPI_ERROR_CURSOR; - - } - else if (parseTree->into != NULL) /* select into table */ - { - res = SPI_OK_SELINTO; - isRetrieveIntoRelation = true; - queryDesc->dest = None; /* */ - } - break; - case CMD_INSERT: - res = SPI_OK_INSERT; - break; - case CMD_DELETE: - res = SPI_OK_DELETE; - break; - case CMD_UPDATE: - res = SPI_OK_UPDATE; - break; - default: - return SPI_ERROR_OPUNKNOWN; - } - - if (state == NULL) /* plan preparation */ - return res; - -#ifdef SPI_EXECUTOR_STATS - if (ShowExecutorStats) - ResetUsage(); -#endif - - ExecutorStart(queryDesc, state); - - /* - * Don't work currently --- need to rearrange callers so that we - * prepare the portal before doing CreateExecutorState() etc. See - * pquery.c for the correct order of operations. - */ - if (isRetrieveIntoPortal) - elog(FATAL, "SPI_select: retrieve into portal not implemented"); - - ExecutorRun(queryDesc, state, ForwardScanDirection, (long) tcount); - - _SPI_current->processed = state->es_processed; - save_lastoid = state->es_lastoid; - - if (operation == CMD_SELECT && queryDesc->dest == SPI) - { - if (_SPI_checktuples()) - elog(FATAL, "SPI_select: # of processed tuples check failed"); - } - - ExecutorEnd(queryDesc, state); - -#ifdef SPI_EXECUTOR_STATS - if (ShowExecutorStats) - ShowUsage("SPI EXECUTOR STATS"); -#endif - - if (dest == SPI) - { - SPI_processed = _SPI_current->processed; - SPI_lastoid = save_lastoid; - SPI_tuptable = _SPI_current->tuptable; - } - queryDesc->dest = dest; - - return res; - -} - -/* - * _SPI_cursor_operation() - * - * Do a FETCH or MOVE in a cursor - */ -static void -_SPI_cursor_operation(Portal portal, bool forward, int count, - CommandDest dest) -{ - QueryDesc *querydesc; - EState *estate; - MemoryContext oldcontext; - ScanDirection direction; - CommandDest olddest; - - /* Check that the portal is valid */ - if (!PortalIsValid(portal)) - elog(ERROR, "invalid portal in SPI cursor operation"); - - /* Push the SPI stack */ - _SPI_begin_call(true); - - /* Reset the SPI result */ - SPI_processed = 0; - SPI_tuptable = NULL; - _SPI_current->processed = 0; - _SPI_current->tuptable = NULL; - - /* Switch to the portals memory context */ - oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - - querydesc = PortalGetQueryDesc(portal); - estate = PortalGetState(portal); - - /* Save the queries command destination and set it to SPI (for fetch) */ - /* or None (for move) */ - olddest = querydesc->dest; - querydesc->dest = dest; - - /* Run the executor like PerformPortalFetch and remember states */ - if (forward) - { - if (portal->atEnd) - direction = NoMovementScanDirection; - else - direction = ForwardScanDirection; - - ExecutorRun(querydesc, estate, direction, (long) count); - - if (estate->es_processed > 0) - portal->atStart = false; /* OK to back up now */ - if (count <= 0 || (int) estate->es_processed < count) - portal->atEnd = true; /* we retrieved 'em all */ - } - else - { - if (portal->atStart) - direction = NoMovementScanDirection; - else - direction = BackwardScanDirection; - - ExecutorRun(querydesc, estate, direction, (long) count); - - if (estate->es_processed > 0) - portal->atEnd = false; /* OK to go forward now */ - if (count <= 0 || (int) estate->es_processed < count) - portal->atStart = true; /* we retrieved 'em all */ - } - - _SPI_current->processed = estate->es_processed; - - /* Restore the old command destination and switch back to callers */ - /* memory context */ - querydesc->dest = olddest; - MemoryContextSwitchTo(oldcontext); - - if (dest == SPI && _SPI_checktuples()) - elog(FATAL, "SPI_fetch: # of processed tuples check failed"); - - /* Put the result into place for access by caller */ - SPI_processed = _SPI_current->processed; - SPI_tuptable = _SPI_current->tuptable; - - /* Pop the SPI stack */ - _SPI_end_call(true); -} - - -static MemoryContext -_SPI_execmem() -{ - return MemoryContextSwitchTo(_SPI_current->execCxt); -} - -static MemoryContext -_SPI_procmem() -{ - return MemoryContextSwitchTo(_SPI_current->procCxt); -} - -/* - * _SPI_begin_call - * - */ -static int -_SPI_begin_call(bool execmem) -{ - if (_SPI_curid + 1 != _SPI_connected) - return SPI_ERROR_UNCONNECTED; - _SPI_curid++; - if (_SPI_current != &(_SPI_stack[_SPI_curid])) - elog(FATAL, "SPI: stack corrupted"); - - if (execmem) /* switch to the Executor memory context */ - _SPI_execmem(); - - return 0; -} - -static int -_SPI_end_call(bool procmem) -{ - /* - * We' returning to procedure where _SPI_curid == _SPI_connected - 1 - */ - _SPI_curid--; - - _SPI_current->qtlist = NULL; - - if (procmem) /* switch to the procedure memory context */ - { - _SPI_procmem(); - /* and free Executor memory */ - MemoryContextResetAndDeleteChildren(_SPI_current->execCxt); - } - - return 0; -} - -static bool -_SPI_checktuples(void) -{ - uint32 processed = _SPI_current->processed; - SPITupleTable *tuptable = _SPI_current->tuptable; - bool failed = false; - - if (processed == 0) - { - if (tuptable != NULL) - failed = true; - } - else - { - /* some tuples were processed */ - if (tuptable == NULL) /* spi_printtup was not called */ - failed = true; - else if (processed != (tuptable->alloced - tuptable->free)) - failed = true; - } - - return failed; -} - -static _SPI_plan * -_SPI_copy_plan(_SPI_plan *plan, int location) -{ - _SPI_plan *newplan; - MemoryContext oldcxt; - MemoryContext plancxt; - MemoryContext parentcxt; - - /* Determine correct parent for the plan's memory context */ - if (location == _SPI_CPLAN_PROCXT) - parentcxt = _SPI_current->procCxt; - else if (location == _SPI_CPLAN_TOPCXT) - parentcxt = TopMemoryContext; - else - parentcxt = CurrentMemoryContext; - - /* - * Create a memory context for the plan. We don't expect the plan to - * be very large, so use smaller-than-default alloc parameters. - */ - plancxt = AllocSetContextCreate(parentcxt, - "SPI Plan", - 1024, - 1024, - ALLOCSET_DEFAULT_MAXSIZE); - oldcxt = MemoryContextSwitchTo(plancxt); - - /* Copy the SPI plan into its own context */ - newplan = (_SPI_plan *) palloc(sizeof(_SPI_plan)); - newplan->plancxt = plancxt; - newplan->qtlist = (List *) copyObject(plan->qtlist); - newplan->ptlist = (List *) copyObject(plan->ptlist); - newplan->nargs = plan->nargs; - if (plan->nargs > 0) - { - newplan->argtypes = (Oid *) palloc(plan->nargs * sizeof(Oid)); - memcpy(newplan->argtypes, plan->argtypes, plan->nargs * sizeof(Oid)); - } - else - newplan->argtypes = NULL; - - MemoryContextSwitchTo(oldcxt); - - return newplan; -} |