diff options
author | Pavan Deolasee | 2017-06-14 05:42:18 +0000 |
---|---|---|
committer | Pavan Deolasee | 2017-06-14 05:42:18 +0000 |
commit | 15dd5274c323fb93e4e3ea9ad2185aaaec10f79c (patch) | |
tree | 9dafb4c7f735d9429ea461dc792933af87493c33 /contrib | |
parent | dfbb88e3bbb526dcb204b456b9e5cfd9d10d0d0a (diff) | |
parent | d5cb3bab564e0927ffac7c8729eacf181a12dd40 (diff) |
Merge from PG master upto d5cb3bab564e0927ffac7c8729eacf181a12dd40
This is the result of the "git merge remotes/PGSQL/master" upto the said commit
point. We have done some basic analysis, fixed compilation problems etc, but
bulk of the logical problems in conflict resolution etc will be handled by
subsequent commits.
Diffstat (limited to 'contrib')
306 files changed, 16900 insertions, 14115 deletions
diff --git a/contrib/Makefile b/contrib/Makefile index fedc61b243..d250341270 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -6,6 +6,7 @@ include $(top_builddir)/src/Makefile.global SUBDIRS = \ adminpack \ + amcheck \ auth_delay \ auto_explain \ bloom \ @@ -50,7 +51,6 @@ SUBDIRS = \ test_decoding \ tsm_system_rows \ tsm_system_time \ - tsearch2 \ unaccent \ vacuumlo \ stormstats diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c index ea781a0a5a..10338f951f 100644 --- a/contrib/adminpack/adminpack.c +++ b/contrib/adminpack/adminpack.c @@ -3,7 +3,7 @@ * adminpack.c * * - * Copyright (c) 2002-2016, PostgreSQL Global Development Group + * Copyright (c) 2002-2017, PostgreSQL Global Development Group * * Author: Andreas Pflug <pgadmin@pse-consulting.de> * @@ -124,8 +124,8 @@ pg_file_write(PG_FUNCTION_ARGS) requireSuperuser(); - filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); - data = PG_GETARG_TEXT_P(1); + filename = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false); + data = PG_GETARG_TEXT_PP(1); if (!PG_GETARG_BOOL(2)) { @@ -136,10 +136,10 @@ pg_file_write(PG_FUNCTION_ARGS) (ERRCODE_DUPLICATE_FILE, errmsg("file \"%s\" exists", filename))); - f = fopen(filename, "wb"); + f = AllocateFile(filename, "wb"); } else - f = fopen(filename, "ab"); + f = AllocateFile(filename, "ab"); if (!f) ereport(ERROR, @@ -147,16 +147,11 @@ pg_file_write(PG_FUNCTION_ARGS) errmsg("could not open file \"%s\" for writing: %m", filename))); - if (VARSIZE(data) != 0) - { - count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f); - - if (count != VARSIZE(data) - VARHDRSZ) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not write file \"%s\": %m", filename))); - } - fclose(f); + count = fwrite(VARDATA_ANY(data), 1, VARSIZE_ANY_EXHDR(data), f); + if (count != VARSIZE_ANY_EXHDR(data) || FreeFile(f)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not write file \"%s\": %m", filename))); PG_RETURN_INT64(count); } @@ -175,12 +170,12 @@ pg_file_rename(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) PG_RETURN_NULL(); - fn1 = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); - fn2 = convert_and_check_filename(PG_GETARG_TEXT_P(1), false); + fn1 = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false); + fn2 = convert_and_check_filename(PG_GETARG_TEXT_PP(1), false); if (PG_ARGISNULL(2)) fn3 = 0; else - fn3 = convert_and_check_filename(PG_GETARG_TEXT_P(2), false); + fn3 = convert_and_check_filename(PG_GETARG_TEXT_PP(2), false); if (access(fn1, W_OK) < 0) { @@ -259,7 +254,7 @@ pg_file_unlink(PG_FUNCTION_ARGS) requireSuperuser(); - filename = convert_and_check_filename(PG_GETARG_TEXT_P(0), false); + filename = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false); if (access(filename, W_OK) < 0) { diff --git a/contrib/tsearch2/.gitignore b/contrib/amcheck/.gitignore index 5dcb3ff972..5dcb3ff972 100644 --- a/contrib/tsearch2/.gitignore +++ b/contrib/amcheck/.gitignore diff --git a/contrib/amcheck/Makefile b/contrib/amcheck/Makefile new file mode 100644 index 0000000000..43bed919ae --- /dev/null +++ b/contrib/amcheck/Makefile @@ -0,0 +1,21 @@ +# contrib/amcheck/Makefile + +MODULE_big = amcheck +OBJS = verify_nbtree.o $(WIN32RES) + +EXTENSION = amcheck +DATA = amcheck--1.0.sql +PGFILEDESC = "amcheck - function for verifying relation integrity" + +REGRESS = check check_btree + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/amcheck +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/amcheck/amcheck--1.0.sql b/contrib/amcheck/amcheck--1.0.sql new file mode 100644 index 0000000000..a6612d130c --- /dev/null +++ b/contrib/amcheck/amcheck--1.0.sql @@ -0,0 +1,24 @@ +/* contrib/amcheck/amcheck--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION amcheck" to load this file. \quit + +-- +-- bt_index_check() +-- +CREATE FUNCTION bt_index_check(index regclass) +RETURNS VOID +AS 'MODULE_PATHNAME', 'bt_index_check' +LANGUAGE C STRICT PARALLEL RESTRICTED; + +-- +-- bt_index_parent_check() +-- +CREATE FUNCTION bt_index_parent_check(index regclass) +RETURNS VOID +AS 'MODULE_PATHNAME', 'bt_index_parent_check' +LANGUAGE C STRICT PARALLEL RESTRICTED; + +-- Don't want these to be available to public +REVOKE ALL ON FUNCTION bt_index_check(regclass) FROM PUBLIC; +REVOKE ALL ON FUNCTION bt_index_parent_check(regclass) FROM PUBLIC; diff --git a/contrib/amcheck/amcheck.control b/contrib/amcheck/amcheck.control new file mode 100644 index 0000000000..05e2861d7a --- /dev/null +++ b/contrib/amcheck/amcheck.control @@ -0,0 +1,5 @@ +# amcheck extension +comment = 'functions for verifying relation integrity' +default_version = '1.0' +module_pathname = '$libdir/amcheck' +relocatable = true diff --git a/contrib/amcheck/expected/check.out b/contrib/amcheck/expected/check.out new file mode 100644 index 0000000000..5b3e6d530c --- /dev/null +++ b/contrib/amcheck/expected/check.out @@ -0,0 +1 @@ +CREATE EXTENSION amcheck; diff --git a/contrib/amcheck/expected/check_btree.out b/contrib/amcheck/expected/check_btree.out new file mode 100644 index 0000000000..df3741e2c9 --- /dev/null +++ b/contrib/amcheck/expected/check_btree.out @@ -0,0 +1,92 @@ +-- minimal test, basically just verifying that amcheck +CREATE TABLE bttest_a(id int8); +CREATE TABLE bttest_b(id int8); +INSERT INTO bttest_a SELECT * FROM generate_series(1, 100000); +INSERT INTO bttest_b SELECT * FROM generate_series(100000, 1, -1); +CREATE INDEX bttest_a_idx ON bttest_a USING btree (id); +CREATE INDEX bttest_b_idx ON bttest_b USING btree (id); +CREATE ROLE bttest_role; +-- verify permissions are checked (error due to function not callable) +SET ROLE bttest_role; +SELECT bt_index_check('bttest_a_idx'::regclass); +ERROR: permission denied for function bt_index_check +SELECT bt_index_parent_check('bttest_a_idx'::regclass); +ERROR: permission denied for function bt_index_parent_check +RESET ROLE; +-- we, intentionally, don't check relation permissions - it's useful +-- to run this cluster-wide with a restricted account, and as tested +-- above explicit permission has to be granted for that. +GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO bttest_role; +GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO bttest_role; +SET ROLE bttest_role; +SELECT bt_index_check('bttest_a_idx'); + bt_index_check +---------------- + +(1 row) + +SELECT bt_index_parent_check('bttest_a_idx'); + bt_index_parent_check +----------------------- + +(1 row) + +RESET ROLE; +-- verify plain tables are rejected (error) +SELECT bt_index_check('bttest_a'); +ERROR: "bttest_a" is not an index +SELECT bt_index_parent_check('bttest_a'); +ERROR: "bttest_a" is not an index +-- verify non-existing indexes are rejected (error) +SELECT bt_index_check(17); +ERROR: could not open relation with OID 17 +SELECT bt_index_parent_check(17); +ERROR: could not open relation with OID 17 +-- verify wrong index types are rejected (error) +BEGIN; +CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id); +SELECT bt_index_parent_check('bttest_a_brin_idx'); +ERROR: only B-Tree indexes are supported as targets for verification +DETAIL: Relation "bttest_a_brin_idx" is not a B-Tree index. +ROLLBACK; +-- normal check outside of xact +SELECT bt_index_check('bttest_a_idx'); + bt_index_check +---------------- + +(1 row) + +-- more expansive test +SELECT bt_index_parent_check('bttest_b_idx'); + bt_index_parent_check +----------------------- + +(1 row) + +BEGIN; +SELECT bt_index_check('bttest_a_idx'); + bt_index_check +---------------- + +(1 row) + +SELECT bt_index_parent_check('bttest_b_idx'); + bt_index_parent_check +----------------------- + +(1 row) + +-- make sure we don't have any leftover locks +SELECT * FROM pg_locks +WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[]) + AND pid = pg_backend_pid(); + locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid | mode | granted | fastpath +----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----+------+---------+---------- +(0 rows) + +COMMIT; +-- cleanup +DROP TABLE bttest_a; +DROP TABLE bttest_b; +DROP OWNED BY bttest_role; -- permissions +DROP ROLE bttest_role; diff --git a/contrib/amcheck/sql/check.sql b/contrib/amcheck/sql/check.sql new file mode 100644 index 0000000000..5b3e6d530c --- /dev/null +++ b/contrib/amcheck/sql/check.sql @@ -0,0 +1 @@ +CREATE EXTENSION amcheck; diff --git a/contrib/amcheck/sql/check_btree.sql b/contrib/amcheck/sql/check_btree.sql new file mode 100644 index 0000000000..fd90531027 --- /dev/null +++ b/contrib/amcheck/sql/check_btree.sql @@ -0,0 +1,61 @@ +-- minimal test, basically just verifying that amcheck +CREATE TABLE bttest_a(id int8); +CREATE TABLE bttest_b(id int8); + +INSERT INTO bttest_a SELECT * FROM generate_series(1, 100000); +INSERT INTO bttest_b SELECT * FROM generate_series(100000, 1, -1); + +CREATE INDEX bttest_a_idx ON bttest_a USING btree (id); +CREATE INDEX bttest_b_idx ON bttest_b USING btree (id); + +CREATE ROLE bttest_role; + +-- verify permissions are checked (error due to function not callable) +SET ROLE bttest_role; +SELECT bt_index_check('bttest_a_idx'::regclass); +SELECT bt_index_parent_check('bttest_a_idx'::regclass); +RESET ROLE; + +-- we, intentionally, don't check relation permissions - it's useful +-- to run this cluster-wide with a restricted account, and as tested +-- above explicit permission has to be granted for that. +GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO bttest_role; +GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO bttest_role; +SET ROLE bttest_role; +SELECT bt_index_check('bttest_a_idx'); +SELECT bt_index_parent_check('bttest_a_idx'); +RESET ROLE; + +-- verify plain tables are rejected (error) +SELECT bt_index_check('bttest_a'); +SELECT bt_index_parent_check('bttest_a'); + +-- verify non-existing indexes are rejected (error) +SELECT bt_index_check(17); +SELECT bt_index_parent_check(17); + +-- verify wrong index types are rejected (error) +BEGIN; +CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id); +SELECT bt_index_parent_check('bttest_a_brin_idx'); +ROLLBACK; + +-- normal check outside of xact +SELECT bt_index_check('bttest_a_idx'); +-- more expansive test +SELECT bt_index_parent_check('bttest_b_idx'); + +BEGIN; +SELECT bt_index_check('bttest_a_idx'); +SELECT bt_index_parent_check('bttest_b_idx'); +-- make sure we don't have any leftover locks +SELECT * FROM pg_locks +WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[]) + AND pid = pg_backend_pid(); +COMMIT; + +-- cleanup +DROP TABLE bttest_a; +DROP TABLE bttest_b; +DROP OWNED BY bttest_role; -- permissions +DROP ROLE bttest_role; diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c new file mode 100644 index 0000000000..c134e5f3b0 --- /dev/null +++ b/contrib/amcheck/verify_nbtree.c @@ -0,0 +1,1248 @@ +/*------------------------------------------------------------------------- + * + * verify_nbtree.c + * Verifies the integrity of nbtree indexes based on invariants. + * + * For B-Tree indexes, verification includes checking that each page in the + * target index has items in logical order as reported by an insertion scankey + * (the insertion scankey sort-wise NULL semantics are needed for + * verification). + * + * + * Copyright (c) 2017, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/amcheck/verify_nbtree.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/nbtree.h" +#include "access/transam.h" +#include "catalog/index.h" +#include "catalog/pg_am.h" +#include "commands/tablecmds.h" +#include "miscadmin.h" +#include "storage/lmgr.h" +#include "utils/memutils.h" +#include "utils/snapmgr.h" + + +PG_MODULE_MAGIC; + +/* + * A B-Tree cannot possibly have this many levels, since there must be one + * block per level, which is bound by the range of BlockNumber: + */ +#define InvalidBtreeLevel ((uint32) InvalidBlockNumber) + +/* + * State associated with verifying a B-Tree index + * + * target is the point of reference for a verification operation. + * + * Other B-Tree pages may be allocated, but those are always auxiliary (e.g., + * they are current target's child pages). Conceptually, problems are only + * ever found in the current target page. Each page found by verification's + * left/right, top/bottom scan becomes the target exactly once. + */ +typedef struct BtreeCheckState +{ + /* + * Unchanging state, established at start of verification: + */ + + /* B-Tree Index Relation */ + Relation rel; + /* ShareLock held on heap/index, rather than AccessShareLock? */ + bool readonly; + /* Per-page context */ + MemoryContext targetcontext; + /* Buffer access strategy */ + BufferAccessStrategy checkstrategy; + + /* + * Mutable state, for verification of particular page: + */ + + /* Current target page */ + Page target; + /* Target block number */ + BlockNumber targetblock; + /* Target page's LSN */ + XLogRecPtr targetlsn; +} BtreeCheckState; + +/* + * Starting point for verifying an entire B-Tree index level + */ +typedef struct BtreeLevel +{ + /* Level number (0 is leaf page level). */ + uint32 level; + + /* Left most block on level. Scan of level begins here. */ + BlockNumber leftmost; + + /* Is this level reported as "true" root level by meta page? */ + bool istruerootlevel; +} BtreeLevel; + +PG_FUNCTION_INFO_V1(bt_index_check); +PG_FUNCTION_INFO_V1(bt_index_parent_check); + +static void bt_index_check_internal(Oid indrelid, bool parentcheck); +static inline void btree_index_checkable(Relation rel); +static void bt_check_every_level(Relation rel, bool readonly); +static BtreeLevel bt_check_level_from_leftmost(BtreeCheckState *state, + BtreeLevel level); +static void bt_target_page_check(BtreeCheckState *state); +static ScanKey bt_right_page_check_scankey(BtreeCheckState *state); +static void bt_downlink_check(BtreeCheckState *state, BlockNumber childblock, + ScanKey targetkey); +static inline bool offset_is_negative_infinity(BTPageOpaque opaque, + OffsetNumber offset); +static inline bool invariant_leq_offset(BtreeCheckState *state, + ScanKey key, + OffsetNumber upperbound); +static inline bool invariant_geq_offset(BtreeCheckState *state, + ScanKey key, + OffsetNumber lowerbound); +static inline bool invariant_leq_nontarget_offset(BtreeCheckState *state, + Page other, + ScanKey key, + OffsetNumber upperbound); +static Page palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum); + +/* + * bt_index_check(index regclass) + * + * Verify integrity of B-Tree index. + * + * Acquires AccessShareLock on heap & index relations. Does not consider + * invariants that exist between parent/child pages. + */ +Datum +bt_index_check(PG_FUNCTION_ARGS) +{ + Oid indrelid = PG_GETARG_OID(0); + + bt_index_check_internal(indrelid, false); + + PG_RETURN_VOID(); +} + +/* + * bt_index_parent_check(index regclass) + * + * Verify integrity of B-Tree index. + * + * Acquires ShareLock on heap & index relations. Verifies that downlinks in + * parent pages are valid lower bounds on child pages. + */ +Datum +bt_index_parent_check(PG_FUNCTION_ARGS) +{ + Oid indrelid = PG_GETARG_OID(0); + + bt_index_check_internal(indrelid, true); + + PG_RETURN_VOID(); +} + +/* + * Helper for bt_index_[parent_]check, coordinating the bulk of the work. + */ +static void +bt_index_check_internal(Oid indrelid, bool parentcheck) +{ + Oid heapid; + Relation indrel; + Relation heaprel; + LOCKMODE lockmode; + + if (parentcheck) + lockmode = ShareLock; + else + lockmode = AccessShareLock; + + /* + * We must lock table before index to avoid deadlocks. However, if the + * passed indrelid isn't an index then IndexGetRelation() will fail. + * Rather than emitting a not-very-helpful error message, postpone + * complaining, expecting that the is-it-an-index test below will fail. + * + * In hot standby mode this will raise an error when parentcheck is true. + */ + heapid = IndexGetRelation(indrelid, true); + if (OidIsValid(heapid)) + heaprel = heap_open(heapid, lockmode); + else + heaprel = NULL; + + /* + * Open the target index relations separately (like relation_openrv(), but + * with heap relation locked first to prevent deadlocking). In hot + * standby mode this will raise an error when parentcheck is true. + */ + indrel = index_open(indrelid, lockmode); + + /* + * Since we did the IndexGetRelation call above without any lock, it's + * barely possible that a race against an index drop/recreation could have + * netted us the wrong table. Although the table itself won't actually be + * examined during verification currently, a recheck still seems like a + * good idea. + */ + if (heaprel == NULL || heapid != IndexGetRelation(indrelid, false)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("could not open parent table of index %s", + RelationGetRelationName(indrel)))); + + /* Relation suitable for checking as B-Tree? */ + btree_index_checkable(indrel); + + /* Check index */ + bt_check_every_level(indrel, parentcheck); + + /* + * Release locks early. That's ok here because nothing in the called + * routines will trigger shared cache invalidations to be sent, so we can + * relax the usual pattern of only releasing locks after commit. + */ + index_close(indrel, lockmode); + if (heaprel) + heap_close(heaprel, lockmode); +} + +/* + * Basic checks about the suitability of a relation for checking as a B-Tree + * index. + * + * NB: Intentionally not checking permissions, the function is normally not + * callable by non-superusers. If granted, it's useful to be able to check a + * whole cluster. + */ +static inline void +btree_index_checkable(Relation rel) +{ + if (rel->rd_rel->relkind != RELKIND_INDEX || + rel->rd_rel->relam != BTREE_AM_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only B-Tree indexes are supported as targets for verification"), + errdetail("Relation \"%s\" is not a B-Tree index.", + RelationGetRelationName(rel)))); + + if (RELATION_IS_OTHER_TEMP(rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"), + errdetail("Index \"%s\" is associated with temporary relation.", + RelationGetRelationName(rel)))); + + if (!IndexIsValid(rel->rd_index)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot check index \"%s\"", + RelationGetRelationName(rel)), + errdetail("Index is not valid"))); +} + +/* + * Main entry point for B-Tree SQL-callable functions. Walks the B-Tree in + * logical order, verifying invariants as it goes. + * + * It is the caller's responsibility to acquire appropriate heavyweight lock on + * the index relation, and advise us if extra checks are safe when a ShareLock + * is held. + * + * A ShareLock is generally assumed to prevent any kind of physical + * modification to the index structure, including modifications that VACUUM may + * make. This does not include setting of the LP_DEAD bit by concurrent index + * scans, although that is just metadata that is not able to directly affect + * any check performed here. Any concurrent process that might act on the + * LP_DEAD bit being set (recycle space) requires a heavyweight lock that + * cannot be held while we hold a ShareLock. (Besides, even if that could + * happen, the ad-hoc recycling when a page might otherwise split is performed + * per-page, and requires an exclusive buffer lock, which wouldn't cause us + * trouble. _bt_delitems_vacuum() may only delete leaf items, and so the extra + * parent/child check cannot be affected.) + */ +static void +bt_check_every_level(Relation rel, bool readonly) +{ + BtreeCheckState *state; + Page metapage; + BTMetaPageData *metad; + uint32 previouslevel; + BtreeLevel current; + + /* + * RecentGlobalXmin assertion matches index_getnext_tid(). See note on + * RecentGlobalXmin/B-Tree page deletion. + */ + Assert(TransactionIdIsValid(RecentGlobalXmin)); + + /* + * Initialize state for entire verification operation + */ + state = palloc(sizeof(BtreeCheckState)); + state->rel = rel; + state->readonly = readonly; + /* Create context for page */ + state->targetcontext = AllocSetContextCreate(CurrentMemoryContext, + "amcheck context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + state->checkstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Get true root block from meta-page */ + metapage = palloc_btree_page(state, BTREE_METAPAGE); + metad = BTPageGetMeta(metapage); + + /* + * Certain deletion patterns can result in "skinny" B-Tree indexes, where + * the fast root and true root differ. + * + * Start from the true root, not the fast root, unlike conventional index + * scans. This approach is more thorough, and removes the risk of + * following a stale fast root from the meta page. + */ + if (metad->btm_fastroot != metad->btm_root) + ereport(DEBUG1, + (errcode(ERRCODE_NO_DATA), + errmsg("harmless fast root mismatch in index %s", + RelationGetRelationName(rel)), + errdetail_internal("Fast root block %u (level %u) differs from true root block %u (level %u).", + metad->btm_fastroot, metad->btm_fastlevel, + metad->btm_root, metad->btm_level))); + + /* + * Starting at the root, verify every level. Move left to right, top to + * bottom. Note that there may be no pages other than the meta page (meta + * page can indicate that root is P_NONE when the index is totally empty). + */ + previouslevel = InvalidBtreeLevel; + current.level = metad->btm_level; + current.leftmost = metad->btm_root; + current.istruerootlevel = true; + while (current.leftmost != P_NONE) + { + /* + * Verify this level, and get left most page for next level down, if + * not at leaf level + */ + current = bt_check_level_from_leftmost(state, current); + + if (current.leftmost == InvalidBlockNumber) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("index \"%s\" has no valid pages on level below %u or first level", + RelationGetRelationName(rel), previouslevel))); + + previouslevel = current.level; + } + + /* Be tidy: */ + MemoryContextDelete(state->targetcontext); +} + +/* + * Given a left-most block at some level, move right, verifying each page + * individually (with more verification across pages for "readonly" + * callers). Caller should pass the true root page as the leftmost initially, + * working their way down by passing what is returned for the last call here + * until level 0 (leaf page level) was reached. + * + * Returns state for next call, if any. This includes left-most block number + * one level lower that should be passed on next level/call, which is set to + * P_NONE on last call here (when leaf level is verified). Level numbers + * follow the nbtree convention: higher levels have higher numbers, because new + * levels are added only due to a root page split. Note that prior to the + * first root page split, the root is also a leaf page, so there is always a + * level 0 (leaf level), and it's always the last level processed. + * + * Note on memory management: State's per-page context is reset here, between + * each call to bt_target_page_check(). + */ +static BtreeLevel +bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level) +{ + /* State to establish early, concerning entire level */ + BTPageOpaque opaque; + MemoryContext oldcontext; + BtreeLevel nextleveldown; + + /* Variables for iterating across level using right links */ + BlockNumber leftcurrent = P_NONE; + BlockNumber current = level.leftmost; + + /* Initialize return state */ + nextleveldown.leftmost = InvalidBlockNumber; + nextleveldown.level = InvalidBtreeLevel; + nextleveldown.istruerootlevel = false; + + /* Use page-level context for duration of this call */ + oldcontext = MemoryContextSwitchTo(state->targetcontext); + + elog(DEBUG2, "verifying level %u%s", level.level, + level.istruerootlevel ? + " (true root level)" : level.level == 0 ? " (leaf level)" : ""); + + do + { + /* Don't rely on CHECK_FOR_INTERRUPTS() calls at lower level */ + CHECK_FOR_INTERRUPTS(); + + /* Initialize state for this iteration */ + state->targetblock = current; + state->target = palloc_btree_page(state, state->targetblock); + state->targetlsn = PageGetLSN(state->target); + + opaque = (BTPageOpaque) PageGetSpecialPointer(state->target); + + if (P_IGNORE(opaque)) + { + if (P_RIGHTMOST(opaque)) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("block %u fell off the end of index \"%s\"", + current, RelationGetRelationName(state->rel)))); + else + ereport(DEBUG1, + (errcode(ERRCODE_NO_DATA), + errmsg("block %u of index \"%s\" ignored", + current, RelationGetRelationName(state->rel)))); + goto nextpage; + } + else if (nextleveldown.leftmost == InvalidBlockNumber) + { + /* + * A concurrent page split could make the caller supplied leftmost + * block no longer contain the leftmost page, or no longer be the + * true root, but where that isn't possible due to heavyweight + * locking, check that the first valid page meets caller's + * expectations. + */ + if (state->readonly) + { + if (!P_LEFTMOST(opaque)) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("block %u is not leftmost in index \"%s\"", + current, RelationGetRelationName(state->rel)))); + + if (level.istruerootlevel && !P_ISROOT(opaque)) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("block %u is not true root in index \"%s\"", + current, RelationGetRelationName(state->rel)))); + } + + /* + * Before beginning any non-trivial examination of level, prepare + * state for next bt_check_level_from_leftmost() invocation for + * the next level for the next level down (if any). + * + * There should be at least one non-ignorable page per level, + * unless this is the leaf level, which is assumed by caller to be + * final level. + */ + if (!P_ISLEAF(opaque)) + { + IndexTuple itup; + ItemId itemid; + + /* Internal page -- downlink gets leftmost on next level */ + itemid = PageGetItemId(state->target, P_FIRSTDATAKEY(opaque)); + itup = (IndexTuple) PageGetItem(state->target, itemid); + nextleveldown.leftmost = ItemPointerGetBlockNumber(&(itup->t_tid)); + nextleveldown.level = opaque->btpo.level - 1; + } + else + { + /* + * Leaf page -- final level caller must process. + * + * Note that this could also be the root page, if there has + * been no root page split yet. + */ + nextleveldown.leftmost = P_NONE; + nextleveldown.level = InvalidBtreeLevel; + } + + /* + * Finished setting up state for this call/level. Control will + * never end up back here in any future loop iteration for this + * level. + */ + } + + if (state->readonly && opaque->btpo_prev != leftcurrent) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("left link/right link pair in index \"%s\" not in agreement", + RelationGetRelationName(state->rel)), + errdetail_internal("Block=%u left block=%u left link from block=%u.", + current, leftcurrent, opaque->btpo_prev))); + + /* Check level, which must be valid for non-ignorable page */ + if (level.level != opaque->btpo.level) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("leftmost down link for level points to block in index \"%s\" whose level is not one level down", + RelationGetRelationName(state->rel)), + errdetail_internal("Block pointed to=%u expected level=%u level in pointed to block=%u.", + current, level.level, opaque->btpo.level))); + + /* Verify invariants for page -- all important checks occur here */ + bt_target_page_check(state); + +nextpage: + + /* Try to detect circular links */ + if (current == leftcurrent || current == opaque->btpo_prev) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("circular link chain found in block %u of index \"%s\"", + current, RelationGetRelationName(state->rel)))); + + leftcurrent = current; + current = opaque->btpo_next; + + /* Free page and associated memory for this iteration */ + MemoryContextReset(state->targetcontext); + } + while (current != P_NONE); + + /* Don't change context for caller */ + MemoryContextSwitchTo(oldcontext); + + return nextleveldown; +} + +/* + * Function performs the following checks on target page, or pages ancillary to + * target page: + * + * - That every "real" data item is less than or equal to the high key, which + * is an upper bound on the items on the pages (where there is a high key at + * all -- pages that are rightmost lack one). + * + * - That within the page, every "real" item is less than or equal to the item + * immediately to its right, if any (i.e., that the items are in order within + * the page, so that the binary searches performed by index scans are sane). + * + * - That the last item stored on the page is less than or equal to the first + * "real" data item on the page to the right (if such a first item is + * available). + * + * Furthermore, when state passed shows ShareLock held, and target page is + * internal page, function also checks: + * + * - That all child pages respect downlinks lower bound. + * + * Note: Memory allocated in this routine is expected to be released by caller + * resetting state->targetcontext. + */ +static void +bt_target_page_check(BtreeCheckState *state) +{ + OffsetNumber offset; + OffsetNumber max; + BTPageOpaque topaque; + + topaque = (BTPageOpaque) PageGetSpecialPointer(state->target); + max = PageGetMaxOffsetNumber(state->target); + + elog(DEBUG2, "verifying %u items on %s block %u", max, + P_ISLEAF(topaque) ? "leaf" : "internal", state->targetblock); + + /* + * Loop over page items, starting from first non-highkey item, not high + * key (if any). Also, immediately skip "negative infinity" real item (if + * any). + */ + for (offset = P_FIRSTDATAKEY(topaque); + offset <= max; + offset = OffsetNumberNext(offset)) + { + ItemId itemid; + IndexTuple itup; + ScanKey skey; + + CHECK_FOR_INTERRUPTS(); + + /* + * Don't try to generate scankey using "negative infinity" garbage + * data + */ + if (offset_is_negative_infinity(topaque, offset)) + continue; + + /* Build insertion scankey for current page offset */ + itemid = PageGetItemId(state->target, offset); + itup = (IndexTuple) PageGetItem(state->target, itemid); + skey = _bt_mkscankey(state->rel, itup); + + /* + * * High key check * + * + * If there is a high key (if this is not the rightmost page on its + * entire level), check that high key actually is upper bound on all + * page items. + * + * We prefer to check all items against high key rather than checking + * just the last and trusting that the operator class obeys the + * transitive law (which implies that all previous items also + * respected the high key invariant if they pass the item order + * check). + * + * Ideally, we'd compare every item in the index against every other + * item in the index, and not trust opclass obedience of the + * transitive law to bridge the gap between children and their + * grandparents (as well as great-grandparents, and so on). We don't + * go to those lengths because that would be prohibitively expensive, + * and probably not markedly more effective in practice. + */ + if (!P_RIGHTMOST(topaque) && + !invariant_leq_offset(state, skey, P_HIKEY)) + { + char *itid, + *htid; + + itid = psprintf("(%u,%u)", state->targetblock, offset); + htid = psprintf("(%u,%u)", + ItemPointerGetBlockNumber(&(itup->t_tid)), + ItemPointerGetOffsetNumber(&(itup->t_tid))); + + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("high key invariant violated for index \"%s\"", + RelationGetRelationName(state->rel)), + errdetail_internal("Index tid=%s points to %s tid=%s page lsn=%X/%X.", + itid, + P_ISLEAF(topaque) ? "heap" : "index", + htid, + (uint32) (state->targetlsn >> 32), + (uint32) state->targetlsn))); + } + + /* + * * Item order check * + * + * Check that items are stored on page in logical order, by checking + * current item is less than or equal to next item (if any). + */ + if (OffsetNumberNext(offset) <= max && + !invariant_leq_offset(state, skey, + OffsetNumberNext(offset))) + { + char *itid, + *htid, + *nitid, + *nhtid; + + itid = psprintf("(%u,%u)", state->targetblock, offset); + htid = psprintf("(%u,%u)", + ItemPointerGetBlockNumber(&(itup->t_tid)), + ItemPointerGetOffsetNumber(&(itup->t_tid))); + nitid = psprintf("(%u,%u)", state->targetblock, + OffsetNumberNext(offset)); + + /* Reuse itup to get pointed-to heap location of second item */ + itemid = PageGetItemId(state->target, OffsetNumberNext(offset)); + itup = (IndexTuple) PageGetItem(state->target, itemid); + nhtid = psprintf("(%u,%u)", + ItemPointerGetBlockNumber(&(itup->t_tid)), + ItemPointerGetOffsetNumber(&(itup->t_tid))); + + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("item order invariant violated for index \"%s\"", + RelationGetRelationName(state->rel)), + errdetail_internal("Lower index tid=%s (points to %s tid=%s) " + "higher index tid=%s (points to %s tid=%s) " + "page lsn=%X/%X.", + itid, + P_ISLEAF(topaque) ? "heap" : "index", + htid, + nitid, + P_ISLEAF(topaque) ? "heap" : "index", + nhtid, + (uint32) (state->targetlsn >> 32), + (uint32) state->targetlsn))); + } + + /* + * * Last item check * + * + * Check last item against next/right page's first data item's when + * last item on page is reached. This additional check can detect + * transposed pages. + * + * This check is similar to the item order check that will have + * already been performed for every other "real" item on target page + * when last item is checked. The difference is that the next item + * (the item that is compared to target's last item) needs to come + * from the next/sibling page. There may not be such an item + * available from sibling for various reasons, though (e.g., target is + * the rightmost page on level). + */ + else if (offset == max) + { + ScanKey rightkey; + + /* Get item in next/right page */ + rightkey = bt_right_page_check_scankey(state); + + if (rightkey && + !invariant_geq_offset(state, rightkey, max)) + { + /* + * As explained at length in bt_right_page_check_scankey(), + * there is a known !readonly race that could account for + * apparent violation of invariant, which we must check for + * before actually proceeding with raising error. Our canary + * condition is that target page was deleted. + */ + if (!state->readonly) + { + /* Get fresh copy of target page */ + state->target = palloc_btree_page(state, state->targetblock); + /* Note that we deliberately do not update target LSN */ + topaque = (BTPageOpaque) PageGetSpecialPointer(state->target); + + /* + * All !readonly checks now performed; just return + */ + if (P_IGNORE(topaque)) + return; + } + + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("cross page item order invariant violated for index \"%s\"", + RelationGetRelationName(state->rel)), + errdetail_internal("Last item on page tid=(%u,%u) page lsn=%X/%X.", + state->targetblock, offset, + (uint32) (state->targetlsn >> 32), + (uint32) state->targetlsn))); + } + } + + /* + * * Downlink check * + * + * Additional check of child items iff this is an internal page and + * caller holds a ShareLock. This happens for every downlink (item) + * in target excluding the negative-infinity downlink (again, this is + * because it has no useful value to compare). + */ + if (!P_ISLEAF(topaque) && state->readonly) + { + BlockNumber childblock = ItemPointerGetBlockNumber(&(itup->t_tid)); + + bt_downlink_check(state, childblock, skey); + } + } +} + +/* + * Return a scankey for an item on page to right of current target (or the + * first non-ignorable page), sufficient to check ordering invariant on last + * item in current target page. Returned scankey relies on local memory + * allocated for the child page, which caller cannot pfree(). Caller's memory + * context should be reset between calls here. + * + * This is the first data item, and so all adjacent items are checked against + * their immediate sibling item (which may be on a sibling page, or even a + * "cousin" page at parent boundaries where target's rightlink points to page + * with different parent page). If no such valid item is available, return + * NULL instead. + * + * Note that !readonly callers must reverify that target page has not + * been concurrently deleted. + */ +static ScanKey +bt_right_page_check_scankey(BtreeCheckState *state) +{ + BTPageOpaque opaque; + ItemId rightitem; + BlockNumber targetnext; + Page rightpage; + OffsetNumber nline; + + /* Determine target's next block number */ + opaque = (BTPageOpaque) PageGetSpecialPointer(state->target); + + /* If target is already rightmost, no right sibling; nothing to do here */ + if (P_RIGHTMOST(opaque)) + return NULL; + + /* + * General notes on concurrent page splits and page deletion: + * + * Routines like _bt_search() don't require *any* page split interlock + * when descending the tree, including something very light like a buffer + * pin. That's why it's okay that we don't either. This avoidance of any + * need to "couple" buffer locks is the raison d' etre of the Lehman & Yao + * algorithm, in fact. + * + * That leaves deletion. A deleted page won't actually be recycled by + * VACUUM early enough for us to fail to at least follow its right link + * (or left link, or downlink) and find its sibling, because recycling + * does not occur until no possible index scan could land on the page. + * Index scans can follow links with nothing more than their snapshot as + * an interlock and be sure of at least that much. (See page + * recycling/RecentGlobalXmin notes in nbtree README.) + * + * Furthermore, it's okay if we follow a rightlink and find a half-dead or + * dead (ignorable) page one or more times. There will either be a + * further right link to follow that leads to a live page before too long + * (before passing by parent's rightmost child), or we will find the end + * of the entire level instead (possible when parent page is itself the + * rightmost on its level). + */ + targetnext = opaque->btpo_next; + for (;;) + { + CHECK_FOR_INTERRUPTS(); + + rightpage = palloc_btree_page(state, targetnext); + opaque = (BTPageOpaque) PageGetSpecialPointer(rightpage); + + if (!P_IGNORE(opaque) || P_RIGHTMOST(opaque)) + break; + + /* We landed on a deleted page, so step right to find a live page */ + targetnext = opaque->btpo_next; + ereport(DEBUG1, + (errcode(ERRCODE_NO_DATA), + errmsg("level %u leftmost page of index \"%s\" was found deleted or half dead", + opaque->btpo.level, RelationGetRelationName(state->rel)), + errdetail_internal("Deleted page found when building scankey from right sibling."))); + + /* Be slightly more pro-active in freeing this memory, just in case */ + pfree(rightpage); + } + + /* + * No ShareLock held case -- why it's safe to proceed. + * + * Problem: + * + * We must avoid false positive reports of corruption when caller treats + * item returned here as an upper bound on target's last item. In + * general, false positives are disallowed. Avoiding them here when + * caller is !readonly is subtle. + * + * A concurrent page deletion by VACUUM of the target page can result in + * the insertion of items on to this right sibling page that would + * previously have been inserted on our target page. There might have + * been insertions that followed the target's downlink after it was made + * to point to right sibling instead of target by page deletion's first + * phase. The inserters insert items that would belong on target page. + * This race is very tight, but it's possible. This is our only problem. + * + * Non-problems: + * + * We are not hindered by a concurrent page split of the target; we'll + * never land on the second half of the page anyway. A concurrent split + * of the right page will also not matter, because the first data item + * remains the same within the left half, which we'll reliably land on. If + * we had to skip over ignorable/deleted pages, it cannot matter because + * their key space has already been atomically merged with the first + * non-ignorable page we eventually find (doesn't matter whether the page + * we eventually find is a true sibling or a cousin of target, which we go + * into below). + * + * Solution: + * + * Caller knows that it should reverify that target is not ignorable + * (half-dead or deleted) when cross-page sibling item comparison appears + * to indicate corruption (invariant fails). This detects the single race + * condition that exists for caller. This is correct because the + * continued existence of target block as non-ignorable (not half-dead or + * deleted) implies that target page was not merged into from the right by + * deletion; the key space at or after target never moved left. Target's + * parent either has the same downlink to target as before, or a <= + * downlink due to deletion at the left of target. Target either has the + * same highkey as before, or a highkey <= before when there is a page + * split. (The rightmost concurrently-split-from-target-page page will + * still have the same highkey as target was originally found to have, + * which for our purposes is equivalent to target's highkey itself never + * changing, since we reliably skip over + * concurrently-split-from-target-page pages.) + * + * In simpler terms, we allow that the key space of the target may expand + * left (the key space can move left on the left side of target only), but + * the target key space cannot expand right and get ahead of us without + * our detecting it. The key space of the target cannot shrink, unless it + * shrinks to zero due to the deletion of the original page, our canary + * condition. (To be very precise, we're a bit stricter than that because + * it might just have been that the target page split and only the + * original target page was deleted. We can be more strict, just not more + * lax.) + * + * Top level tree walk caller moves on to next page (makes it the new + * target) following recovery from this race. (cf. The rationale for + * child/downlink verification needing a ShareLock within + * bt_downlink_check(), where page deletion is also the main source of + * trouble.) + * + * Note that it doesn't matter if right sibling page here is actually a + * cousin page, because in order for the key space to be readjusted in a + * way that causes us issues in next level up (guiding problematic + * concurrent insertions to the cousin from the grandparent rather than to + * the sibling from the parent), there'd have to be page deletion of + * target's parent page (affecting target's parent's downlink in target's + * grandparent page). Internal page deletion only occurs when there are + * no child pages (they were all fully deleted), and caller is checking + * that the target's parent has at least one non-deleted (so + * non-ignorable) child: the target page. (Note that the first phase of + * deletion atomically marks the page to be deleted half-dead/ignorable at + * the same time downlink in its parent is removed, so caller will + * definitely not fail to detect that this happened.) + * + * This trick is inspired by the method backward scans use for dealing + * with concurrent page splits; concurrent page deletion is a problem that + * similarly receives special consideration sometimes (it's possible that + * the backwards scan will re-read its "original" block after failing to + * find a right-link to it, having already moved in the opposite direction + * (right/"forwards") a few times to try to locate one). Just like us, + * that happens only to determine if there was a concurrent page deletion + * of a reference page, and just like us if there was a page deletion of + * that reference page it means we can move on from caring about the + * reference page. See the nbtree README for a full description of how + * that works. + */ + nline = PageGetMaxOffsetNumber(rightpage); + + /* + * Get first data item, if any + */ + if (P_ISLEAF(opaque) && nline >= P_FIRSTDATAKEY(opaque)) + { + /* Return first data item (if any) */ + rightitem = PageGetItemId(rightpage, P_FIRSTDATAKEY(opaque)); + } + else if (!P_ISLEAF(opaque) && + nline >= OffsetNumberNext(P_FIRSTDATAKEY(opaque))) + { + /* + * Return first item after the internal page's "negative infinity" + * item + */ + rightitem = PageGetItemId(rightpage, + OffsetNumberNext(P_FIRSTDATAKEY(opaque))); + } + else + { + /* + * No first item. Page is probably empty leaf page, but it's also + * possible that it's an internal page with only a negative infinity + * item. + */ + ereport(DEBUG1, + (errcode(ERRCODE_NO_DATA), + errmsg("%s block %u of index \"%s\" has no first data item", + P_ISLEAF(opaque) ? "leaf" : "internal", targetnext, + RelationGetRelationName(state->rel)))); + return NULL; + } + + /* + * Return first real item scankey. Note that this relies on right page + * memory remaining allocated. + */ + return _bt_mkscankey(state->rel, + (IndexTuple) PageGetItem(rightpage, rightitem)); +} + +/* + * Checks one of target's downlink against its child page. + * + * Conceptually, the target page continues to be what is checked here. The + * target block is still blamed in the event of finding an invariant violation. + * The downlink insertion into the target is probably where any problem raised + * here arises, and there is no such thing as a parent link, so doing the + * verification this way around is much more practical. + */ +static void +bt_downlink_check(BtreeCheckState *state, BlockNumber childblock, + ScanKey targetkey) +{ + OffsetNumber offset; + OffsetNumber maxoffset; + Page child; + BTPageOpaque copaque; + + /* + * Caller must have ShareLock on target relation, because of + * considerations around page deletion by VACUUM. + * + * NB: In general, page deletion deletes the right sibling's downlink, not + * the downlink of the page being deleted; the deleted page's downlink is + * reused for its sibling. The key space is thereby consolidated between + * the deleted page and its right sibling. (We cannot delete a parent + * page's rightmost child unless it is the last child page, and we intend + * to also delete the parent itself.) + * + * If this verification happened without a ShareLock, the following race + * condition could cause false positives: + * + * In general, concurrent page deletion might occur, including deletion of + * the left sibling of the child page that is examined here. If such a + * page deletion were to occur, closely followed by an insertion into the + * newly expanded key space of the child, a window for the false positive + * opens up: the stale parent/target downlink originally followed to get + * to the child legitimately ceases to be a lower bound on all items in + * the page, since the key space was concurrently expanded "left". + * (Insertion followed the "new" downlink for the child, not our now-stale + * downlink, which was concurrently physically removed in target/parent as + * part of deletion's first phase.) + * + * Note that while the cross-page-same-level last item check uses a trick + * that allows it to perform verification for !readonly callers, a similar + * trick seems difficult here. The trick that that other check uses is, + * in essence, to lock down race conditions to those that occur due to + * concurrent page deletion of the target; that's a race that can be + * reliably detected before actually reporting corruption. + * + * On the other hand, we'd need to lock down race conditions involving + * deletion of child's left page, for long enough to read the child page + * into memory (in other words, a scheme with concurrently held buffer + * locks on both child and left-of-child pages). That's unacceptable for + * amcheck functions on general principle, though. + */ + Assert(state->readonly); + + /* + * Verify child page has the downlink key from target page (its parent) as + * a lower bound. + * + * Check all items, rather than checking just the first and trusting that + * the operator class obeys the transitive law. + */ + child = palloc_btree_page(state, childblock); + copaque = (BTPageOpaque) PageGetSpecialPointer(child); + maxoffset = PageGetMaxOffsetNumber(child); + + for (offset = P_FIRSTDATAKEY(copaque); + offset <= maxoffset; + offset = OffsetNumberNext(offset)) + { + /* + * Skip comparison of target page key against "negative infinity" + * item, if any. Checking it would indicate that it's not an upper + * bound, but that's only because of the hard-coding within + * _bt_compare(). + */ + if (offset_is_negative_infinity(copaque, offset)) + continue; + + if (!invariant_leq_nontarget_offset(state, child, + targetkey, offset)) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("down-link lower bound invariant violated for index \"%s\"", + RelationGetRelationName(state->rel)), + errdetail_internal("Parent block=%u child index tid=(%u,%u) parent page lsn=%X/%X.", + state->targetblock, childblock, offset, + (uint32) (state->targetlsn >> 32), + (uint32) state->targetlsn))); + } + + pfree(child); +} + +/* + * Is particular offset within page (whose special state is passed by caller) + * the page negative-infinity item? + * + * As noted in comments above _bt_compare(), there is special handling of the + * first data item as a "negative infinity" item. The hard-coding within + * _bt_compare() makes comparing this item for the purposes of verification + * pointless at best, since the IndexTuple only contains a valid TID (a + * reference TID to child page). + */ +static inline bool +offset_is_negative_infinity(BTPageOpaque opaque, OffsetNumber offset) +{ + /* + * For internal pages only, the first item after high key, if any, is + * negative infinity item. Internal pages always have a negative infinity + * item, whereas leaf pages never have one. This implies that negative + * infinity item is either first or second line item, or there is none + * within page. + * + * Right-most pages don't have a high key, but could be said to + * conceptually have a "positive infinity" high key. Thus, there is a + * symmetry between down link items in parent pages, and high keys in + * children. Together, they represent the part of the key space that + * belongs to each page in the index. For example, all children of the + * root page will have negative infinity as a lower bound from root + * negative infinity downlink, and positive infinity as an upper bound + * (implicitly, from "imaginary" positive infinity high key in root). + */ + return !P_ISLEAF(opaque) && offset == P_FIRSTDATAKEY(opaque); +} + +/* + * Does the invariant hold that the key is less than or equal to a given upper + * bound offset item? + * + * If this function returns false, convention is that caller throws error due + * to corruption. + */ +static inline bool +invariant_leq_offset(BtreeCheckState *state, ScanKey key, + OffsetNumber upperbound) +{ + int16 natts = state->rel->rd_rel->relnatts; + int32 cmp; + + cmp = _bt_compare(state->rel, natts, key, state->target, upperbound); + + return cmp <= 0; +} + +/* + * Does the invariant hold that the key is greater than or equal to a given + * lower bound offset item? + * + * If this function returns false, convention is that caller throws error due + * to corruption. + */ +static inline bool +invariant_geq_offset(BtreeCheckState *state, ScanKey key, + OffsetNumber lowerbound) +{ + int16 natts = state->rel->rd_rel->relnatts; + int32 cmp; + + cmp = _bt_compare(state->rel, natts, key, state->target, lowerbound); + + return cmp >= 0; +} + +/* + * Does the invariant hold that the key is less than or equal to a given upper + * bound offset item, with the offset relating to a caller-supplied page that + * is not the current target page? Caller's non-target page is typically a + * child page of the target, checked as part of checking a property of the + * target page (i.e. the key comes from the target). + * + * If this function returns false, convention is that caller throws error due + * to corruption. + */ +static inline bool +invariant_leq_nontarget_offset(BtreeCheckState *state, + Page nontarget, ScanKey key, + OffsetNumber upperbound) +{ + int16 natts = state->rel->rd_rel->relnatts; + int32 cmp; + + cmp = _bt_compare(state->rel, natts, key, nontarget, upperbound); + + return cmp <= 0; +} + +/* + * Given a block number of a B-Tree page, return page in palloc()'d memory. + * While at it, perform some basic checks of the page. + * + * There is never an attempt to get a consistent view of multiple pages using + * multiple concurrent buffer locks; in general, we only acquire a single pin + * and buffer lock at a time, which is often all that the nbtree code requires. + * + * Operating on a copy of the page is useful because it prevents control + * getting stuck in an uninterruptible state when an underlying operator class + * misbehaves. + */ +static Page +palloc_btree_page(BtreeCheckState *state, BlockNumber blocknum) +{ + Buffer buffer; + Page page; + BTPageOpaque opaque; + + page = palloc(BLCKSZ); + + /* + * We copy the page into local storage to avoid holding pin on the buffer + * longer than we must. + */ + buffer = ReadBufferExtended(state->rel, MAIN_FORKNUM, blocknum, RBM_NORMAL, + state->checkstrategy); + LockBuffer(buffer, BT_READ); + + /* + * Perform the same basic sanity checking that nbtree itself performs for + * every page: + */ + _bt_checkpage(state->rel, buffer); + + /* Only use copy of page in palloc()'d memory */ + memcpy(page, BufferGetPage(buffer), BLCKSZ); + UnlockReleaseBuffer(buffer); + + opaque = (BTPageOpaque) PageGetSpecialPointer(page); + + if (opaque->btpo_flags & BTP_META && blocknum != BTREE_METAPAGE) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalid meta page found at block %u in index \"%s\"", + blocknum, RelationGetRelationName(state->rel)))); + + /* Check page from block that ought to be meta page */ + if (blocknum == BTREE_METAPAGE) + { + BTMetaPageData *metad = BTPageGetMeta(page); + + if (!(opaque->btpo_flags & BTP_META) || + metad->btm_magic != BTREE_MAGIC) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("index \"%s\" meta page is corrupt", + RelationGetRelationName(state->rel)))); + + if (metad->btm_version != BTREE_VERSION) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("version mismatch in index \"%s\": file version %d, code version %d", + RelationGetRelationName(state->rel), + metad->btm_version, BTREE_VERSION))); + } + + /* + * Deleted pages have no sane "level" field, so can only check non-deleted + * page level + */ + if (P_ISLEAF(opaque) && !P_ISDELETED(opaque) && opaque->btpo.level != 0) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalid leaf page level %u for block %u in index \"%s\"", + opaque->btpo.level, blocknum, RelationGetRelationName(state->rel)))); + + if (blocknum != BTREE_METAPAGE && !P_ISLEAF(opaque) && + !P_ISDELETED(opaque) && opaque->btpo.level == 0) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalid internal page level 0 for block %u in index \"%s\"", + opaque->btpo.level, RelationGetRelationName(state->rel)))); + + if (!P_ISLEAF(opaque) && P_HAS_GARBAGE(opaque)) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("internal page block %u in index \"%s\" has garbage items", + blocknum, RelationGetRelationName(state->rel)))); + + return page; +} diff --git a/contrib/auth_delay/auth_delay.c b/contrib/auth_delay/auth_delay.c index 86f3aa0b7b..e0604fb808 100644 --- a/contrib/auth_delay/auth_delay.c +++ b/contrib/auth_delay/auth_delay.c @@ -2,7 +2,7 @@ * * auth_delay.c * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/auth_delay/auth_delay.c @@ -11,6 +11,8 @@ */ #include "postgres.h" +#include <limits.h> + #include "libpq/auth.h" #include "port.h" #include "utils/guc.h" diff --git a/contrib/auto_explain/auto_explain.c b/contrib/auto_explain/auto_explain.c index 4ccd2aa849..9213ffb6a4 100644 --- a/contrib/auto_explain/auto_explain.c +++ b/contrib/auto_explain/auto_explain.c @@ -3,7 +3,7 @@ * auto_explain.c * * - * Copyright (c) 2008-2016, PostgreSQL Global Development Group + * Copyright (c) 2008-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/auto_explain/auto_explain.c @@ -61,7 +61,7 @@ void _PG_fini(void); static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags); static void explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, - uint64 count); + uint64 count, bool execute_once); static void explain_ExecutorFinish(QueryDesc *queryDesc); static void explain_ExecutorEnd(QueryDesc *queryDesc); @@ -257,15 +257,16 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags) * ExecutorRun hook: all we need do is track nesting depth */ static void -explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count) +explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, + uint64 count, bool execute_once) { nesting_level++; PG_TRY(); { if (prev_ExecutorRun) - prev_ExecutorRun(queryDesc, direction, count); + prev_ExecutorRun(queryDesc, direction, count, execute_once); else - standard_ExecutorRun(queryDesc, direction, count); + standard_ExecutorRun(queryDesc, direction, count, execute_once); nesting_level--; } PG_CATCH(); diff --git a/contrib/bloom/blcost.c b/contrib/bloom/blcost.c index 989789850e..ba39f627fd 100644 --- a/contrib/bloom/blcost.c +++ b/contrib/bloom/blcost.c @@ -3,7 +3,7 @@ * blcost.c * Cost estimate function for bloom indexes. * - * Copyright (c) 2016, PostgreSQL Global Development Group + * Copyright (c) 2016-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/bloom/blcost.c @@ -24,7 +24,8 @@ void blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, - Selectivity *indexSelectivity, double *indexCorrelation) + Selectivity *indexSelectivity, double *indexCorrelation, + double *indexPages) { IndexOptInfo *index = path->indexinfo; List *qinfos; @@ -45,4 +46,5 @@ blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, *indexTotalCost = costs.indexTotalCost; *indexSelectivity = costs.indexSelectivity; *indexCorrelation = costs.indexCorrelation; + *indexPages = costs.numIndexPages; } diff --git a/contrib/bloom/blinsert.c b/contrib/bloom/blinsert.c index 78eec5c67e..0d506e3c1a 100644 --- a/contrib/bloom/blinsert.c +++ b/contrib/bloom/blinsert.c @@ -3,7 +3,7 @@ * blinsert.c * Bloom index build and insert functions. * - * Copyright (c) 2016, PostgreSQL Global Development Group + * Copyright (c) 2016-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/bloom/blinsert.c @@ -130,9 +130,7 @@ blbuild(Relation heap, Relation index, IndexInfo *indexInfo) initBloomState(&buildstate.blstate, index); buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext, "Bloom build temporary context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); initCachedPage(&buildstate); /* Do the heap scan */ @@ -166,13 +164,18 @@ blbuildempty(Relation index) metapage = (Page) palloc(BLCKSZ); BloomFillMetapage(index, metapage); - /* Write the page. If archiving/streaming, XLOG it. */ + /* + * Write the page and log it. It might seem that an immediate sync would + * be sufficient to guarantee that the file exists on disk, but recovery + * itself might remove it while replaying, for example, an + * XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record. Therefore, we need + * this even when wal_level=minimal. + */ PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO); smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO, (char *) metapage, true); - if (XLogIsNeeded()) - log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, - BLOOM_METAPAGE_BLKNO, metapage, false); + log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM, + BLOOM_METAPAGE_BLKNO, metapage, false); /* * An immediate sync is required even if we xlog'd the page, because the @@ -187,7 +190,9 @@ blbuildempty(Relation index) */ bool blinsert(Relation index, Datum *values, bool *isnull, - ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique) + ItemPointer ht_ctid, Relation heapRel, + IndexUniqueCheck checkUnique, + IndexInfo *indexInfo) { BloomState blstate; BloomTuple *itup; @@ -204,9 +209,7 @@ blinsert(Relation index, Datum *values, bool *isnull, insertCtx = AllocSetContextCreate(CurrentMemoryContext, "Bloom insert temporary context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); oldCtx = MemoryContextSwitchTo(insertCtx); diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h index bc451a00db..0cfe49aad8 100644 --- a/contrib/bloom/bloom.h +++ b/contrib/bloom/bloom.h @@ -3,7 +3,7 @@ * bloom.h * Header for bloom index. * - * Copyright (c) 2016, PostgreSQL Global Development Group + * Copyright (c) 2016-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/bloom/bloom.h @@ -174,7 +174,6 @@ typedef BloomScanOpaqueData *BloomScanOpaque; /* blutils.c */ extern void _PG_init(void); -extern Datum blhandler(PG_FUNCTION_ARGS); extern void initBloomState(BloomState *state, Relation index); extern void BloomFillMetapage(Relation index, Page metaPage); extern void BloomInitMetapage(Relation index); @@ -190,7 +189,8 @@ extern bool blvalidate(Oid opclassoid); /* index access method interface functions */ extern bool blinsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, - IndexUniqueCheck checkUnique); + IndexUniqueCheck checkUnique, + struct IndexInfo *indexInfo); extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys); extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm); extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys, @@ -208,6 +208,6 @@ extern bytea *bloptions(Datum reloptions, bool validate); extern void blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, Cost *indexStartupCost, Cost *indexTotalCost, Selectivity *indexSelectivity, - double *indexCorrelation); + double *indexCorrelation, double *indexPages); #endif diff --git a/contrib/bloom/blscan.c b/contrib/bloom/blscan.c index 316a906734..b8fa2d0a71 100644 --- a/contrib/bloom/blscan.c +++ b/contrib/bloom/blscan.c @@ -3,7 +3,7 @@ * blscan.c * Bloom index scan functions. * - * Copyright (c) 2016, PostgreSQL Global Development Group + * Copyright (c) 2016-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/bloom/blscan.c diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c index debf4f46eb..00a65875b0 100644 --- a/contrib/bloom/blutils.c +++ b/contrib/bloom/blutils.c @@ -3,7 +3,7 @@ * blutils.c * Bloom index utilities. * - * Portions Copyright (c) 2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2016-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1990-1993, Regents of the University of California * * IDENTIFICATION @@ -75,7 +75,7 @@ _PG_init(void) bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext, buf); bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT; - bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[i]); + bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) +sizeof(int) * i; } } @@ -119,6 +119,7 @@ blhandler(PG_FUNCTION_ARGS) amroutine->amstorage = false; amroutine->amclusterable = false; amroutine->ampredlocks = false; + amroutine->amcanparallel = false; amroutine->amkeytype = InvalidOid; amroutine->ambuild = blbuild; @@ -138,6 +139,9 @@ blhandler(PG_FUNCTION_ARGS) amroutine->amendscan = blendscan; amroutine->ammarkpos = NULL; amroutine->amrestrpos = NULL; + amroutine->amestimateparallelscan = NULL; + amroutine->aminitparallelscan = NULL; + amroutine->amparallelrescan = NULL; PG_RETURN_POINTER(amroutine); } diff --git a/contrib/bloom/blvacuum.c b/contrib/bloom/blvacuum.c index 482242f1c2..04abd0f6b6 100644 --- a/contrib/bloom/blvacuum.c +++ b/contrib/bloom/blvacuum.c @@ -3,7 +3,7 @@ * blvacuum.c * Bloom VACUUM functions. * - * Copyright (c) 2016, PostgreSQL Global Development Group + * Copyright (c) 2016-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/bloom/blvacuum.c @@ -51,7 +51,7 @@ blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, initBloomState(&state, index); /* - * Interate over the pages. We don't care about concurrently added pages, + * Iterate over the pages. We don't care about concurrently added pages, * they can't contain tuples to delete. */ npages = RelationGetNumberOfBlocks(index); diff --git a/contrib/bloom/blvalidate.c b/contrib/bloom/blvalidate.c index 12e7c7dbda..cb75d23199 100644 --- a/contrib/bloom/blvalidate.c +++ b/contrib/bloom/blvalidate.c @@ -3,7 +3,7 @@ * blvalidate.c * Opclass validator for bloom. * - * Copyright (c) 2016, PostgreSQL Global Development Group + * Copyright (c) 2016-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/bloom/blvalidate.c @@ -21,6 +21,7 @@ #include "catalog/pg_type.h" #include "utils/builtins.h" #include "utils/lsyscache.h" +#include "utils/regproc.h" #include "utils/syscache.h" #include "bloom.h" diff --git a/contrib/bloom/t/001_wal.pl b/contrib/bloom/t/001_wal.pl index 56c6618d3d..dbba198254 100644 --- a/contrib/bloom/t/001_wal.pl +++ b/contrib/bloom/t/001_wal.pl @@ -16,7 +16,7 @@ sub test_index_replay # Wait for standby to catch up my $applname = $node_standby->name; my $caughtup_query = -"SELECT pg_current_xlog_location() <= write_location FROM pg_stat_replication WHERE application_name = '$applname';"; +"SELECT pg_current_wal_lsn() <= write_lsn FROM pg_stat_replication WHERE application_name = '$applname';"; $node_master->poll_query_until('postgres', $caughtup_query) or die "Timed out while waiting for standby 1 to catch up"; diff --git a/contrib/btree_gin/Makefile b/contrib/btree_gin/Makefile index 0492091599..690e1d7602 100644 --- a/contrib/btree_gin/Makefile +++ b/contrib/btree_gin/Makefile @@ -4,13 +4,14 @@ MODULE_big = btree_gin OBJS = btree_gin.o $(WIN32RES) EXTENSION = btree_gin -DATA = btree_gin--1.0.sql btree_gin--unpackaged--1.0.sql +DATA = btree_gin--1.0.sql btree_gin--1.0--1.1.sql btree_gin--1.1--1.2.sql \ + btree_gin--unpackaged--1.0.sql PGFILEDESC = "btree_gin - B-tree equivalent GIN operator classes" REGRESS = install_btree_gin int2 int4 int8 float4 float8 money oid \ timestamp timestamptz time timetz date interval \ - macaddr inet cidr text varchar char bytea bit varbit \ - numeric + macaddr macaddr8 inet cidr text varchar char bytea bit varbit \ + numeric enum ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/btree_gin/btree_gin--1.0--1.1.sql b/contrib/btree_gin/btree_gin--1.0--1.1.sql new file mode 100644 index 0000000000..dd81d27599 --- /dev/null +++ b/contrib/btree_gin/btree_gin--1.0--1.1.sql @@ -0,0 +1,35 @@ +/* contrib/btree_gin/btree_gin--1.0--1.1.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.1'" to load this file. \quit + +-- macaddr8 datatype support new in 10.0. +CREATE FUNCTION gin_extract_value_macaddr8(macaddr8, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_compare_prefix_macaddr8(macaddr8, macaddr8, int2, internal) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_extract_query_macaddr8(macaddr8, internal, int2, internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR CLASS macaddr8_ops +DEFAULT FOR TYPE macaddr8 USING gin +AS + OPERATOR 1 <, + OPERATOR 2 <=, + OPERATOR 3 =, + OPERATOR 4 >=, + OPERATOR 5 >, + FUNCTION 1 macaddr8_cmp(macaddr8, macaddr8), + FUNCTION 2 gin_extract_value_macaddr8(macaddr8, internal), + FUNCTION 3 gin_extract_query_macaddr8(macaddr8, internal, int2, internal, internal), + FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal), + FUNCTION 5 gin_compare_prefix_macaddr8(macaddr8, macaddr8, int2, internal), +STORAGE macaddr8; diff --git a/contrib/btree_gin/btree_gin--1.1--1.2.sql b/contrib/btree_gin/btree_gin--1.1--1.2.sql new file mode 100644 index 0000000000..2a16837bfa --- /dev/null +++ b/contrib/btree_gin/btree_gin--1.1--1.2.sql @@ -0,0 +1,47 @@ +/* contrib/btree_gin/btree_gin--1.1--1.2.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.1'" to load this file. \quit + +-- +-- +-- +-- enum ops +-- +-- + + +CREATE FUNCTION gin_extract_value_anyenum(anyenum, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_compare_prefix_anyenum(anyenum, anyenum, int2, internal) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_extract_query_anyenum(anyenum, internal, int2, internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE FUNCTION gin_enum_cmp(anyenum, anyenum) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR CLASS enum_ops +DEFAULT FOR TYPE anyenum USING gin +AS + OPERATOR 1 <, + OPERATOR 2 <=, + OPERATOR 3 =, + OPERATOR 4 >=, + OPERATOR 5 >, + FUNCTION 1 gin_enum_cmp(anyenum,anyenum), + FUNCTION 2 gin_extract_value_anyenum(anyenum, internal), + FUNCTION 3 gin_extract_query_anyenum(anyenum, internal, int2, internal, internal), + FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal), + FUNCTION 5 gin_compare_prefix_anyenum(anyenum,anyenum,int2, internal), +STORAGE anyenum; diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c index 030b61097f..6f0c752b2e 100644 --- a/contrib/btree_gin/btree_gin.c +++ b/contrib/btree_gin/btree_gin.c @@ -25,7 +25,6 @@ typedef struct QueryInfo Datum (*typecmp) (FunctionCallInfo); } QueryInfo; - /*** GIN support functions shared by all datatypes ***/ static Datum @@ -112,13 +111,14 @@ gin_btree_compare_prefix(FunctionCallInfo fcinfo) int32 res, cmp; - cmp = DatumGetInt32(DirectFunctionCall2Coll( - data->typecmp, - PG_GET_COLLATION(), + cmp = DatumGetInt32(CallerFInfoFunctionCall2( + data->typecmp, + fcinfo->flinfo, + PG_GET_COLLATION(), (data->strategy == BTLessStrategyNumber || data->strategy == BTLessEqualStrategyNumber) - ? data->datum : a, - b)); + ? data->datum : a, + b)); switch (data->strategy) { @@ -323,6 +323,16 @@ leftmostvalue_macaddr(void) GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp) static Datum +leftmostvalue_macaddr8(void) +{ + macaddr8 *v = palloc0(sizeof(macaddr8)); + + return Macaddr8PGetDatum(v); +} + +GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp) + +static Datum leftmostvalue_inet(void) { return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0")); @@ -416,3 +426,54 @@ leftmostvalue_numeric(void) } GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp) + +/* + * Use a similar trick to that used for numeric for enums, since we don't + * actually know the leftmost value of any enum without knowing the concrete + * type, so we use a dummy leftmost value of InvalidOid. + * + * Note that we use CallerFInfoFunctionCall2 here so that enum_cmp + * gets a valid fn_extra to work with. Unlike most other type comparison + * routines it needs it, so we can't use DirectFunctionCall2. + */ + + +#define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid) + +PG_FUNCTION_INFO_V1(gin_enum_cmp); + +Datum +gin_enum_cmp(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + int res = 0; + + if (ENUM_IS_LEFTMOST(a)) + { + res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1; + } + else if (ENUM_IS_LEFTMOST(b)) + { + res = 1; + } + else + { + res = DatumGetInt32(CallerFInfoFunctionCall2( + enum_cmp, + fcinfo->flinfo, + PG_GET_COLLATION(), + ObjectIdGetDatum(a), + ObjectIdGetDatum(b))); + } + + PG_RETURN_INT32(res); +} + +static Datum +leftmostvalue_enum(void) +{ + return ObjectIdGetDatum(InvalidOid); +} + +GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp) diff --git a/contrib/btree_gin/btree_gin.control b/contrib/btree_gin/btree_gin.control index 3b2cb2d709..3acc5af1a7 100644 --- a/contrib/btree_gin/btree_gin.control +++ b/contrib/btree_gin/btree_gin.control @@ -1,5 +1,5 @@ # btree_gin extension comment = 'support for indexing common datatypes in GIN' -default_version = '1.0' +default_version = '1.2' module_pathname = '$libdir/btree_gin' relocatable = true diff --git a/contrib/btree_gin/expected/enum.out b/contrib/btree_gin/expected/enum.out new file mode 100644 index 0000000000..71e0c4bf40 --- /dev/null +++ b/contrib/btree_gin/expected/enum.out @@ -0,0 +1,63 @@ +set enable_seqscan=off; +CREATE TYPE rainbow AS ENUM ('r','o','y','g','b','i','v'); +CREATE TABLE test_enum ( + i rainbow +); +INSERT INTO test_enum VALUES ('v'),('y'),('r'),('g'),('o'),('i'),('b'); +CREATE INDEX idx_enum ON test_enum USING gin (i); +SELECT * FROM test_enum WHERE i<'g'::rainbow ORDER BY i; + i +--- + r + o + y +(3 rows) + +SELECT * FROM test_enum WHERE i<='g'::rainbow ORDER BY i; + i +--- + r + o + y + g +(4 rows) + +SELECT * FROM test_enum WHERE i='g'::rainbow ORDER BY i; + i +--- + g +(1 row) + +SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; + i +--- + g + b + i + v +(4 rows) + +SELECT * FROM test_enum WHERE i>'g'::rainbow ORDER BY i; + i +--- + b + i + v +(3 rows) + +explain (costs off) SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; + QUERY PLAN +----------------------------------------------- + Sort + Sort Key: i + -> Bitmap Heap Scan on test_enum + Recheck Cond: (i >= 'g'::rainbow) + -> Bitmap Index Scan on idx_enum + Index Cond: (i >= 'g'::rainbow) +(6 rows) + +-- make sure we handle the non-evenly-numbered oid case for enums +create type e as enum ('0', '2', '3'); +alter type e add value '1' after '0'; +create table t as select (i % 4)::text::e from generate_series(0, 100000) as i; +create index on t using gin (e); diff --git a/contrib/btree_gin/expected/install_btree_gin.out b/contrib/btree_gin/expected/install_btree_gin.out index 0fae4c5bfe..631a0df722 100644 --- a/contrib/btree_gin/expected/install_btree_gin.out +++ b/contrib/btree_gin/expected/install_btree_gin.out @@ -1 +1,9 @@ CREATE EXTENSION btree_gin; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + diff --git a/contrib/btree_gin/expected/macaddr8.out b/contrib/btree_gin/expected/macaddr8.out new file mode 100644 index 0000000000..025b0c171e --- /dev/null +++ b/contrib/btree_gin/expected/macaddr8.out @@ -0,0 +1,51 @@ +set enable_seqscan=off; +CREATE TABLE test_macaddr8 ( + i macaddr8 +); +INSERT INTO test_macaddr8 VALUES + ( '22:00:5c:03:55:08:01:02' ), + ( '22:00:5c:04:55:08:01:02' ), + ( '22:00:5c:05:55:08:01:02' ), + ( '22:00:5c:08:55:08:01:02' ), + ( '22:00:5c:09:55:08:01:02' ), + ( '22:00:5c:10:55:08:01:02' ) +; +CREATE INDEX idx_macaddr8 ON test_macaddr8 USING gin (i); +SELECT * FROM test_macaddr8 WHERE i<'22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; + i +------------------------- + 22:00:5c:03:55:08:01:02 + 22:00:5c:04:55:08:01:02 + 22:00:5c:05:55:08:01:02 +(3 rows) + +SELECT * FROM test_macaddr8 WHERE i<='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; + i +------------------------- + 22:00:5c:03:55:08:01:02 + 22:00:5c:04:55:08:01:02 + 22:00:5c:05:55:08:01:02 + 22:00:5c:08:55:08:01:02 +(4 rows) + +SELECT * FROM test_macaddr8 WHERE i='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; + i +------------------------- + 22:00:5c:08:55:08:01:02 +(1 row) + +SELECT * FROM test_macaddr8 WHERE i>='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; + i +------------------------- + 22:00:5c:08:55:08:01:02 + 22:00:5c:09:55:08:01:02 + 22:00:5c:10:55:08:01:02 +(3 rows) + +SELECT * FROM test_macaddr8 WHERE i>'22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; + i +------------------------- + 22:00:5c:09:55:08:01:02 + 22:00:5c:10:55:08:01:02 +(2 rows) + diff --git a/contrib/btree_gin/sql/enum.sql b/contrib/btree_gin/sql/enum.sql new file mode 100644 index 0000000000..ec2a622c5a --- /dev/null +++ b/contrib/btree_gin/sql/enum.sql @@ -0,0 +1,26 @@ +set enable_seqscan=off; + +CREATE TYPE rainbow AS ENUM ('r','o','y','g','b','i','v'); + +CREATE TABLE test_enum ( + i rainbow +); + +INSERT INTO test_enum VALUES ('v'),('y'),('r'),('g'),('o'),('i'),('b'); + +CREATE INDEX idx_enum ON test_enum USING gin (i); + +SELECT * FROM test_enum WHERE i<'g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i<='g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i='g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; +SELECT * FROM test_enum WHERE i>'g'::rainbow ORDER BY i; + +explain (costs off) SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i; + + +-- make sure we handle the non-evenly-numbered oid case for enums +create type e as enum ('0', '2', '3'); +alter type e add value '1' after '0'; +create table t as select (i % 4)::text::e from generate_series(0, 100000) as i; +create index on t using gin (e); diff --git a/contrib/btree_gin/sql/install_btree_gin.sql b/contrib/btree_gin/sql/install_btree_gin.sql index 0fae4c5bfe..746e77654f 100644 --- a/contrib/btree_gin/sql/install_btree_gin.sql +++ b/contrib/btree_gin/sql/install_btree_gin.sql @@ -1 +1,6 @@ CREATE EXTENSION btree_gin; + +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); diff --git a/contrib/btree_gin/sql/macaddr8.sql b/contrib/btree_gin/sql/macaddr8.sql new file mode 100644 index 0000000000..86785c3ca9 --- /dev/null +++ b/contrib/btree_gin/sql/macaddr8.sql @@ -0,0 +1,22 @@ +set enable_seqscan=off; + +CREATE TABLE test_macaddr8 ( + i macaddr8 +); + +INSERT INTO test_macaddr8 VALUES + ( '22:00:5c:03:55:08:01:02' ), + ( '22:00:5c:04:55:08:01:02' ), + ( '22:00:5c:05:55:08:01:02' ), + ( '22:00:5c:08:55:08:01:02' ), + ( '22:00:5c:09:55:08:01:02' ), + ( '22:00:5c:10:55:08:01:02' ) +; + +CREATE INDEX idx_macaddr8 ON test_macaddr8 USING gin (i); + +SELECT * FROM test_macaddr8 WHERE i<'22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; +SELECT * FROM test_macaddr8 WHERE i<='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; +SELECT * FROM test_macaddr8 WHERE i='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; +SELECT * FROM test_macaddr8 WHERE i>='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; +SELECT * FROM test_macaddr8 WHERE i>'22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i; diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index 5134f72611..af651205ec 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -5,17 +5,19 @@ MODULE_big = btree_gist OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o \ btree_int4.o btree_int8.o btree_float4.o btree_float8.o btree_cash.o \ btree_oid.o btree_ts.o btree_time.o btree_date.o btree_interval.o \ - btree_macaddr.o btree_inet.o btree_text.o btree_bytea.o btree_bit.o \ - btree_numeric.o $(WIN32RES) + btree_macaddr.o btree_macaddr8.o btree_inet.o btree_text.o \ + btree_bytea.o btree_bit.o btree_numeric.o btree_uuid.o \ + btree_enum.o $(WIN32RES) EXTENSION = btree_gist -DATA = btree_gist--1.2.sql btree_gist--1.1--1.2.sql btree_gist--1.0--1.1.sql \ - btree_gist--unpackaged--1.0.sql +DATA = btree_gist--unpackaged--1.0.sql btree_gist--1.0--1.1.sql \ + btree_gist--1.1--1.2.sql btree_gist--1.2.sql btree_gist--1.2--1.3.sql \ + btree_gist--1.3--1.4.sql btree_gist--1.4--1.5.sql PGFILEDESC = "btree_gist - B-tree equivalent GiST operator classes" REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz \ - time timetz date interval macaddr inet cidr text varchar char bytea \ - bit varbit numeric not_equal + time timetz date interval macaddr macaddr8 inet cidr text varchar char \ + bytea bit varbit numeric uuid not_equal enum SHLIB_LINK += $(filter -lm, $(LIBS)) diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c index af210439f0..a56a2752a7 100644 --- a/contrib/btree_gist/btree_bit.c +++ b/contrib/btree_gist/btree_bit.c @@ -5,6 +5,7 @@ #include "btree_gist.h" #include "btree_utils_var.h" +#include "utils/builtins.h" #include "utils/bytea.h" #include "utils/varbit.h" @@ -23,7 +24,7 @@ PG_FUNCTION_INFO_V1(gbt_bit_same); /* define for comparison */ static bool -gbt_bitgt(const void *a, const void *b, Oid collation) +gbt_bitgt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(bitgt, PointerGetDatum(a), @@ -31,7 +32,7 @@ gbt_bitgt(const void *a, const void *b, Oid collation) } static bool -gbt_bitge(const void *a, const void *b, Oid collation) +gbt_bitge(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(bitge, PointerGetDatum(a), @@ -39,7 +40,7 @@ gbt_bitge(const void *a, const void *b, Oid collation) } static bool -gbt_biteq(const void *a, const void *b, Oid collation) +gbt_biteq(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(biteq, PointerGetDatum(a), @@ -47,7 +48,7 @@ gbt_biteq(const void *a, const void *b, Oid collation) } static bool -gbt_bitle(const void *a, const void *b, Oid collation) +gbt_bitle(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(bitle, PointerGetDatum(a), @@ -55,7 +56,7 @@ gbt_bitle(const void *a, const void *b, Oid collation) } static bool -gbt_bitlt(const void *a, const void *b, Oid collation) +gbt_bitlt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(bitlt, PointerGetDatum(a), @@ -63,7 +64,7 @@ gbt_bitlt(const void *a, const void *b, Oid collation) } static int32 -gbt_bitcmp(const void *a, const void *b, Oid collation) +gbt_bitcmp(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetInt32(DirectFunctionCall2(byteacmp, PointerGetDatum(a), @@ -91,7 +92,7 @@ gbt_bit_xfrm(bytea *leaf) static GBT_VARKEY * -gbt_bit_l2n(GBT_VARKEY *leaf) +gbt_bit_l2n(GBT_VARKEY *leaf, FmgrInfo *flinfo) { GBT_VARKEY *out = leaf; GBT_VARKEY_R r = gbt_var_key_readable(leaf); @@ -151,13 +152,13 @@ gbt_bit_consistent(PG_FUNCTION_ARGS) if (GIST_LEAF(entry)) retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), - TRUE, &tinfo); + TRUE, &tinfo, fcinfo->flinfo); else { bytea *q = gbt_bit_xfrm((bytea *) query); retval = gbt_var_consistent(&r, q, strategy, PG_GET_COLLATION(), - FALSE, &tinfo); + FALSE, &tinfo, fcinfo->flinfo); } PG_RETURN_BOOL(retval); } @@ -171,7 +172,7 @@ gbt_bit_union(PG_FUNCTION_ARGS) int32 *size = (int *) PG_GETARG_POINTER(1); PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } @@ -182,7 +183,7 @@ gbt_bit_picksplit(PG_FUNCTION_ARGS) GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), - &tinfo); + &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } @@ -193,7 +194,7 @@ gbt_bit_same(PG_FUNCTION_ARGS) Datum d2 = PG_GETARG_DATUM(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo); + *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } @@ -206,5 +207,5 @@ gbt_bit_penalty(PG_FUNCTION_ARGS) float *result = (float *) PG_GETARG_POINTER(2); PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c index dfc25a45c6..00753e7f48 100644 --- a/contrib/btree_gist/btree_bytea.c +++ b/contrib/btree_gist/btree_bytea.c @@ -5,6 +5,7 @@ #include "btree_gist.h" #include "btree_utils_var.h" +#include "utils/builtins.h" #include "utils/bytea.h" @@ -22,7 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_bytea_same); /* define for comparison */ static bool -gbt_byteagt(const void *a, const void *b, Oid collation) +gbt_byteagt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(byteagt, PointerGetDatum(a), @@ -30,7 +31,7 @@ gbt_byteagt(const void *a, const void *b, Oid collation) } static bool -gbt_byteage(const void *a, const void *b, Oid collation) +gbt_byteage(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(byteage, PointerGetDatum(a), @@ -38,7 +39,7 @@ gbt_byteage(const void *a, const void *b, Oid collation) } static bool -gbt_byteaeq(const void *a, const void *b, Oid collation) +gbt_byteaeq(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(byteaeq, PointerGetDatum(a), @@ -46,7 +47,7 @@ gbt_byteaeq(const void *a, const void *b, Oid collation) } static bool -gbt_byteale(const void *a, const void *b, Oid collation) +gbt_byteale(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(byteale, PointerGetDatum(a), @@ -54,7 +55,7 @@ gbt_byteale(const void *a, const void *b, Oid collation) } static bool -gbt_bytealt(const void *a, const void *b, Oid collation) +gbt_bytealt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(bytealt, PointerGetDatum(a), @@ -62,7 +63,7 @@ gbt_bytealt(const void *a, const void *b, Oid collation) } static int32 -gbt_byteacmp(const void *a, const void *b, Oid collation) +gbt_byteacmp(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetInt32(DirectFunctionCall2(byteacmp, PointerGetDatum(a), @@ -117,7 +118,7 @@ gbt_bytea_consistent(PG_FUNCTION_ARGS) *recheck = false; retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), - GIST_LEAF(entry), &tinfo); + GIST_LEAF(entry), &tinfo, fcinfo->flinfo); PG_RETURN_BOOL(retval); } @@ -130,7 +131,7 @@ gbt_bytea_union(PG_FUNCTION_ARGS) int32 *size = (int *) PG_GETARG_POINTER(1); PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } @@ -141,7 +142,7 @@ gbt_bytea_picksplit(PG_FUNCTION_ARGS) GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), - &tinfo); + &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } @@ -152,7 +153,7 @@ gbt_bytea_same(PG_FUNCTION_ARGS) Datum d2 = PG_GETARG_DATUM(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo); + *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } @@ -165,5 +166,5 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS) float *result = (float *) PG_GETARG_POINTER(2); PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } diff --git a/contrib/btree_gist/btree_cash.c b/contrib/btree_gist/btree_cash.c index aa14735338..1116ca084f 100644 --- a/contrib/btree_gist/btree_cash.c +++ b/contrib/btree_gist/btree_cash.c @@ -26,33 +26,33 @@ PG_FUNCTION_INFO_V1(gbt_cash_penalty); PG_FUNCTION_INFO_V1(gbt_cash_same); static bool -gbt_cashgt(const void *a, const void *b) +gbt_cashgt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Cash *) a) > *((const Cash *) b)); } static bool -gbt_cashge(const void *a, const void *b) +gbt_cashge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Cash *) a) >= *((const Cash *) b)); } static bool -gbt_casheq(const void *a, const void *b) +gbt_casheq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Cash *) a) == *((const Cash *) b)); } static bool -gbt_cashle(const void *a, const void *b) +gbt_cashle(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Cash *) a) <= *((const Cash *) b)); } static bool -gbt_cashlt(const void *a, const void *b) +gbt_cashlt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Cash *) a) < *((const Cash *) b)); } static int -gbt_cashkey_cmp(const void *a, const void *b) +gbt_cashkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { cashKEY *ia = (cashKEY *) (((const Nsrt *) a)->t); cashKEY *ib = (cashKEY *) (((const Nsrt *) b)->t); @@ -69,7 +69,7 @@ gbt_cashkey_cmp(const void *a, const void *b) } static float8 -gbt_cash_dist(const void *a, const void *b) +gbt_cash_dist(const void *a, const void *b, FmgrInfo *flinfo) { return GET_FLOAT_DISTANCE(Cash, a, b); } @@ -151,7 +151,7 @@ gbt_cash_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -170,7 +170,7 @@ gbt_cash_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -182,7 +182,7 @@ gbt_cash_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(cashKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(cashKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -205,7 +205,7 @@ gbt_cash_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -216,6 +216,6 @@ gbt_cash_same(PG_FUNCTION_ARGS) cashKEY *b2 = (cashKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_date.c b/contrib/btree_gist/btree_date.c index bb516a9500..28c7c2ac86 100644 --- a/contrib/btree_gist/btree_date.c +++ b/contrib/btree_gist/btree_date.c @@ -5,6 +5,7 @@ #include "btree_gist.h" #include "btree_utils_num.h" +#include "utils/builtins.h" #include "utils/date.h" typedef struct @@ -26,7 +27,7 @@ PG_FUNCTION_INFO_V1(gbt_date_penalty); PG_FUNCTION_INFO_V1(gbt_date_same); static bool -gbt_dategt(const void *a, const void *b) +gbt_dategt(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool( DirectFunctionCall2(date_gt, DateADTGetDatum(*((const DateADT *) a)), DateADTGetDatum(*((const DateADT *) b))) @@ -34,7 +35,7 @@ gbt_dategt(const void *a, const void *b) } static bool -gbt_datege(const void *a, const void *b) +gbt_datege(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool( DirectFunctionCall2(date_ge, DateADTGetDatum(*((const DateADT *) a)), DateADTGetDatum(*((const DateADT *) b))) @@ -42,7 +43,7 @@ gbt_datege(const void *a, const void *b) } static bool -gbt_dateeq(const void *a, const void *b) +gbt_dateeq(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool( DirectFunctionCall2(date_eq, DateADTGetDatum(*((const DateADT *) a)), DateADTGetDatum(*((const DateADT *) b))) @@ -50,7 +51,7 @@ gbt_dateeq(const void *a, const void *b) } static bool -gbt_datele(const void *a, const void *b) +gbt_datele(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool( DirectFunctionCall2(date_le, DateADTGetDatum(*((const DateADT *) a)), DateADTGetDatum(*((const DateADT *) b))) @@ -58,7 +59,7 @@ gbt_datele(const void *a, const void *b) } static bool -gbt_datelt(const void *a, const void *b) +gbt_datelt(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool( DirectFunctionCall2(date_lt, DateADTGetDatum(*((const DateADT *) a)), DateADTGetDatum(*((const DateADT *) b))) @@ -68,7 +69,7 @@ gbt_datelt(const void *a, const void *b) static int -gbt_datekey_cmp(const void *a, const void *b) +gbt_datekey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { dateKEY *ia = (dateKEY *) (((const Nsrt *) a)->t); dateKEY *ib = (dateKEY *) (((const Nsrt *) b)->t); @@ -82,7 +83,7 @@ gbt_datekey_cmp(const void *a, const void *b) } static float8 -gdb_date_dist(const void *a, const void *b) +gdb_date_dist(const void *a, const void *b, FmgrInfo *flinfo) { /* we assume the difference can't overflow */ Datum diff = DirectFunctionCall2(date_mi, @@ -162,7 +163,7 @@ gbt_date_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -181,7 +182,7 @@ gbt_date_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -193,7 +194,7 @@ gbt_date_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(dateKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(dateKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -243,7 +244,7 @@ gbt_date_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -254,6 +255,6 @@ gbt_date_same(PG_FUNCTION_ARGS) dateKEY *b2 = (dateKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_enum.c b/contrib/btree_gist/btree_enum.c new file mode 100644 index 0000000000..8bbadfe860 --- /dev/null +++ b/contrib/btree_gist/btree_enum.c @@ -0,0 +1,187 @@ +/* + * contrib/btree_gist/btree_enum.c + */ +#include "postgres.h" +#include "fmgr.h" +#include "utils/builtins.h" + +#include "btree_gist.h" +#include "btree_utils_num.h" + +/* enums are really Oids, so we just use the same structure */ + +typedef struct +{ + Oid lower; + Oid upper; +} oidKEY; + +/* +** enum ops +*/ +PG_FUNCTION_INFO_V1(gbt_enum_compress); +PG_FUNCTION_INFO_V1(gbt_enum_fetch); +PG_FUNCTION_INFO_V1(gbt_enum_union); +PG_FUNCTION_INFO_V1(gbt_enum_picksplit); +PG_FUNCTION_INFO_V1(gbt_enum_consistent); +PG_FUNCTION_INFO_V1(gbt_enum_penalty); +PG_FUNCTION_INFO_V1(gbt_enum_same); + + +static bool +gbt_enumgt(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool( + CallerFInfoFunctionCall2(enum_gt, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b))) + ); +} +static bool +gbt_enumge(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool( + CallerFInfoFunctionCall2(enum_ge, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b))) + ); +} +static bool +gbt_enumeq(const void *a, const void *b, FmgrInfo *flinfo) +{ + return (*((const Oid *) a) == *((const Oid *) b)); +} +static bool +gbt_enumle(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool( + CallerFInfoFunctionCall2(enum_le, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b))) + ); +} +static bool +gbt_enumlt(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool( + CallerFInfoFunctionCall2(enum_lt, flinfo, InvalidOid, ObjectIdGetDatum(*((const Oid *) a)), ObjectIdGetDatum(*((const Oid *) b))) + ); +} + +static int +gbt_enumkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) +{ + oidKEY *ia = (oidKEY *) (((const Nsrt *) a)->t); + oidKEY *ib = (oidKEY *) (((const Nsrt *) b)->t); + + if (ia->lower == ib->lower) + { + if (ia->upper == ib->upper) + return 0; + + return DatumGetInt32( + CallerFInfoFunctionCall2(enum_cmp, flinfo, InvalidOid, ObjectIdGetDatum(ia->upper), ObjectIdGetDatum(ib->upper)) + ); + } + + return DatumGetInt32( + CallerFInfoFunctionCall2(enum_cmp, flinfo, InvalidOid, ObjectIdGetDatum(ia->lower), ObjectIdGetDatum(ib->lower)) + ); +} + +static const gbtree_ninfo tinfo = +{ + gbt_t_enum, + sizeof(Oid), + 8, /* sizeof(gbtreekey8) */ + gbt_enumgt, + gbt_enumge, + gbt_enumeq, + gbt_enumle, + gbt_enumlt, + gbt_enumkey_cmp, + NULL /* no KNN support at least for now */ +}; + + +/************************************************** + * Enum ops + **************************************************/ + + +Datum +gbt_enum_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + + PG_RETURN_POINTER(gbt_num_compress(entry, &tinfo)); +} + +Datum +gbt_enum_fetch(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + + PG_RETURN_POINTER(gbt_num_fetch(entry, &tinfo)); +} + +Datum +gbt_enum_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + Oid query = PG_GETARG_OID(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + oidKEY *kkk = (oidKEY *) DatumGetPointer(entry->key); + GBT_NUMKEY_R key; + + /* All cases served by this function are exact */ + *recheck = false; + + key.lower = (GBT_NUMKEY *) &kkk->lower; + key.upper = (GBT_NUMKEY *) &kkk->upper; + + PG_RETURN_BOOL( + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) + ); +} + +Datum +gbt_enum_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + void *out = palloc(sizeof(oidKEY)); + + *(int *) PG_GETARG_POINTER(1) = sizeof(oidKEY); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); +} + + +Datum +gbt_enum_penalty(PG_FUNCTION_ARGS) +{ + oidKEY *origentry = (oidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key); + oidKEY *newentry = (oidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key); + float *result = (float *) PG_GETARG_POINTER(2); + + penalty_num(result, origentry->lower, origentry->upper, newentry->lower, newentry->upper); + + PG_RETURN_POINTER(result); +} + +Datum +gbt_enum_picksplit(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(gbt_num_picksplit( + (GistEntryVector *) PG_GETARG_POINTER(0), + (GIST_SPLITVEC *) PG_GETARG_POINTER(1), + &tinfo, fcinfo->flinfo + )); +} + +Datum +gbt_enum_same(PG_FUNCTION_ARGS) +{ + oidKEY *b1 = (oidKEY *) PG_GETARG_POINTER(0); + oidKEY *b2 = (oidKEY *) PG_GETARG_POINTER(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); + PG_RETURN_POINTER(result); +} diff --git a/contrib/btree_gist/btree_float4.c b/contrib/btree_gist/btree_float4.c index 13dc4a5c0f..fe6993c226 100644 --- a/contrib/btree_gist/btree_float4.c +++ b/contrib/btree_gist/btree_float4.c @@ -25,33 +25,33 @@ PG_FUNCTION_INFO_V1(gbt_float4_penalty); PG_FUNCTION_INFO_V1(gbt_float4_same); static bool -gbt_float4gt(const void *a, const void *b) +gbt_float4gt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float4 *) a) > *((const float4 *) b)); } static bool -gbt_float4ge(const void *a, const void *b) +gbt_float4ge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float4 *) a) >= *((const float4 *) b)); } static bool -gbt_float4eq(const void *a, const void *b) +gbt_float4eq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float4 *) a) == *((const float4 *) b)); } static bool -gbt_float4le(const void *a, const void *b) +gbt_float4le(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float4 *) a) <= *((const float4 *) b)); } static bool -gbt_float4lt(const void *a, const void *b) +gbt_float4lt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float4 *) a) < *((const float4 *) b)); } static int -gbt_float4key_cmp(const void *a, const void *b) +gbt_float4key_cmp(const void *a, const void *b, FmgrInfo *flinfo) { float4KEY *ia = (float4KEY *) (((const Nsrt *) a)->t); float4KEY *ib = (float4KEY *) (((const Nsrt *) b)->t); @@ -68,7 +68,7 @@ gbt_float4key_cmp(const void *a, const void *b) } static float8 -gbt_float4_dist(const void *a, const void *b) +gbt_float4_dist(const void *a, const void *b, FmgrInfo *flinfo) { return GET_FLOAT_DISTANCE(float4, a, b); } @@ -144,7 +144,7 @@ gbt_float4_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -163,7 +163,7 @@ gbt_float4_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -175,7 +175,7 @@ gbt_float4_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(float4KEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(float4KEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -198,7 +198,7 @@ gbt_float4_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -209,6 +209,6 @@ gbt_float4_same(PG_FUNCTION_ARGS) float4KEY *b2 = (float4KEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_float8.c b/contrib/btree_gist/btree_float8.c index c3a2415733..13153d811f 100644 --- a/contrib/btree_gist/btree_float8.c +++ b/contrib/btree_gist/btree_float8.c @@ -26,33 +26,33 @@ PG_FUNCTION_INFO_V1(gbt_float8_same); static bool -gbt_float8gt(const void *a, const void *b) +gbt_float8gt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float8 *) a) > *((const float8 *) b)); } static bool -gbt_float8ge(const void *a, const void *b) +gbt_float8ge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float8 *) a) >= *((const float8 *) b)); } static bool -gbt_float8eq(const void *a, const void *b) +gbt_float8eq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float8 *) a) == *((const float8 *) b)); } static bool -gbt_float8le(const void *a, const void *b) +gbt_float8le(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float8 *) a) <= *((const float8 *) b)); } static bool -gbt_float8lt(const void *a, const void *b) +gbt_float8lt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const float8 *) a) < *((const float8 *) b)); } static int -gbt_float8key_cmp(const void *a, const void *b) +gbt_float8key_cmp(const void *a, const void *b, FmgrInfo *flinfo) { float8KEY *ia = (float8KEY *) (((const Nsrt *) a)->t); float8KEY *ib = (float8KEY *) (((const Nsrt *) b)->t); @@ -69,7 +69,7 @@ gbt_float8key_cmp(const void *a, const void *b) } static float8 -gbt_float8_dist(const void *a, const void *b) +gbt_float8_dist(const void *a, const void *b, FmgrInfo *flinfo) { float8 arg1 = *(const float8 *) a; float8 arg2 = *(const float8 *) b; @@ -151,7 +151,7 @@ gbt_float8_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -170,7 +170,7 @@ gbt_float8_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -182,7 +182,7 @@ gbt_float8_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(float8KEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(float8KEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -205,7 +205,7 @@ gbt_float8_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -216,6 +216,6 @@ gbt_float8_same(PG_FUNCTION_ARGS) float8KEY *b2 = (float8KEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_gist--1.2--1.3.sql b/contrib/btree_gist/btree_gist--1.2--1.3.sql new file mode 100644 index 0000000000..726561e87b --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.2--1.3.sql @@ -0,0 +1,65 @@ +/* contrib/btree_gist/btree_gist--1.2--1.3.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.3'" to load this file. \quit + +-- Add support for indexing UUID columns + +-- define the GiST support methods +CREATE FUNCTION gbt_uuid_consistent(internal,uuid,int2,oid,internal) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_uuid_fetch(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_uuid_compress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_uuid_penalty(internal,internal,internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_uuid_picksplit(internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_uuid_union(internal, internal) +RETURNS gbtreekey32 +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_uuid_same(gbtreekey32, gbtreekey32, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +-- Create the operator class +CREATE OPERATOR CLASS gist_uuid_ops +DEFAULT FOR TYPE uuid USING gist +AS + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 gbt_uuid_consistent (internal, uuid, int2, oid, internal), + FUNCTION 2 gbt_uuid_union (internal, internal), + FUNCTION 3 gbt_uuid_compress (internal), + FUNCTION 4 gbt_decompress (internal), + FUNCTION 5 gbt_uuid_penalty (internal, internal, internal), + FUNCTION 6 gbt_uuid_picksplit (internal, internal), + FUNCTION 7 gbt_uuid_same (gbtreekey32, gbtreekey32, internal), + STORAGE gbtreekey32; + +-- These are "loose" in the opfamily for consistency with the rest of btree_gist +ALTER OPERATOR FAMILY gist_uuid_ops USING gist ADD + OPERATOR 6 <> (uuid, uuid) , + FUNCTION 9 (uuid, uuid) gbt_uuid_fetch (internal) ; diff --git a/contrib/btree_gist/btree_gist--1.3--1.4.sql b/contrib/btree_gist/btree_gist--1.3--1.4.sql new file mode 100644 index 0000000000..f77f6c8380 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.3--1.4.sql @@ -0,0 +1,64 @@ +/* contrib/btree_gist/btree_gist--1.3--1.4.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.4'" to load this file. \quit + +-- Add support for indexing macaddr8 columns + +-- define the GiST support methods +CREATE FUNCTION gbt_macad8_consistent(internal,macaddr8,int2,oid,internal) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_macad8_compress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_macad8_fetch(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_macad8_penalty(internal,internal,internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_macad8_picksplit(internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_macad8_union(internal, internal) +RETURNS gbtreekey16 +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_macad8_same(gbtreekey16, gbtreekey16, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +-- Create the operator class +CREATE OPERATOR CLASS gist_macaddr8_ops +DEFAULT FOR TYPE macaddr8 USING gist +AS + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 gbt_macad8_consistent (internal, macaddr8, int2, oid, internal), + FUNCTION 2 gbt_macad8_union (internal, internal), + FUNCTION 3 gbt_macad8_compress (internal), + FUNCTION 4 gbt_decompress (internal), + FUNCTION 5 gbt_macad8_penalty (internal, internal, internal), + FUNCTION 6 gbt_macad8_picksplit (internal, internal), + FUNCTION 7 gbt_macad8_same (gbtreekey16, gbtreekey16, internal), + STORAGE gbtreekey16; + +ALTER OPERATOR FAMILY gist_macaddr8_ops USING gist ADD + OPERATOR 6 <> (macaddr8, macaddr8) , + FUNCTION 9 (macaddr8, macaddr8) gbt_macad8_fetch (internal); diff --git a/contrib/btree_gist/btree_gist--1.4--1.5.sql b/contrib/btree_gist/btree_gist--1.4--1.5.sql new file mode 100644 index 0000000000..cf974c2f53 --- /dev/null +++ b/contrib/btree_gist/btree_gist--1.4--1.5.sql @@ -0,0 +1,69 @@ +/* contrib/btree_gist/btree_gist--1.4--1.5.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "ALTER EXTENSION btree_gist UPDATE TO '1.5'" to load this file. \quit + +-- +-- +-- +-- enum ops +-- +-- +-- +-- define the GiST support methods +CREATE FUNCTION gbt_enum_consistent(internal,anyenum,int2,oid,internal) +RETURNS bool +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_enum_compress(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_enum_fetch(internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_enum_penalty(internal,internal,internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_enum_picksplit(internal, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_enum_union(internal, internal) +RETURNS gbtreekey8 +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION gbt_enum_same(gbtreekey8, gbtreekey8, internal) +RETURNS internal +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +-- Create the operator class +CREATE OPERATOR CLASS gist_enum_ops +DEFAULT FOR TYPE anyenum USING gist +AS + OPERATOR 1 < , + OPERATOR 2 <= , + OPERATOR 3 = , + OPERATOR 4 >= , + OPERATOR 5 > , + FUNCTION 1 gbt_enum_consistent (internal, anyenum, int2, oid, internal), + FUNCTION 2 gbt_enum_union (internal, internal), + FUNCTION 3 gbt_enum_compress (internal), + FUNCTION 4 gbt_decompress (internal), + FUNCTION 5 gbt_enum_penalty (internal, internal, internal), + FUNCTION 6 gbt_enum_picksplit (internal, internal), + FUNCTION 7 gbt_enum_same (gbtreekey8, gbtreekey8, internal), + STORAGE gbtreekey8; + +ALTER OPERATOR FAMILY gist_enum_ops USING gist ADD + OPERATOR 6 <> (anyenum, anyenum) , + FUNCTION 9 (anyenum, anyenum) gbt_enum_fetch (internal) ; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control index 74d0e92591..81c850905c 100644 --- a/contrib/btree_gist/btree_gist.control +++ b/contrib/btree_gist/btree_gist.control @@ -1,5 +1,5 @@ # btree_gist extension comment = 'support for indexing common datatypes in GiST' -default_version = '1.2' +default_version = '1.5' module_pathname = '$libdir/btree_gist' relocatable = true diff --git a/contrib/btree_gist/btree_gist.h b/contrib/btree_gist/btree_gist.h index dcffbb5178..011285abce 100644 --- a/contrib/btree_gist/btree_gist.h +++ b/contrib/btree_gist/btree_gist.h @@ -27,21 +27,14 @@ enum gbtree_type gbt_t_date, gbt_t_intv, gbt_t_macad, + gbt_t_macad8, gbt_t_text, gbt_t_bpchar, gbt_t_bytea, gbt_t_bit, - gbt_t_inet + gbt_t_inet, + gbt_t_uuid, + gbt_t_enum }; - - -/* - * Generic btree functions - */ - -Datum gbtreekey_in(PG_FUNCTION_ARGS); - -Datum gbtreekey_out(PG_FUNCTION_ARGS); - #endif diff --git a/contrib/btree_gist/btree_inet.c b/contrib/btree_gist/btree_inet.c index 822786125d..e1561b37b7 100644 --- a/contrib/btree_gist/btree_inet.c +++ b/contrib/btree_gist/btree_inet.c @@ -27,33 +27,33 @@ PG_FUNCTION_INFO_V1(gbt_inet_same); static bool -gbt_inetgt(const void *a, const void *b) +gbt_inetgt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const double *) a) > *((const double *) b)); } static bool -gbt_inetge(const void *a, const void *b) +gbt_inetge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const double *) a) >= *((const double *) b)); } static bool -gbt_ineteq(const void *a, const void *b) +gbt_ineteq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const double *) a) == *((const double *) b)); } static bool -gbt_inetle(const void *a, const void *b) +gbt_inetle(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const double *) a) <= *((const double *) b)); } static bool -gbt_inetlt(const void *a, const void *b) +gbt_inetlt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const double *) a) < *((const double *) b)); } static int -gbt_inetkey_cmp(const void *a, const void *b) +gbt_inetkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { inetKEY *ia = (inetKEY *) (((const Nsrt *) a)->t); inetKEY *ib = (inetKEY *) (((const Nsrt *) b)->t); @@ -133,7 +133,7 @@ gbt_inet_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL(gbt_num_consistent(&key, (void *) &query, - &strategy, GIST_LEAF(entry), &tinfo)); + &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo)); } @@ -144,7 +144,7 @@ gbt_inet_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(inetKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(inetKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -167,7 +167,7 @@ gbt_inet_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -178,6 +178,6 @@ gbt_inet_same(PG_FUNCTION_ARGS) inetKEY *b2 = (inetKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_int2.c b/contrib/btree_gist/btree_int2.c index 54dc1cc518..0a4498a693 100644 --- a/contrib/btree_gist/btree_int2.c +++ b/contrib/btree_gist/btree_int2.c @@ -25,33 +25,33 @@ PG_FUNCTION_INFO_V1(gbt_int2_penalty); PG_FUNCTION_INFO_V1(gbt_int2_same); static bool -gbt_int2gt(const void *a, const void *b) +gbt_int2gt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int16 *) a) > *((const int16 *) b)); } static bool -gbt_int2ge(const void *a, const void *b) +gbt_int2ge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int16 *) a) >= *((const int16 *) b)); } static bool -gbt_int2eq(const void *a, const void *b) +gbt_int2eq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int16 *) a) == *((const int16 *) b)); } static bool -gbt_int2le(const void *a, const void *b) +gbt_int2le(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int16 *) a) <= *((const int16 *) b)); } static bool -gbt_int2lt(const void *a, const void *b) +gbt_int2lt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int16 *) a) < *((const int16 *) b)); } static int -gbt_int2key_cmp(const void *a, const void *b) +gbt_int2key_cmp(const void *a, const void *b, FmgrInfo *flinfo) { int16KEY *ia = (int16KEY *) (((const Nsrt *) a)->t); int16KEY *ib = (int16KEY *) (((const Nsrt *) b)->t); @@ -68,7 +68,7 @@ gbt_int2key_cmp(const void *a, const void *b) } static float8 -gbt_int2_dist(const void *a, const void *b) +gbt_int2_dist(const void *a, const void *b, FmgrInfo *flinfo) { return GET_FLOAT_DISTANCE(int16, a, b); } @@ -151,7 +151,7 @@ gbt_int2_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -170,7 +170,7 @@ gbt_int2_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -182,7 +182,7 @@ gbt_int2_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(int16KEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(int16KEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -204,7 +204,7 @@ gbt_int2_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -215,6 +215,6 @@ gbt_int2_same(PG_FUNCTION_ARGS) int16KEY *b2 = (int16KEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_int4.c b/contrib/btree_gist/btree_int4.c index ddbcf52746..b29cbc81a3 100644 --- a/contrib/btree_gist/btree_int4.c +++ b/contrib/btree_gist/btree_int4.c @@ -26,33 +26,33 @@ PG_FUNCTION_INFO_V1(gbt_int4_same); static bool -gbt_int4gt(const void *a, const void *b) +gbt_int4gt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int32 *) a) > *((const int32 *) b)); } static bool -gbt_int4ge(const void *a, const void *b) +gbt_int4ge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int32 *) a) >= *((const int32 *) b)); } static bool -gbt_int4eq(const void *a, const void *b) +gbt_int4eq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int32 *) a) == *((const int32 *) b)); } static bool -gbt_int4le(const void *a, const void *b) +gbt_int4le(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int32 *) a) <= *((const int32 *) b)); } static bool -gbt_int4lt(const void *a, const void *b) +gbt_int4lt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int32 *) a) < *((const int32 *) b)); } static int -gbt_int4key_cmp(const void *a, const void *b) +gbt_int4key_cmp(const void *a, const void *b, FmgrInfo *flinfo) { int32KEY *ia = (int32KEY *) (((const Nsrt *) a)->t); int32KEY *ib = (int32KEY *) (((const Nsrt *) b)->t); @@ -69,7 +69,7 @@ gbt_int4key_cmp(const void *a, const void *b) } static float8 -gbt_int4_dist(const void *a, const void *b) +gbt_int4_dist(const void *a, const void *b, FmgrInfo *flinfo) { return GET_FLOAT_DISTANCE(int32, a, b); } @@ -152,7 +152,7 @@ gbt_int4_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -171,7 +171,7 @@ gbt_int4_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -183,7 +183,7 @@ gbt_int4_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(int32KEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(int32KEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -205,7 +205,7 @@ gbt_int4_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -216,6 +216,6 @@ gbt_int4_same(PG_FUNCTION_ARGS) int32KEY *b2 = (int32KEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_int8.c b/contrib/btree_gist/btree_int8.c index 44bf69a4fb..df1f5338c8 100644 --- a/contrib/btree_gist/btree_int8.c +++ b/contrib/btree_gist/btree_int8.c @@ -26,33 +26,33 @@ PG_FUNCTION_INFO_V1(gbt_int8_same); static bool -gbt_int8gt(const void *a, const void *b) +gbt_int8gt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int64 *) a) > *((const int64 *) b)); } static bool -gbt_int8ge(const void *a, const void *b) +gbt_int8ge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int64 *) a) >= *((const int64 *) b)); } static bool -gbt_int8eq(const void *a, const void *b) +gbt_int8eq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int64 *) a) == *((const int64 *) b)); } static bool -gbt_int8le(const void *a, const void *b) +gbt_int8le(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int64 *) a) <= *((const int64 *) b)); } static bool -gbt_int8lt(const void *a, const void *b) +gbt_int8lt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const int64 *) a) < *((const int64 *) b)); } static int -gbt_int8key_cmp(const void *a, const void *b) +gbt_int8key_cmp(const void *a, const void *b, FmgrInfo *flinfo) { int64KEY *ia = (int64KEY *) (((const Nsrt *) a)->t); int64KEY *ib = (int64KEY *) (((const Nsrt *) b)->t); @@ -69,7 +69,7 @@ gbt_int8key_cmp(const void *a, const void *b) } static float8 -gbt_int8_dist(const void *a, const void *b) +gbt_int8_dist(const void *a, const void *b, FmgrInfo *flinfo) { return GET_FLOAT_DISTANCE(int64, a, b); } @@ -152,7 +152,7 @@ gbt_int8_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -171,7 +171,7 @@ gbt_int8_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -183,7 +183,7 @@ gbt_int8_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(int64KEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(int64KEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -205,7 +205,7 @@ gbt_int8_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -216,6 +216,6 @@ gbt_int8_same(PG_FUNCTION_ARGS) int64KEY *b2 = (int64KEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_interval.c b/contrib/btree_gist/btree_interval.c index acccb8b42e..e4dd9e4238 100644 --- a/contrib/btree_gist/btree_interval.c +++ b/contrib/btree_gist/btree_interval.c @@ -5,6 +5,7 @@ #include "btree_gist.h" #include "btree_utils_num.h" +#include "utils/builtins.h" #include "utils/timestamp.h" typedef struct @@ -29,37 +30,37 @@ PG_FUNCTION_INFO_V1(gbt_intv_same); static bool -gbt_intvgt(const void *a, const void *b) +gbt_intvgt(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(interval_gt, IntervalPGetDatum(a), IntervalPGetDatum(b))); } static bool -gbt_intvge(const void *a, const void *b) +gbt_intvge(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(interval_ge, IntervalPGetDatum(a), IntervalPGetDatum(b))); } static bool -gbt_intveq(const void *a, const void *b) +gbt_intveq(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(interval_eq, IntervalPGetDatum(a), IntervalPGetDatum(b))); } static bool -gbt_intvle(const void *a, const void *b) +gbt_intvle(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(interval_le, IntervalPGetDatum(a), IntervalPGetDatum(b))); } static bool -gbt_intvlt(const void *a, const void *b) +gbt_intvlt(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(interval_lt, IntervalPGetDatum(a), IntervalPGetDatum(b))); } static int -gbt_intvkey_cmp(const void *a, const void *b) +gbt_intvkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { intvKEY *ia = (intvKEY *) (((const Nsrt *) a)->t); intvKEY *ib = (intvKEY *) (((const Nsrt *) b)->t); @@ -80,7 +81,7 @@ intr2num(const Interval *i) } static float8 -gbt_intv_dist(const void *a, const void *b) +gbt_intv_dist(const void *a, const void *b, FmgrInfo *flinfo) { return (float8) Abs(intr2num((const Interval *) a) - intr2num((const Interval *) b)); } @@ -225,7 +226,7 @@ gbt_intv_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -244,7 +245,7 @@ gbt_intv_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -256,7 +257,7 @@ gbt_intv_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(intvKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(intvKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -286,7 +287,7 @@ gbt_intv_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -297,6 +298,6 @@ gbt_intv_same(PG_FUNCTION_ARGS) intvKEY *b2 = (intvKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_macaddr.c b/contrib/btree_gist/btree_macaddr.c index 87d96c00ac..d530b4e878 100644 --- a/contrib/btree_gist/btree_macaddr.c +++ b/contrib/btree_gist/btree_macaddr.c @@ -28,37 +28,37 @@ PG_FUNCTION_INFO_V1(gbt_macad_same); static bool -gbt_macadgt(const void *a, const void *b) +gbt_macadgt(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(macaddr_gt, PointerGetDatum(a), PointerGetDatum(b))); } static bool -gbt_macadge(const void *a, const void *b) +gbt_macadge(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(macaddr_ge, PointerGetDatum(a), PointerGetDatum(b))); } static bool -gbt_macadeq(const void *a, const void *b) +gbt_macadeq(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(macaddr_eq, PointerGetDatum(a), PointerGetDatum(b))); } static bool -gbt_macadle(const void *a, const void *b) +gbt_macadle(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(macaddr_le, PointerGetDatum(a), PointerGetDatum(b))); } static bool -gbt_macadlt(const void *a, const void *b) +gbt_macadlt(const void *a, const void *b, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(macaddr_lt, PointerGetDatum(a), PointerGetDatum(b))); } static int -gbt_macadkey_cmp(const void *a, const void *b) +gbt_macadkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { macKEY *ia = (macKEY *) (((const Nsrt *) a)->t); macKEY *ib = (macKEY *) (((const Nsrt *) b)->t); @@ -142,7 +142,7 @@ gbt_macad_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -154,7 +154,7 @@ gbt_macad_union(PG_FUNCTION_ARGS) void *out = palloc0(sizeof(macKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(macKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -184,7 +184,7 @@ gbt_macad_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -195,6 +195,6 @@ gbt_macad_same(PG_FUNCTION_ARGS) macKEY *b2 = (macKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_macaddr8.c b/contrib/btree_gist/btree_macaddr8.c new file mode 100644 index 0000000000..96afbcdead --- /dev/null +++ b/contrib/btree_gist/btree_macaddr8.c @@ -0,0 +1,200 @@ +/* + * contrib/btree_gist/btree_macaddr8.c + */ +#include "postgres.h" + +#include "btree_gist.h" +#include "btree_utils_num.h" +#include "utils/builtins.h" +#include "utils/inet.h" + +typedef struct +{ + macaddr8 lower; + macaddr8 upper; + /* make struct size = sizeof(gbtreekey16) */ +} mac8KEY; + +/* +** OID ops +*/ +PG_FUNCTION_INFO_V1(gbt_macad8_compress); +PG_FUNCTION_INFO_V1(gbt_macad8_fetch); +PG_FUNCTION_INFO_V1(gbt_macad8_union); +PG_FUNCTION_INFO_V1(gbt_macad8_picksplit); +PG_FUNCTION_INFO_V1(gbt_macad8_consistent); +PG_FUNCTION_INFO_V1(gbt_macad8_penalty); +PG_FUNCTION_INFO_V1(gbt_macad8_same); + + +static bool +gbt_macad8gt(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool(DirectFunctionCall2(macaddr8_gt, PointerGetDatum(a), PointerGetDatum(b))); +} +static bool +gbt_macad8ge(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool(DirectFunctionCall2(macaddr8_ge, PointerGetDatum(a), PointerGetDatum(b))); +} + +static bool +gbt_macad8eq(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool(DirectFunctionCall2(macaddr8_eq, PointerGetDatum(a), PointerGetDatum(b))); +} + +static bool +gbt_macad8le(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool(DirectFunctionCall2(macaddr8_le, PointerGetDatum(a), PointerGetDatum(b))); +} + +static bool +gbt_macad8lt(const void *a, const void *b, FmgrInfo *flinfo) +{ + return DatumGetBool(DirectFunctionCall2(macaddr8_lt, PointerGetDatum(a), PointerGetDatum(b))); +} + + +static int +gbt_macad8key_cmp(const void *a, const void *b, FmgrInfo *flinfo) +{ + mac8KEY *ia = (mac8KEY *) (((const Nsrt *) a)->t); + mac8KEY *ib = (mac8KEY *) (((const Nsrt *) b)->t); + int res; + + res = DatumGetInt32(DirectFunctionCall2(macaddr8_cmp, Macaddr8PGetDatum(&ia->lower), Macaddr8PGetDatum(&ib->lower))); + if (res == 0) + return DatumGetInt32(DirectFunctionCall2(macaddr8_cmp, Macaddr8PGetDatum(&ia->upper), Macaddr8PGetDatum(&ib->upper))); + + return res; +} + + +static const gbtree_ninfo tinfo = +{ + gbt_t_macad8, + sizeof(macaddr8), + 16, /* sizeof(gbtreekey16) */ + gbt_macad8gt, + gbt_macad8ge, + gbt_macad8eq, + gbt_macad8le, + gbt_macad8lt, + gbt_macad8key_cmp, + NULL +}; + + +/************************************************** + * macaddr ops + **************************************************/ + + + +static uint64 +mac8_2_uint64(macaddr8 *m) +{ + unsigned char *mi = (unsigned char *) m; + uint64 res = 0; + int i; + + for (i = 0; i < 8; i++) + res += (((uint64) mi[i]) << ((uint64) ((7 - i) * 8))); + return res; +} + + + +Datum +gbt_macad8_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + + PG_RETURN_POINTER(gbt_num_compress(entry, &tinfo)); +} + +Datum +gbt_macad8_fetch(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + + PG_RETURN_POINTER(gbt_num_fetch(entry, &tinfo)); +} + +Datum +gbt_macad8_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + macaddr8 *query = (macaddr8 *) PG_GETARG_POINTER(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + mac8KEY *kkk = (mac8KEY *) DatumGetPointer(entry->key); + GBT_NUMKEY_R key; + + /* All cases served by this function are exact */ + *recheck = false; + + key.lower = (GBT_NUMKEY *) &kkk->lower; + key.upper = (GBT_NUMKEY *) &kkk->upper; + + PG_RETURN_BOOL( + gbt_num_consistent(&key, (void *) query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) + ); +} + + +Datum +gbt_macad8_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + void *out = palloc0(sizeof(mac8KEY)); + + *(int *) PG_GETARG_POINTER(1) = sizeof(mac8KEY); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); +} + + +Datum +gbt_macad8_penalty(PG_FUNCTION_ARGS) +{ + mac8KEY *origentry = (mac8KEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key); + mac8KEY *newentry = (mac8KEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key); + float *result = (float *) PG_GETARG_POINTER(2); + uint64 iorg[2], + inew[2]; + + iorg[0] = mac8_2_uint64(&origentry->lower); + iorg[1] = mac8_2_uint64(&origentry->upper); + inew[0] = mac8_2_uint64(&newentry->lower); + inew[1] = mac8_2_uint64(&newentry->upper); + + penalty_num(result, iorg[0], iorg[1], inew[0], inew[1]); + + PG_RETURN_POINTER(result); + +} + +Datum +gbt_macad8_picksplit(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(gbt_num_picksplit( + (GistEntryVector *) PG_GETARG_POINTER(0), + (GIST_SPLITVEC *) PG_GETARG_POINTER(1), + &tinfo, fcinfo->flinfo + )); +} + +Datum +gbt_macad8_same(PG_FUNCTION_ARGS) +{ + mac8KEY *b1 = (mac8KEY *) PG_GETARG_POINTER(0); + mac8KEY *b2 = (mac8KEY *) PG_GETARG_POINTER(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); + PG_RETURN_POINTER(result); +} diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c index 47b00209c8..43793d36a2 100644 --- a/contrib/btree_gist/btree_numeric.c +++ b/contrib/btree_gist/btree_numeric.c @@ -27,7 +27,7 @@ PG_FUNCTION_INFO_V1(gbt_numeric_same); /* define for comparison */ static bool -gbt_numeric_gt(const void *a, const void *b, Oid collation) +gbt_numeric_gt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(numeric_gt, PointerGetDatum(a), @@ -35,7 +35,7 @@ gbt_numeric_gt(const void *a, const void *b, Oid collation) } static bool -gbt_numeric_ge(const void *a, const void *b, Oid collation) +gbt_numeric_ge(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(numeric_ge, PointerGetDatum(a), @@ -43,7 +43,7 @@ gbt_numeric_ge(const void *a, const void *b, Oid collation) } static bool -gbt_numeric_eq(const void *a, const void *b, Oid collation) +gbt_numeric_eq(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(numeric_eq, PointerGetDatum(a), @@ -51,7 +51,7 @@ gbt_numeric_eq(const void *a, const void *b, Oid collation) } static bool -gbt_numeric_le(const void *a, const void *b, Oid collation) +gbt_numeric_le(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(numeric_le, PointerGetDatum(a), @@ -59,7 +59,7 @@ gbt_numeric_le(const void *a, const void *b, Oid collation) } static bool -gbt_numeric_lt(const void *a, const void *b, Oid collation) +gbt_numeric_lt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2(numeric_lt, PointerGetDatum(a), @@ -67,7 +67,7 @@ gbt_numeric_lt(const void *a, const void *b, Oid collation) } static int32 -gbt_numeric_cmp(const void *a, const void *b, Oid collation) +gbt_numeric_cmp(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetInt32(DirectFunctionCall2(numeric_cmp, PointerGetDatum(a), @@ -122,7 +122,7 @@ gbt_numeric_consistent(PG_FUNCTION_ARGS) *recheck = false; retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), - GIST_LEAF(entry), &tinfo); + GIST_LEAF(entry), &tinfo, fcinfo->flinfo); PG_RETURN_BOOL(retval); } @@ -135,7 +135,7 @@ gbt_numeric_union(PG_FUNCTION_ARGS) int32 *size = (int *) PG_GETARG_POINTER(1); PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } @@ -146,7 +146,7 @@ gbt_numeric_same(PG_FUNCTION_ARGS) Datum d2 = PG_GETARG_DATUM(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo); + *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } @@ -171,7 +171,7 @@ gbt_numeric_penalty(PG_FUNCTION_ARGS) rk = gbt_var_key_readable(org); uni = PointerGetDatum(gbt_var_key_copy(&rk)); - gbt_var_bin_union(&uni, newe, PG_GET_COLLATION(), &tinfo); + gbt_var_bin_union(&uni, newe, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo); ok = gbt_var_key_readable(org); uk = gbt_var_key_readable((GBT_VARKEY *) DatumGetPointer(uni)); @@ -233,6 +233,6 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS) GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), - &tinfo); + &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } diff --git a/contrib/btree_gist/btree_oid.c b/contrib/btree_gist/btree_oid.c index ac61a76aa0..e0d6f2adf1 100644 --- a/contrib/btree_gist/btree_oid.c +++ b/contrib/btree_gist/btree_oid.c @@ -26,33 +26,33 @@ PG_FUNCTION_INFO_V1(gbt_oid_same); static bool -gbt_oidgt(const void *a, const void *b) +gbt_oidgt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Oid *) a) > *((const Oid *) b)); } static bool -gbt_oidge(const void *a, const void *b) +gbt_oidge(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Oid *) a) >= *((const Oid *) b)); } static bool -gbt_oideq(const void *a, const void *b) +gbt_oideq(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Oid *) a) == *((const Oid *) b)); } static bool -gbt_oidle(const void *a, const void *b) +gbt_oidle(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Oid *) a) <= *((const Oid *) b)); } static bool -gbt_oidlt(const void *a, const void *b) +gbt_oidlt(const void *a, const void *b, FmgrInfo *flinfo) { return (*((const Oid *) a) < *((const Oid *) b)); } static int -gbt_oidkey_cmp(const void *a, const void *b) +gbt_oidkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { oidKEY *ia = (oidKEY *) (((const Nsrt *) a)->t); oidKEY *ib = (oidKEY *) (((const Nsrt *) b)->t); @@ -69,7 +69,7 @@ gbt_oidkey_cmp(const void *a, const void *b) } static float8 -gbt_oid_dist(const void *a, const void *b) +gbt_oid_dist(const void *a, const void *b, FmgrInfo *flinfo) { Oid aa = *(const Oid *) a; Oid bb = *(const Oid *) b; @@ -152,7 +152,7 @@ gbt_oid_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -171,7 +171,7 @@ gbt_oid_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -183,7 +183,7 @@ gbt_oid_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(oidKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(oidKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -205,7 +205,7 @@ gbt_oid_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -216,6 +216,6 @@ gbt_oid_same(PG_FUNCTION_ARGS) oidKEY *b2 = (oidKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 2e00cb60ba..090c849470 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -23,7 +23,7 @@ PG_FUNCTION_INFO_V1(gbt_text_same); /* define for comparison */ static bool -gbt_textgt(const void *a, const void *b, Oid collation) +gbt_textgt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2Coll(text_gt, collation, @@ -32,7 +32,7 @@ gbt_textgt(const void *a, const void *b, Oid collation) } static bool -gbt_textge(const void *a, const void *b, Oid collation) +gbt_textge(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2Coll(text_ge, collation, @@ -41,7 +41,7 @@ gbt_textge(const void *a, const void *b, Oid collation) } static bool -gbt_texteq(const void *a, const void *b, Oid collation) +gbt_texteq(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2Coll(texteq, collation, @@ -50,7 +50,7 @@ gbt_texteq(const void *a, const void *b, Oid collation) } static bool -gbt_textle(const void *a, const void *b, Oid collation) +gbt_textle(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2Coll(text_le, collation, @@ -59,7 +59,7 @@ gbt_textle(const void *a, const void *b, Oid collation) } static bool -gbt_textlt(const void *a, const void *b, Oid collation) +gbt_textlt(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetBool(DirectFunctionCall2Coll(text_lt, collation, @@ -68,7 +68,7 @@ gbt_textlt(const void *a, const void *b, Oid collation) } static int32 -gbt_textcmp(const void *a, const void *b, Oid collation) +gbt_textcmp(const void *a, const void *b, Oid collation, FmgrInfo *flinfo) { return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp, collation, @@ -161,7 +161,7 @@ gbt_text_consistent(PG_FUNCTION_ARGS) } retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(), - GIST_LEAF(entry), &tinfo); + GIST_LEAF(entry), &tinfo, fcinfo->flinfo); PG_RETURN_BOOL(retval); } @@ -190,7 +190,7 @@ gbt_bpchar_consistent(PG_FUNCTION_ARGS) } retval = gbt_var_consistent(&r, trim, strategy, PG_GET_COLLATION(), - GIST_LEAF(entry), &tinfo); + GIST_LEAF(entry), &tinfo, fcinfo->flinfo); PG_RETURN_BOOL(retval); } @@ -202,7 +202,7 @@ gbt_text_union(PG_FUNCTION_ARGS) int32 *size = (int *) PG_GETARG_POINTER(1); PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } @@ -213,7 +213,7 @@ gbt_text_picksplit(PG_FUNCTION_ARGS) GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(), - &tinfo); + &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(v); } @@ -224,7 +224,7 @@ gbt_text_same(PG_FUNCTION_ARGS) Datum d2 = PG_GETARG_DATUM(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo); + *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } @@ -237,5 +237,5 @@ gbt_text_penalty(PG_FUNCTION_ARGS) float *result = (float *) PG_GETARG_POINTER(2); PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(), - &tinfo)); + &tinfo, fcinfo->flinfo)); } diff --git a/contrib/btree_gist/btree_time.c b/contrib/btree_gist/btree_time.c index 41d9959214..5eec8323f5 100644 --- a/contrib/btree_gist/btree_time.c +++ b/contrib/btree_gist/btree_time.c @@ -5,6 +5,7 @@ #include "btree_gist.h" #include "btree_utils_num.h" +#include "utils/builtins.h" #include "utils/date.h" #include "utils/timestamp.h" @@ -37,7 +38,7 @@ PG_FUNCTION_INFO_V1(gbt_time_same); static bool -gbt_timegt(const void *a, const void *b) +gbt_timegt(const void *a, const void *b, FmgrInfo *flinfo) { const TimeADT *aa = (const TimeADT *) a; const TimeADT *bb = (const TimeADT *) b; @@ -48,7 +49,7 @@ gbt_timegt(const void *a, const void *b) } static bool -gbt_timege(const void *a, const void *b) +gbt_timege(const void *a, const void *b, FmgrInfo *flinfo) { const TimeADT *aa = (const TimeADT *) a; const TimeADT *bb = (const TimeADT *) b; @@ -59,7 +60,7 @@ gbt_timege(const void *a, const void *b) } static bool -gbt_timeeq(const void *a, const void *b) +gbt_timeeq(const void *a, const void *b, FmgrInfo *flinfo) { const TimeADT *aa = (const TimeADT *) a; const TimeADT *bb = (const TimeADT *) b; @@ -70,7 +71,7 @@ gbt_timeeq(const void *a, const void *b) } static bool -gbt_timele(const void *a, const void *b) +gbt_timele(const void *a, const void *b, FmgrInfo *flinfo) { const TimeADT *aa = (const TimeADT *) a; const TimeADT *bb = (const TimeADT *) b; @@ -81,7 +82,7 @@ gbt_timele(const void *a, const void *b) } static bool -gbt_timelt(const void *a, const void *b) +gbt_timelt(const void *a, const void *b, FmgrInfo *flinfo) { const TimeADT *aa = (const TimeADT *) a; const TimeADT *bb = (const TimeADT *) b; @@ -94,7 +95,7 @@ gbt_timelt(const void *a, const void *b) static int -gbt_timekey_cmp(const void *a, const void *b) +gbt_timekey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { timeKEY *ia = (timeKEY *) (((const Nsrt *) a)->t); timeKEY *ib = (timeKEY *) (((const Nsrt *) b)->t); @@ -108,7 +109,7 @@ gbt_timekey_cmp(const void *a, const void *b) } static float8 -gbt_time_dist(const void *a, const void *b) +gbt_time_dist(const void *a, const void *b, FmgrInfo *flinfo) { const TimeADT *aa = (const TimeADT *) a; const TimeADT *bb = (const TimeADT *) b; @@ -178,11 +179,7 @@ gbt_timetz_compress(PG_FUNCTION_ARGS) retval = palloc(sizeof(GISTENTRY)); /* We are using the time + zone only to compress */ -#ifdef HAVE_INT64_TIMESTAMP tmp = tz->time + (tz->zone * INT64CONST(1000000)); -#else - tmp = (tz->time + tz->zone); -#endif r->lower = r->upper = tmp; gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page, @@ -220,7 +217,7 @@ gbt_time_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -238,7 +235,7 @@ gbt_time_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -258,17 +255,13 @@ gbt_timetz_consistent(PG_FUNCTION_ARGS) /* All cases served by this function are inexact */ *recheck = true; -#ifdef HAVE_INT64_TIMESTAMP qqq = query->time + (query->zone * INT64CONST(1000000)); -#else - qqq = (query->time + query->zone); -#endif key.lower = (GBT_NUMKEY *) &kkk->lower; key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -280,7 +273,7 @@ gbt_time_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(timeKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(timeKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -333,7 +326,7 @@ gbt_time_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -344,6 +337,6 @@ gbt_time_same(PG_FUNCTION_ARGS) timeKEY *b2 = (timeKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_ts.c b/contrib/btree_gist/btree_ts.c index ab22b271d3..592466c948 100644 --- a/contrib/btree_gist/btree_ts.c +++ b/contrib/btree_gist/btree_ts.c @@ -3,6 +3,8 @@ */ #include "postgres.h" +#include <limits.h> + #include "btree_gist.h" #include "btree_utils_num.h" #include "utils/builtins.h" @@ -38,7 +40,7 @@ PG_FUNCTION_INFO_V1(gbt_ts_same); static bool -gbt_tsgt(const void *a, const void *b) +gbt_tsgt(const void *a, const void *b, FmgrInfo *flinfo) { const Timestamp *aa = (const Timestamp *) a; const Timestamp *bb = (const Timestamp *) b; @@ -49,7 +51,7 @@ gbt_tsgt(const void *a, const void *b) } static bool -gbt_tsge(const void *a, const void *b) +gbt_tsge(const void *a, const void *b, FmgrInfo *flinfo) { const Timestamp *aa = (const Timestamp *) a; const Timestamp *bb = (const Timestamp *) b; @@ -60,7 +62,7 @@ gbt_tsge(const void *a, const void *b) } static bool -gbt_tseq(const void *a, const void *b) +gbt_tseq(const void *a, const void *b, FmgrInfo *flinfo) { const Timestamp *aa = (const Timestamp *) a; const Timestamp *bb = (const Timestamp *) b; @@ -71,7 +73,7 @@ gbt_tseq(const void *a, const void *b) } static bool -gbt_tsle(const void *a, const void *b) +gbt_tsle(const void *a, const void *b, FmgrInfo *flinfo) { const Timestamp *aa = (const Timestamp *) a; const Timestamp *bb = (const Timestamp *) b; @@ -82,7 +84,7 @@ gbt_tsle(const void *a, const void *b) } static bool -gbt_tslt(const void *a, const void *b) +gbt_tslt(const void *a, const void *b, FmgrInfo *flinfo) { const Timestamp *aa = (const Timestamp *) a; const Timestamp *bb = (const Timestamp *) b; @@ -94,7 +96,7 @@ gbt_tslt(const void *a, const void *b) static int -gbt_tskey_cmp(const void *a, const void *b) +gbt_tskey_cmp(const void *a, const void *b, FmgrInfo *flinfo) { tsKEY *ia = (tsKEY *) (((const Nsrt *) a)->t); tsKEY *ib = (tsKEY *) (((const Nsrt *) b)->t); @@ -108,7 +110,7 @@ gbt_tskey_cmp(const void *a, const void *b) } static float8 -gbt_ts_dist(const void *a, const void *b) +gbt_ts_dist(const void *a, const void *b, FmgrInfo *flinfo) { const Timestamp *aa = (const Timestamp *) a; const Timestamp *bb = (const Timestamp *) b; @@ -153,11 +155,7 @@ ts_dist(PG_FUNCTION_ARGS) p->day = INT_MAX; p->month = INT_MAX; -#ifdef HAVE_INT64_TIMESTAMP p->time = PG_INT64_MAX; -#else - p->time = DBL_MAX; -#endif PG_RETURN_INTERVAL_P(p); } else @@ -181,11 +179,7 @@ tstz_dist(PG_FUNCTION_ARGS) p->day = INT_MAX; p->month = INT_MAX; -#ifdef HAVE_INT64_TIMESTAMP p->time = PG_INT64_MAX; -#else - p->time = DBL_MAX; -#endif PG_RETURN_INTERVAL_P(p); } @@ -271,7 +265,7 @@ gbt_ts_consistent(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &query, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -289,7 +283,7 @@ gbt_ts_distance(PG_FUNCTION_ARGS) key.upper = (GBT_NUMKEY *) &kkk->upper; PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &query, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -314,7 +308,7 @@ gbt_tstz_consistent(PG_FUNCTION_ARGS) qqq = tstz_to_ts_gmt(query); PG_RETURN_BOOL( - gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo) + gbt_num_consistent(&key, (void *) &qqq, &strategy, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -334,7 +328,7 @@ gbt_tstz_distance(PG_FUNCTION_ARGS) qqq = tstz_to_ts_gmt(query); PG_RETURN_FLOAT8( - gbt_num_distance(&key, (void *) &qqq, GIST_LEAF(entry), &tinfo) + gbt_num_distance(&key, (void *) &qqq, GIST_LEAF(entry), &tinfo, fcinfo->flinfo) ); } @@ -346,7 +340,7 @@ gbt_ts_union(PG_FUNCTION_ARGS) void *out = palloc(sizeof(tsKEY)); *(int *) PG_GETARG_POINTER(1) = sizeof(tsKEY); - PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo)); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); } @@ -395,7 +389,7 @@ gbt_ts_picksplit(PG_FUNCTION_ARGS) PG_RETURN_POINTER(gbt_num_picksplit( (GistEntryVector *) PG_GETARG_POINTER(0), (GIST_SPLITVEC *) PG_GETARG_POINTER(1), - &tinfo + &tinfo, fcinfo->flinfo )); } @@ -406,6 +400,6 @@ gbt_ts_same(PG_FUNCTION_ARGS) tsKEY *b2 = (tsKEY *) PG_GETARG_POINTER(1); bool *result = (bool *) PG_GETARG_POINTER(2); - *result = gbt_num_same((void *) b1, (void *) b2, &tinfo); + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); PG_RETURN_POINTER(result); } diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c index 99cb41f5f5..bae32c4064 100644 --- a/contrib/btree_gist/btree_utils_num.c +++ b/contrib/btree_gist/btree_utils_num.c @@ -48,6 +48,7 @@ gbt_num_compress(GISTENTRY *entry, const gbtree_ninfo *tinfo) leaf = &v.i8; break; case gbt_t_oid: + case gbt_t_enum: v.i4 = DatumGetObjectId(entry->key); leaf = &v.i4; break; @@ -122,6 +123,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo) datum = Int64GetDatum(*(int64 *) entry->key); break; case gbt_t_oid: + case gbt_t_enum: datum = ObjectIdGetDatum(*(Oid *) entry->key); break; case gbt_t_float4: @@ -159,7 +161,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo) */ void * -gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, const gbtree_ninfo *tinfo) +gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, const gbtree_ninfo *tinfo, FmgrInfo *flinfo) { int i, numranges; @@ -181,9 +183,11 @@ gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, const gbtree_nin cur = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[i].key)); c.lower = &cur[0]; c.upper = &cur[tinfo->size]; - if ((*tinfo->f_gt) (o.lower, c.lower)) /* out->lower > cur->lower */ + /* if out->lower > cur->lower, adopt cur as lower */ + if ((*tinfo->f_gt) (o.lower, c.lower, flinfo)) memcpy((void *) o.lower, (void *) c.lower, tinfo->size); - if ((*tinfo->f_lt) (o.upper, c.upper)) /* out->upper < cur->upper */ + /* if out->upper < cur->upper, adopt cur as upper */ + if ((*tinfo->f_lt) (o.upper, c.upper, flinfo)) memcpy((void *) o.upper, (void *) c.upper, tinfo->size); } @@ -197,7 +201,7 @@ gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, const gbtree_nin */ bool -gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo) +gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo, FmgrInfo *flinfo) { GBT_NUMKEY_R b1, b2; @@ -207,13 +211,13 @@ gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo b2.lower = &(((GBT_NUMKEY *) b)[0]); b2.upper = &(((GBT_NUMKEY *) b)[tinfo->size]); - return ((*tinfo->f_eq) (b1.lower, b2.lower) && - (*tinfo->f_eq) (b1.upper, b2.upper)); + return ((*tinfo->f_eq) (b1.lower, b2.lower, flinfo) && + (*tinfo->f_eq) (b1.upper, b2.upper, flinfo)); } void -gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo) +gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo, FmgrInfo *flinfo) { GBT_NUMKEY_R rd; @@ -232,9 +236,9 @@ gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo) ur.lower = &(((GBT_NUMKEY *) DatumGetPointer(*u))[0]); ur.upper = &(((GBT_NUMKEY *) DatumGetPointer(*u))[tinfo->size]); - if ((*tinfo->f_gt) ((void *) ur.lower, (void *) rd.lower)) + if ((*tinfo->f_gt) ((void *) ur.lower, (void *) rd.lower, flinfo)) memcpy((void *) ur.lower, (void *) rd.lower, tinfo->size); - if ((*tinfo->f_lt) ((void *) ur.upper, (void *) rd.upper)) + if ((*tinfo->f_lt) ((void *) ur.upper, (void *) rd.upper, flinfo)) memcpy((void *) ur.upper, (void *) rd.upper, tinfo->size); } } @@ -252,39 +256,41 @@ gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query, const StrategyNumber *strategy, bool is_leaf, - const gbtree_ninfo *tinfo) + const gbtree_ninfo *tinfo, + FmgrInfo *flinfo) { bool retval; switch (*strategy) { case BTLessEqualStrategyNumber: - retval = (*tinfo->f_ge) (query, key->lower); + retval = (*tinfo->f_ge) (query, key->lower, flinfo); break; case BTLessStrategyNumber: if (is_leaf) - retval = (*tinfo->f_gt) (query, key->lower); + retval = (*tinfo->f_gt) (query, key->lower, flinfo); else - retval = (*tinfo->f_ge) (query, key->lower); + retval = (*tinfo->f_ge) (query, key->lower, flinfo); break; case BTEqualStrategyNumber: if (is_leaf) - retval = (*tinfo->f_eq) (query, key->lower); + retval = (*tinfo->f_eq) (query, key->lower, flinfo); else - retval = ((*tinfo->f_le) (key->lower, query) && (*tinfo->f_le) (query, key->upper)) ? true : false; + retval = ((*tinfo->f_le) (key->lower, query, flinfo) && + (*tinfo->f_le) (query, key->upper, flinfo)); break; case BTGreaterStrategyNumber: if (is_leaf) - retval = (*tinfo->f_lt) (query, key->upper); + retval = (*tinfo->f_lt) (query, key->upper, flinfo); else - retval = (*tinfo->f_le) (query, key->upper); + retval = (*tinfo->f_le) (query, key->upper, flinfo); break; case BTGreaterEqualStrategyNumber: - retval = (*tinfo->f_le) (query, key->upper); + retval = (*tinfo->f_le) (query, key->upper, flinfo); break; case BtreeGistNotEqualStrategyNumber: - retval = (!((*tinfo->f_eq) (query, key->lower) && - (*tinfo->f_eq) (query, key->upper))) ? true : false; + retval = (!((*tinfo->f_eq) (query, key->lower, flinfo) && + (*tinfo->f_eq) (query, key->upper, flinfo))); break; default: retval = false; @@ -302,17 +308,18 @@ float8 gbt_num_distance(const GBT_NUMKEY_R *key, const void *query, bool is_leaf, - const gbtree_ninfo *tinfo) + const gbtree_ninfo *tinfo, + FmgrInfo *flinfo) { float8 retval; if (tinfo->f_dist == NULL) elog(ERROR, "KNN search is not supported for btree_gist type %d", (int) tinfo->t); - if (tinfo->f_le(query, key->lower)) - retval = tinfo->f_dist(query, key->lower); - else if (tinfo->f_ge(query, key->upper)) - retval = tinfo->f_dist(query, key->upper); + if (tinfo->f_le(query, key->lower, flinfo)) + retval = tinfo->f_dist(query, key->lower, flinfo); + else if (tinfo->f_ge(query, key->upper, flinfo)) + retval = tinfo->f_dist(query, key->upper, flinfo); else retval = 0.0; @@ -322,7 +329,7 @@ gbt_num_distance(const GBT_NUMKEY_R *key, GIST_SPLITVEC * gbt_num_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, - const gbtree_ninfo *tinfo) + const gbtree_ninfo *tinfo, FmgrInfo *flinfo) { OffsetNumber i, maxoff = entryvec->n - 1; @@ -345,7 +352,7 @@ gbt_num_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, arr[i].t = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[i].key)); arr[i].i = i; } - qsort((void *) &arr[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1, sizeof(Nsrt), tinfo->f_cmp); + qsort_arg((void *) &arr[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1, sizeof(Nsrt), (qsort_arg_comparator) tinfo->f_cmp, (void *) flinfo); /* We do simply create two parts */ @@ -353,13 +360,13 @@ gbt_num_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, { if (i <= (maxoff - FirstOffsetNumber + 1) / 2) { - gbt_num_bin_union(&v->spl_ldatum, arr[i].t, tinfo); + gbt_num_bin_union(&v->spl_ldatum, arr[i].t, tinfo, flinfo); v->spl_left[v->spl_nleft] = arr[i].i; v->spl_nleft++; } else { - gbt_num_bin_union(&v->spl_rdatum, arr[i].t, tinfo); + gbt_num_bin_union(&v->spl_rdatum, arr[i].t, tinfo, flinfo); v->spl_right[v->spl_nright] = arr[i].i; v->spl_nright++; } diff --git a/contrib/btree_gist/btree_utils_num.h b/contrib/btree_gist/btree_utils_num.h index a33491bc09..8aab19396c 100644 --- a/contrib/btree_gist/btree_utils_num.h +++ b/contrib/btree_gist/btree_utils_num.h @@ -42,13 +42,13 @@ typedef struct /* Methods */ - bool (*f_gt) (const void *, const void *); /* greater than */ - bool (*f_ge) (const void *, const void *); /* greater or equal */ - bool (*f_eq) (const void *, const void *); /* equal */ - bool (*f_le) (const void *, const void *); /* less or equal */ - bool (*f_lt) (const void *, const void *); /* less than */ - int (*f_cmp) (const void *, const void *); /* key compare function */ - float8 (*f_dist) (const void *, const void *); /* key distance function */ + bool (*f_gt) (const void *, const void *, FmgrInfo *); /* greater than */ + bool (*f_ge) (const void *, const void *, FmgrInfo *); /* greater or equal */ + bool (*f_eq) (const void *, const void *, FmgrInfo *); /* equal */ + bool (*f_le) (const void *, const void *, FmgrInfo *); /* less or equal */ + bool (*f_lt) (const void *, const void *, FmgrInfo *); /* less than */ + int (*f_cmp) (const void *, const void *, FmgrInfo *); /* key compare function */ + float8 (*f_dist) (const void *, const void *, FmgrInfo *); /* key distance function */ } gbtree_ninfo; @@ -82,17 +82,10 @@ typedef struct * (as a double). Here because we need it for time/timetz as well as * interval. See interval_cmp_internal for comparison. */ -#ifdef HAVE_INT64_TIMESTAMP #define INTERVAL_TO_SEC(ivp) \ (((double) (ivp)->time) / ((double) USECS_PER_SEC) + \ (ivp)->day * (24.0 * SECS_PER_HOUR) + \ (ivp)->month * (30.0 * SECS_PER_DAY)) -#else -#define INTERVAL_TO_SEC(ivp) \ - ((ivp)->time + \ - (ivp)->day * (24.0 * SECS_PER_HOUR) + \ - (ivp)->month * (30.0 * SECS_PER_DAY)) -#endif #define GET_FLOAT_DISTANCE(t, arg1, arg2) Abs( ((float8) *((const t *) (arg1))) - ((float8) *((const t *) (arg2))) ) @@ -120,25 +113,25 @@ extern Interval *abs_interval(Interval *a); extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query, const StrategyNumber *strategy, bool is_leaf, - const gbtree_ninfo *tinfo); + const gbtree_ninfo *tinfo, FmgrInfo *flinfo); extern float8 gbt_num_distance(const GBT_NUMKEY_R *key, const void *query, - bool is_leaf, const gbtree_ninfo *tinfo); + bool is_leaf, const gbtree_ninfo *tinfo, FmgrInfo *flinfo); extern GIST_SPLITVEC *gbt_num_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, - const gbtree_ninfo *tinfo); + const gbtree_ninfo *tinfo, FmgrInfo *flinfo); extern GISTENTRY *gbt_num_compress(GISTENTRY *entry, const gbtree_ninfo *tinfo); extern GISTENTRY *gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo); extern void *gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, - const gbtree_ninfo *tinfo); + const gbtree_ninfo *tinfo, FmgrInfo *flinfo); extern bool gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, - const gbtree_ninfo *tinfo); + const gbtree_ninfo *tinfo, FmgrInfo *flinfo); extern void gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, - const gbtree_ninfo *tinfo); + const gbtree_ninfo *tinfo, FmgrInfo *flinfo); #endif diff --git a/contrib/btree_gist/btree_utils_var.c b/contrib/btree_gist/btree_utils_var.c index 70b3794325..3648adccef 100644 --- a/contrib/btree_gist/btree_utils_var.c +++ b/contrib/btree_gist/btree_utils_var.c @@ -25,6 +25,7 @@ typedef struct { const gbtree_vinfo *tinfo; Oid collation; + FmgrInfo *flinfo; } gbt_vsrt_arg; @@ -103,12 +104,12 @@ gbt_var_key_copy(const GBT_VARKEY_R *u) static GBT_VARKEY * -gbt_var_leaf2node(GBT_VARKEY *leaf, const gbtree_vinfo *tinfo) +gbt_var_leaf2node(GBT_VARKEY *leaf, const gbtree_vinfo *tinfo, FmgrInfo *flinfo) { GBT_VARKEY *out = leaf; if (tinfo->f_l2n) - out = (*tinfo->f_l2n) (leaf); + out = (*tinfo->f_l2n) (leaf, flinfo); return out; } @@ -232,7 +233,7 @@ gbt_var_node_truncate(const GBT_VARKEY *node, int32 cpf_length, const gbtree_vin void gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation, - const gbtree_vinfo *tinfo) + const gbtree_vinfo *tinfo, FmgrInfo *flinfo) { GBT_VARKEY_R eo = gbt_var_key_readable(e); GBT_VARKEY_R nr; @@ -241,7 +242,7 @@ gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation, { GBT_VARKEY *tmp; - tmp = gbt_var_leaf2node(e, tinfo); + tmp = gbt_var_leaf2node(e, tinfo, flinfo); if (tmp != e) eo = gbt_var_key_readable(tmp); } @@ -254,13 +255,13 @@ gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation, nr.lower = ro.lower; nr.upper = ro.upper; - if ((*tinfo->f_cmp) (ro.lower, eo.lower, collation) > 0) + if ((*tinfo->f_cmp) (ro.lower, eo.lower, collation, flinfo) > 0) { nr.lower = eo.lower; update = true; } - if ((*tinfo->f_cmp) (ro.upper, eo.upper, collation) < 0) + if ((*tinfo->f_cmp) (ro.upper, eo.upper, collation, flinfo) < 0) { nr.upper = eo.upper; update = true; @@ -321,7 +322,7 @@ gbt_var_fetch(PG_FUNCTION_ARGS) GBT_VARKEY * gbt_var_union(const GistEntryVector *entryvec, int32 *size, Oid collation, - const gbtree_vinfo *tinfo) + const gbtree_vinfo *tinfo, FmgrInfo *flinfo) { int i = 0, numranges = entryvec->n; @@ -338,7 +339,7 @@ gbt_var_union(const GistEntryVector *entryvec, int32 *size, Oid collation, for (i = 1; i < numranges; i++) { cur = (GBT_VARKEY *) DatumGetPointer(entryvec->vector[i].key); - gbt_var_bin_union(&out, cur, collation, tinfo); + gbt_var_bin_union(&out, cur, collation, tinfo, flinfo); } @@ -360,7 +361,7 @@ gbt_var_union(const GistEntryVector *entryvec, int32 *size, Oid collation, bool gbt_var_same(Datum d1, Datum d2, Oid collation, - const gbtree_vinfo *tinfo) + const gbtree_vinfo *tinfo, FmgrInfo *flinfo) { GBT_VARKEY *t1 = (GBT_VARKEY *) DatumGetPointer(d1); GBT_VARKEY *t2 = (GBT_VARKEY *) DatumGetPointer(d2); @@ -370,14 +371,14 @@ gbt_var_same(Datum d1, Datum d2, Oid collation, r1 = gbt_var_key_readable(t1); r2 = gbt_var_key_readable(t2); - return ((*tinfo->f_cmp) (r1.lower, r2.lower, collation) == 0 && - (*tinfo->f_cmp) (r1.upper, r2.upper, collation) == 0); + return ((*tinfo->f_cmp) (r1.lower, r2.lower, collation, flinfo) == 0 && + (*tinfo->f_cmp) (r1.upper, r2.upper, collation, flinfo) == 0); } float * gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n, - Oid collation, const gbtree_vinfo *tinfo) + Oid collation, const gbtree_vinfo *tinfo, FmgrInfo *flinfo) { GBT_VARKEY *orge = (GBT_VARKEY *) DatumGetPointer(o->key); GBT_VARKEY *newe = (GBT_VARKEY *) DatumGetPointer(n->key); @@ -391,7 +392,7 @@ gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n, { GBT_VARKEY *tmp; - tmp = gbt_var_leaf2node(newe, tinfo); + tmp = gbt_var_leaf2node(newe, tinfo, flinfo); if (tmp != newe) nk = gbt_var_key_readable(tmp); } @@ -399,19 +400,19 @@ gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n, if ((VARSIZE(ok.lower) - VARHDRSZ) == 0 && (VARSIZE(ok.upper) - VARHDRSZ) == 0) *res = 0.0; - else if (!(((*tinfo->f_cmp) (nk.lower, ok.lower, collation) >= 0 || + else if (!(((*tinfo->f_cmp) (nk.lower, ok.lower, collation, flinfo) >= 0 || gbt_bytea_pf_match(ok.lower, nk.lower, tinfo)) && - ((*tinfo->f_cmp) (nk.upper, ok.upper, collation) <= 0 || - gbt_bytea_pf_match(ok.upper, nk.upper, tinfo)))) + ((*tinfo->f_cmp) (nk.upper, ok.upper, collation, flinfo) <= 0 || + gbt_bytea_pf_match(ok.upper, nk.upper, tinfo)))) { Datum d = PointerGetDatum(0); double dres; int32 ol, ul; - gbt_var_bin_union(&d, orge, collation, tinfo); + gbt_var_bin_union(&d, orge, collation, tinfo, flinfo); ol = gbt_var_node_cp_len((GBT_VARKEY *) DatumGetPointer(d), tinfo); - gbt_var_bin_union(&d, newe, collation, tinfo); + gbt_var_bin_union(&d, newe, collation, tinfo, flinfo); ul = gbt_var_node_cp_len((GBT_VARKEY *) DatumGetPointer(d), tinfo); if (ul < ol) @@ -448,16 +449,16 @@ gbt_vsrt_cmp(const void *a, const void *b, void *arg) const gbt_vsrt_arg *varg = (const gbt_vsrt_arg *) arg; int res; - res = (*varg->tinfo->f_cmp) (ar.lower, br.lower, varg->collation); + res = (*varg->tinfo->f_cmp) (ar.lower, br.lower, varg->collation, varg->flinfo); if (res == 0) - return (*varg->tinfo->f_cmp) (ar.upper, br.upper, varg->collation); + return (*varg->tinfo->f_cmp) (ar.upper, br.upper, varg->collation, varg->flinfo); return res; } GIST_SPLITVEC * gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, - Oid collation, const gbtree_vinfo *tinfo) + Oid collation, const gbtree_vinfo *tinfo, FmgrInfo *flinfo) { OffsetNumber i, maxoff = entryvec->n - 1; @@ -489,7 +490,7 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, ro = gbt_var_key_readable((GBT_VARKEY *) cur); if (ro.lower == ro.upper) /* leaf */ { - sv[svcntr] = gbt_var_leaf2node((GBT_VARKEY *) cur, tinfo); + sv[svcntr] = gbt_var_leaf2node((GBT_VARKEY *) cur, tinfo, flinfo); arr[i].t = sv[svcntr]; if (sv[svcntr] != (GBT_VARKEY *) cur) svcntr++; @@ -502,6 +503,7 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, /* sort */ varg.tinfo = tinfo; varg.collation = collation; + varg.flinfo = flinfo; qsort_arg((void *) &arr[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1, sizeof(Vsrt), @@ -514,13 +516,13 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, { if (i <= (maxoff - FirstOffsetNumber + 1) / 2) { - gbt_var_bin_union(&v->spl_ldatum, arr[i].t, collation, tinfo); + gbt_var_bin_union(&v->spl_ldatum, arr[i].t, collation, tinfo, flinfo); v->spl_left[v->spl_nleft] = arr[i].i; v->spl_nleft++; } else { - gbt_var_bin_union(&v->spl_rdatum, arr[i].t, collation, tinfo); + gbt_var_bin_union(&v->spl_rdatum, arr[i].t, collation, tinfo, flinfo); v->spl_right[v->spl_nright] = arr[i].i; v->spl_nright++; } @@ -556,7 +558,8 @@ gbt_var_consistent(GBT_VARKEY_R *key, StrategyNumber strategy, Oid collation, bool is_leaf, - const gbtree_vinfo *tinfo) + const gbtree_vinfo *tinfo, + FmgrInfo *flinfo) { bool retval = FALSE; @@ -564,44 +567,44 @@ gbt_var_consistent(GBT_VARKEY_R *key, { case BTLessEqualStrategyNumber: if (is_leaf) - retval = (*tinfo->f_ge) (query, key->lower, collation); + retval = (*tinfo->f_ge) (query, key->lower, collation, flinfo); else - retval = (*tinfo->f_cmp) (query, key->lower, collation) >= 0 + retval = (*tinfo->f_cmp) (query, key->lower, collation, flinfo) >= 0 || gbt_var_node_pf_match(key, query, tinfo); break; case BTLessStrategyNumber: if (is_leaf) - retval = (*tinfo->f_gt) (query, key->lower, collation); + retval = (*tinfo->f_gt) (query, key->lower, collation, flinfo); else - retval = (*tinfo->f_cmp) (query, key->lower, collation) >= 0 + retval = (*tinfo->f_cmp) (query, key->lower, collation, flinfo) >= 0 || gbt_var_node_pf_match(key, query, tinfo); break; case BTEqualStrategyNumber: if (is_leaf) - retval = (*tinfo->f_eq) (query, key->lower, collation); + retval = (*tinfo->f_eq) (query, key->lower, collation, flinfo); else retval = - ((*tinfo->f_cmp) (key->lower, query, collation) <= 0 && - (*tinfo->f_cmp) (query, key->upper, collation) <= 0) || + ((*tinfo->f_cmp) (key->lower, query, collation, flinfo) <= 0 && + (*tinfo->f_cmp) (query, key->upper, collation, flinfo) <= 0) || gbt_var_node_pf_match(key, query, tinfo); break; case BTGreaterStrategyNumber: if (is_leaf) - retval = (*tinfo->f_lt) (query, key->upper, collation); + retval = (*tinfo->f_lt) (query, key->upper, collation, flinfo); else - retval = (*tinfo->f_cmp) (query, key->upper, collation) <= 0 + retval = (*tinfo->f_cmp) (query, key->upper, collation, flinfo) <= 0 || gbt_var_node_pf_match(key, query, tinfo); break; case BTGreaterEqualStrategyNumber: if (is_leaf) - retval = (*tinfo->f_le) (query, key->upper, collation); + retval = (*tinfo->f_le) (query, key->upper, collation, flinfo); else - retval = (*tinfo->f_cmp) (query, key->upper, collation) <= 0 + retval = (*tinfo->f_cmp) (query, key->upper, collation, flinfo) <= 0 || gbt_var_node_pf_match(key, query, tinfo); break; case BtreeGistNotEqualStrategyNumber: - retval = !((*tinfo->f_eq) (query, key->lower, collation) && - (*tinfo->f_eq) (query, key->upper, collation)); + retval = !((*tinfo->f_eq) (query, key->lower, collation, flinfo) && + (*tinfo->f_eq) (query, key->upper, collation, flinfo)); break; default: retval = FALSE; diff --git a/contrib/btree_gist/btree_utils_var.h b/contrib/btree_gist/btree_utils_var.h index 9a7c4d1055..04a356276b 100644 --- a/contrib/btree_gist/btree_utils_var.h +++ b/contrib/btree_gist/btree_utils_var.h @@ -34,13 +34,13 @@ typedef struct /* Methods */ - bool (*f_gt) (const void *, const void *, Oid); /* greater than */ - bool (*f_ge) (const void *, const void *, Oid); /* greater equal */ - bool (*f_eq) (const void *, const void *, Oid); /* equal */ - bool (*f_le) (const void *, const void *, Oid); /* less equal */ - bool (*f_lt) (const void *, const void *, Oid); /* less than */ - int32 (*f_cmp) (const void *, const void *, Oid); /* compare */ - GBT_VARKEY *(*f_l2n) (GBT_VARKEY *); /* convert leaf to node */ + bool (*f_gt) (const void *, const void *, Oid, FmgrInfo *); /* greater than */ + bool (*f_ge) (const void *, const void *, Oid, FmgrInfo *); /* greater equal */ + bool (*f_eq) (const void *, const void *, Oid, FmgrInfo *); /* equal */ + bool (*f_le) (const void *, const void *, Oid, FmgrInfo *); /* less equal */ + bool (*f_lt) (const void *, const void *, Oid, FmgrInfo *); /* less than */ + int32 (*f_cmp) (const void *, const void *, Oid, FmgrInfo *); /* compare */ + GBT_VARKEY *(*f_l2n) (GBT_VARKEY *, FmgrInfo *flinfo); /* convert leaf to node */ } gbtree_vinfo; @@ -52,22 +52,22 @@ extern GBT_VARKEY *gbt_var_key_copy(const GBT_VARKEY_R *u); extern GISTENTRY *gbt_var_compress(GISTENTRY *entry, const gbtree_vinfo *tinfo); extern GBT_VARKEY *gbt_var_union(const GistEntryVector *entryvec, int32 *size, - Oid collation, const gbtree_vinfo *tinfo); + Oid collation, const gbtree_vinfo *tinfo, FmgrInfo *flinfo); extern bool gbt_var_same(Datum d1, Datum d2, Oid collation, - const gbtree_vinfo *tinfo); + const gbtree_vinfo *tinfo, FmgrInfo *flinfo); extern float *gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n, - Oid collation, const gbtree_vinfo *tinfo); + Oid collation, const gbtree_vinfo *tinfo, FmgrInfo *flinfo); extern bool gbt_var_consistent(GBT_VARKEY_R *key, const void *query, StrategyNumber strategy, Oid collation, bool is_leaf, - const gbtree_vinfo *tinfo); + const gbtree_vinfo *tinfo, FmgrInfo *flinfo); extern GIST_SPLITVEC *gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, - Oid collation, const gbtree_vinfo *tinfo); + Oid collation, const gbtree_vinfo *tinfo, FmgrInfo *flinfo); extern void gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation, - const gbtree_vinfo *tinfo); + const gbtree_vinfo *tinfo, FmgrInfo *flinfo); #endif diff --git a/contrib/btree_gist/btree_uuid.c b/contrib/btree_gist/btree_uuid.c new file mode 100644 index 0000000000..e67b8cc989 --- /dev/null +++ b/contrib/btree_gist/btree_uuid.c @@ -0,0 +1,238 @@ +/* + * contrib/btree_gist/btree_uuid.c + */ +#include "postgres.h" + +#include "btree_gist.h" +#include "btree_utils_num.h" +#include "port/pg_bswap.h" +#include "utils/uuid.h" + +typedef struct +{ + pg_uuid_t lower, + upper; +} uuidKEY; + + +/* + * UUID ops + */ +PG_FUNCTION_INFO_V1(gbt_uuid_compress); +PG_FUNCTION_INFO_V1(gbt_uuid_fetch); +PG_FUNCTION_INFO_V1(gbt_uuid_union); +PG_FUNCTION_INFO_V1(gbt_uuid_picksplit); +PG_FUNCTION_INFO_V1(gbt_uuid_consistent); +PG_FUNCTION_INFO_V1(gbt_uuid_penalty); +PG_FUNCTION_INFO_V1(gbt_uuid_same); + + +static int +uuid_internal_cmp(const pg_uuid_t *arg1, const pg_uuid_t *arg2) +{ + return memcmp(arg1->data, arg2->data, UUID_LEN); +} + +static bool +gbt_uuidgt(const void *a, const void *b, FmgrInfo *flinfo) +{ + return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) > 0; +} + +static bool +gbt_uuidge(const void *a, const void *b, FmgrInfo *flinfo) +{ + return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) >= 0; +} + +static bool +gbt_uuideq(const void *a, const void *b, FmgrInfo *flinfo) +{ + return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) == 0; +} + +static bool +gbt_uuidle(const void *a, const void *b, FmgrInfo *flinfo) +{ + return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) <= 0; +} + +static bool +gbt_uuidlt(const void *a, const void *b, FmgrInfo *flinfo) +{ + return uuid_internal_cmp((const pg_uuid_t *) a, (const pg_uuid_t *) b) < 0; +} + +static int +gbt_uuidkey_cmp(const void *a, const void *b, FmgrInfo *flinfo) +{ + uuidKEY *ia = (uuidKEY *) (((const Nsrt *) a)->t); + uuidKEY *ib = (uuidKEY *) (((const Nsrt *) b)->t); + int res; + + res = uuid_internal_cmp(&ia->lower, &ib->lower); + if (res == 0) + res = uuid_internal_cmp(&ia->upper, &ib->upper); + return res; +} + + +static const gbtree_ninfo tinfo = +{ + gbt_t_uuid, + UUID_LEN, + 32, /* sizeof(gbtreekey32) */ + gbt_uuidgt, + gbt_uuidge, + gbt_uuideq, + gbt_uuidle, + gbt_uuidlt, + gbt_uuidkey_cmp, + NULL +}; + + +/************************************************** + * uuid ops + **************************************************/ + + +Datum +gbt_uuid_compress(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *retval; + + if (entry->leafkey) + { + char *r = (char *) palloc(2 * UUID_LEN); + pg_uuid_t *key = DatumGetUUIDP(entry->key); + + retval = palloc(sizeof(GISTENTRY)); + + memcpy((void *) r, (void *) key, UUID_LEN); + memcpy((void *) (r + UUID_LEN), (void *) key, UUID_LEN); + gistentryinit(*retval, PointerGetDatum(r), + entry->rel, entry->page, + entry->offset, FALSE); + } + else + retval = entry; + + PG_RETURN_POINTER(retval); +} + +Datum +gbt_uuid_fetch(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + + PG_RETURN_POINTER(gbt_num_fetch(entry, &tinfo)); +} + +Datum +gbt_uuid_consistent(PG_FUNCTION_ARGS) +{ + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + pg_uuid_t *query = PG_GETARG_UUID_P(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + uuidKEY *kkk = (uuidKEY *) DatumGetPointer(entry->key); + GBT_NUMKEY_R key; + + /* All cases served by this function are exact */ + *recheck = false; + + key.lower = (GBT_NUMKEY *) &kkk->lower; + key.upper = (GBT_NUMKEY *) &kkk->upper; + + PG_RETURN_BOOL( + gbt_num_consistent(&key, (void *) query, &strategy, + GIST_LEAF(entry), &tinfo, fcinfo->flinfo) + ); +} + +Datum +gbt_uuid_union(PG_FUNCTION_ARGS) +{ + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + void *out = palloc(sizeof(uuidKEY)); + + *(int *) PG_GETARG_POINTER(1) = sizeof(uuidKEY); + PG_RETURN_POINTER(gbt_num_union((void *) out, entryvec, &tinfo, fcinfo->flinfo)); +} + +/* + * Convert a uuid to a "double" value for estimating sizes of ranges. + */ +static double +uuid_2_double(const pg_uuid_t *u) +{ + uint64 uu[2]; + const double two64 = 18446744073709551616.0; /* 2^64 */ + + /* Source data may not be suitably aligned, so copy */ + memcpy(uu, u->data, UUID_LEN); + + /* + * uuid values should be considered as big-endian numbers, since that + * corresponds to how memcmp will compare them. On a little-endian + * machine, byte-swap each half so we can use native uint64 arithmetic. + */ +#ifndef WORDS_BIGENDIAN + uu[0] = BSWAP64(uu[0]); + uu[1] = BSWAP64(uu[1]); +#endif + + /* + * 2^128 is about 3.4e38, which in theory could exceed the range of + * "double" (POSIX only requires 1e37). To avoid any risk of overflow, + * put the decimal point between the two halves rather than treating the + * uuid value as a 128-bit integer. + */ + return (double) uu[0] + (double) uu[1] / two64; +} + +Datum +gbt_uuid_penalty(PG_FUNCTION_ARGS) +{ + uuidKEY *origentry = (uuidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(0))->key); + uuidKEY *newentry = (uuidKEY *) DatumGetPointer(((GISTENTRY *) PG_GETARG_POINTER(1))->key); + float *result = (float *) PG_GETARG_POINTER(2); + double olower, + oupper, + nlower, + nupper; + + olower = uuid_2_double(&origentry->lower); + oupper = uuid_2_double(&origentry->upper); + nlower = uuid_2_double(&newentry->lower); + nupper = uuid_2_double(&newentry->upper); + + penalty_num(result, olower, oupper, nlower, nupper); + + PG_RETURN_POINTER(result); +} + +Datum +gbt_uuid_picksplit(PG_FUNCTION_ARGS) +{ + PG_RETURN_POINTER(gbt_num_picksplit( + (GistEntryVector *) PG_GETARG_POINTER(0), + (GIST_SPLITVEC *) PG_GETARG_POINTER(1), + &tinfo, fcinfo->flinfo + )); +} + +Datum +gbt_uuid_same(PG_FUNCTION_ARGS) +{ + uuidKEY *b1 = (uuidKEY *) PG_GETARG_POINTER(0); + uuidKEY *b2 = (uuidKEY *) PG_GETARG_POINTER(1); + bool *result = (bool *) PG_GETARG_POINTER(2); + + *result = gbt_num_same((void *) b1, (void *) b2, &tinfo, fcinfo->flinfo); + PG_RETURN_POINTER(result); +} diff --git a/contrib/btree_gist/data/enum.data b/contrib/btree_gist/data/enum.data new file mode 100644 index 0000000000..d03bfced98 --- /dev/null +++ b/contrib/btree_gist/data/enum.data @@ -0,0 +1,595 @@ +r +v +i +b +r +\N +y +v +g +o +y +b +o +o +o +o +v +r +i +o +b +r +g +b +i +o +r +r +r +\N +o +b +v +y +o +\N +i +o +o +g +g +b +y +v +g +g +\N +v +g +i +i +\N +v +y +i +r +\N +r +\N +g +\N +g +\N +v +g +y +v +r +v +r +v +y +i +i +v +y +v +i +b +i +i +r +r +\N +\N +y +r +g +i +y +i +i +r +g +y +\N +i +o +r +y +y +g +o +o +g +y +r +g +v +r +i +r +i +r +y +v +b +i +o +r +\N +o +i +v +o +b +\N +b +g +y +o +v +b +i +v +v +o +y +i +i +i +g +b +b +g +r +i +y +o +\N +r +\N +i +i +g +v +o +y +y +o +i +b +r +y +y +o +g +g +g +\N +y +o +v +g +y +g +v +\N +i +o +v +b +b +\N +y +v +\N +v +\N +i +\N +r +b +r +o +r +b +o +g +i +r +b +g +g +y +b +b +g +y +g +v +v +b +\N +i +v +y +b +b +o +g +b +v +g +g +b +\N +y +r +r +b +\N +r +g +i +o +v +\N +o +r +b +o +b +i +\N +\N +y +b +y +\N +i +i +i +o +y +o +i +b +o +g +r +\N +b +y +\N +g +b +y +y +o +o +b +g +i +i +v +b +o +o +v +i +g +i +o +r +o +i +i +r +b +g +o +o +y +v +g +g +g +r +o +i +i +g +\N +o +v +b +b +v +i +g +y +i +i +g +r +y +i +b +\N +g +y +o +\N +i +i +b +v +o +b +v +r +g +o +v +v +y +r +v +g +\N +v +v +b +y +o +g +i +o +b +r +y +r +v +b +b +\N +i +v +y +r +b +i +y +g +\N +g +r +y +y +g +b +o +v +r +i +g +r +b +b +b +\N +y +y +y +i +o +r +g +g +i +y +g +y +v +o +o +g +\N +b +v +o +y +r +\N +o +i +g +\N +i +i +i +o +b +\N +\N +b +\N +v +v +r +\N +o +b +r +o +b +o +r +y +\N +r +i +b +b +y +v +r +g +r +r +\N +g +\N +v +v +y +r +o +r +o +i +o +\N +r +\N +i +v +b +v +\N +b +r +v +o +\N +i +r +b +g +o +\N +o +g +r +v +y +g +v +r +b +r +v +o +g +i +i +g +i +y +b +i +y +r +y +o +r +b +y +y +b +y +g +b +\N +r +g +b +o +y +o +g +r +g +b +\N +v +v +v +g +b +y +v +o +v +g +o +g +i +b +v +i +r +r +i +b +i +b +o +\N +\N +y +r +g +v +o +y +\N +g +v +o +b +v +v +\N +r +v +y +g +b +o +v +b +v +b +r +r +i +r +v +y +v +y +o +v +g +i +r +o +o +i +y +r +\N +y +r +b +y +y +\N +b +\N +\N +i +v diff --git a/contrib/btree_gist/data/uuid.data b/contrib/btree_gist/data/uuid.data new file mode 100644 index 0000000000..df118d3670 --- /dev/null +++ b/contrib/btree_gist/data/uuid.data @@ -0,0 +1,703 @@ +5ad32d7e-b463-4363-a65f-52475d9fab27 +7787b66e-cf0e-48c2-b989-1e44e8a00891 +75ec8a55-afcf-442d-9e1d-f9d67a15caf9 +6f36f2e9-5e58-4961-9e8d-9c3ca1cfcd44 +78f6d184-f74e-4a38-81ce-a821e301e9ac +1aa4bf49-dd76-40df-a86c-393fd202b710 +98559f1d-00b3-417e-bc57-9053545a260d +ac24b4d7-1a8f-4abc-a02e-b3294497d18e +a425d99f-ee91-419a-8cd1-6908b8c89679 +815f3632-4d1b-42e3-8dbf-68a20e7fbea5 +ae212041-e64a-4ff1-b8c9-b922fc3f2087 +8e580963-0584-4391-9f1e-3e607d435bd8 +9348cf2f-fe8b-4f05-829e-e2f732482bcc +807b3ff5-0dac-46e1-ba10-07753ec4c7f5 +93325505-83f0-41f4-a060-0d6e1a166815 +cabeaad4-0096-4bc8-8d29-29623d3658c9 +8edda4dd-07fc-4457-b9c0-a28a97cdf9ee +4b7b09ae-d11d-4d54-aad2-c7dcbce92e04 +3ba8ace3-be63-4a11-b057-8ede07d49089 +70dfc341-127f-4d54-a8d4-8159eb119a8f +\N +f4910786-02d1-4874-9be7-c2cd5774fa1e +61a8407e-91f4-41f4-8050-32c8659839ac +d23c6778-d021-426b-8435-e7ee7172a456 +\N +77b3bb98-f8a6-4dc1-b2db-174da47ef88d +4ed9962e-9f7b-4bd2-b791-62c87e7e0f32 +\N +bf30fce0-2497-4594-94e2-5dabb58fa3d6 +289dcd8c-8046-4748-b1e7-3afc51e3791b +622aa432-d5e9-4d02-8a3f-09813dcc00cf +f95c69c4-7d22-41c2-89c1-6bcc6835d478 +2b4ed1c1-38e2-495c-8fba-f2060b983f8a +05905429-70b8-408b-a9b1-b8f00522ec7a +8b1dcbd2-10ca-4a37-a080-a1c846519370 +a6225a20-25c9-4afe-bc33-e6f600eb57a3 +b0121e3f-157c-4c8f-acf3-78dfc752bcb5 +\N +\N +11f510fb-ae06-4542-b936-cb5713908e10 +\N +26293032-dbd0-4914-be29-c060c5adf98b +d3d0b0ce-b60c-4864-9557-ba4a9cca8b1f +16c70611-bbfd-4a46-95fd-3c9e12ff4641 +27dc15a1-5518-4310-bb31-52da9f148afc +a86c2830-282a-4a74-8b5a-e5b562325d82 +8afadc39-3a85-490e-a31e-18d5e787a639 +1159af7d-4d72-4651-a013-9de9f3f03002 +5835b0b1-6a50-44c2-ab41-81fa34a29411 +\N +e031ab87-ccb2-496c-9eba-9966415f82d7 +\N +67b1194b-b700-440e-aa35-93f118707632 +a9a88b6a-c2f0-4de6-ab99-b34853aae425 +c9e3aeda-2001-4ab9-aaf1-57b33d841960 +6bbc3fae-1495-4178-8445-04960dd97c56 +bd7e52f5-4362-4447-9f7c-da14ed54266b +145fd346-4057-4e91-aa82-594d7a16ebce +\N +029d9c72-77e1-4951-b185-30574d00862d +d9233d92-3491-4985-a6bb-763f177535a5 +abe1ffca-8325-4041-8acd-b08904f76cb0 +0c8afca8-f720-4c4b-aab4-e061011db5ca +4f636f73-71e8-4b14-b4c5-25233d115421 +f05a62f1-f495-4ea1-9e20-433c67d2c94e +a21ee4b2-e9bd-4510-9b74-0bac543fc378 +a41bf7a6-92c2-48f1-80ef-0c3d4e002b5f +ed6b5777-b1b4-4e85-ab12-ce1f8054ced1 +04b9cc30-6d01-4f5d-9254-81eb78fbb4af +a89561d0-88e2-43d0-9548-35be00b1f028 +c77ad187-1ed9-4b25-a474-35bf8481cf25 +cc4cf4a1-aa64-4916-ae50-5fa7bb5c3b3b +\N +70337711-dd30-4454-bd6c-0e8f77ba34a0 +1ed02b49-aace-4c5b-a1cf-bc20e2edf03c +84b0f797-baa6-448f-ba29-0234e64ac30b +40556603-648c-4359-9c4c-5e76f21efd1f +af21ec76-27d6-47d8-9ede-1b08049410d3 +0f889e8a-e3b5-46bb-b222-e91cf32623ff +3f5403c1-16b1-4a36-bf4b-77333226e8e4 +5c5ebc3f-aced-4036-9b4a-59e19bd9c74c +8acec555-d518-422b-b416-19a2c5cbf825 +ab1effba-c614-4af2-9c9a-df8f6566f427 +4ae0722c-1c55-4ef1-a9aa-ddd9c43f3dd0 +77e2f0bf-cff2-4128-92da-c6d4bc656267 +2e643fa5-23db-4073-8dac-6f714f823af0 +d6624eac-e776-4e18-9d3c-d6c265a8c378 +\N +623353ca-0675-4a05-94cf-7bb62e49aece +2d29a192-0272-49fa-99ea-9b30e81f0b47 +54cf75a7-6ece-4e20-8f24-036f3daa2fa1 +8964987c-bacd-4474-83a7-2ea51ac7ff3a +87f3e366-6f70-4304-8789-e03f94adcadb +603c2f61-c91c-4f7b-9de4-ad43ea68b00a +fd036a77-ee2a-4c33-a985-709d8667c1a2 +be90eb3d-c147-447b-8ff1-2a62aba6ef3c +448ec399-4249-4daf-85f8-23ad8cc7fbdf +9788716f-1852-4b37-a2c4-f4f65d4eeef6 +bed0413b-64f7-4cf3-b2bd-765b10a31ba8 +6add9145-07d8-4d1d-bad0-57c2a914b90b +\N +caf27337-384f-4f51-92ce-76bd5dbf7317 +0f73ab51-4dc6-4cbf-b74f-faced034c866 +0b8c972d-f1e4-4150-bd9e-baee71af1ca9 +0567dd6d-0386-4002-849b-591d1512cc1e +4c783517-29df-4efa-8f40-12150943e6ab +1be634a9-77cc-4e17-8b24-817067dd2235 +ab233cca-286f-4977-ab03-0c9870b17934 +88d77def-4019-4b0d-8de5-47f3b84b4c1f +036f53a5-5270-4844-bedb-251512538a33 +\N +\N +afca4ef6-56ac-4bbe-8c44-f54858b55ab7 +8613d2fe-d9b6-4491-a70d-8de3ad41b4df +f084c252-43db-4735-bb47-eb45741cedf8 +d35f523d-4c8b-43a7-8839-2c5567ed4934 +40677618-afed-45cd-9299-ff715494d56f +7a5aa5c4-884f-460f-a9f4-a0033eb82de2 +26007d37-61cc-4386-a85e-08e56de23a2c +3753e811-da3f-4a23-92ce-1ae502c99195 +2dfb1eb2-56bf-4b0f-b2f8-3ec2dc2b0191 +551d2a52-f84a-42e4-aa2b-84f7000a3926 +\N +\N +b9cd6a67-e12b-4a0b-b935-92e267ea776f +fd4ec635-a1e5-4fcb-89a3-57bf8aa408b8 +6599e40d-a602-4a54-a24b-a694432992a2 +\N +55e65ca2-4136-4a4b-ba78-02ecb8053cf3 +c5b2bc4d-acf9-4b11-ae9a-02ecb8053cf3 +34bb5f60-3e47-445a-acbd-a5889334f4d5 +743008b5-0779-4d50-8662-1c5988335c33 +71317e40-193f-4a7f-a94a-a188847b0249 +23f14966-adfc-4460-84fa-14190d33d55f +d1bd4bba-8a17-4e8f-93d9-ed09f4719ea4 +b5964792-f607-4cbe-9759-3e459a90fe5e +c49c96e9-77ec-4b75-a3d3-00f9fa8c9ac2 +842f694a-426c-45c7-a7a9-5d750adbdacb +c48446ee-c9f6-4038-9ca2-e32f8706c36f +fd0ab627-022b-4fa5-bb9d-0ada8088c9e6 +b27dd1a0-7938-4e42-a9e7-93672a7d85a1 +\N +5ae9add1-28d5-4fd4-bf1d-f5a3ebabc31e +55e65ca2-4136-4a4b-ba78-cd3fe4678203 +25706dc9-61d1-4c67-be11-22f953132d48 +723f5921-c592-40a8-955c-7f46995b8173 +69c29563-77e3-43e6-93e4-d6ed321a294e +8d6b7ea4-d062-4fb9-a20a-d718abd9e331 +b1e945bc-2a8e-4d1c-b9e7-322b95d31b86 +4ef84213-0b39-4d86-80bb-14d2376f77cf +fd3daf86-96e0-42b7-b83a-c2781aa28e4d +ad6c141d-e257-410b-8596-77153d37b428 +40dd6844-7b56-4f39-a7cb-c00ae3870108 +883559f7-e3af-4f44-aff3-a4bd906e5f86 +98c04f9a-d3b7-4d21-9f89-43ef124d48fa +\N +55e65ca2-4136-4a4b-ba78-698a09529f02 +ee145a4b-6016-4914-b947-698a09529f02 +931473e1-2cb2-4951-9ab0-039396dc2ccb +0df573b5-3ef9-40ef-9b05-208e649965d1 +b13a6d18-33c9-4aa0-b167-88542cd5d8ba +73cbd742-37db-4497-9a30-e10279e9cb10 +63a68ba8-e223-4db7-b56f-459d25d3f873 +aa2e9b1e-e09c-4848-983e-aa4adf34938b +2d58bb19-ac14-42fe-b427-59798b8d9fcb +5d3b3f34-ce9a-4ee6-9400-c7bf7576ffa8 +41945dd7-9fb0-47af-8ee9-10e5a530da3e +\N +c3a9b409-4e0c-4426-8a3d-a87236d189bd +84da10fe-818d-4a3b-85fc-d4c169ca92bc +12a7071d-902d-4438-9c40-c2cd875b51f7 +2e7e60fb-ba46-4005-b6b5-e3630b699254 +9782a6b8-d676-45a8-8991-1c44e60764cc +6760f7e2-6b5a-4ce7-a66a-9014068fc8eb +\N +\N +2dab7131-2ba8-4def-9b28-00c0b1ed929a +9d3df737-16fd-481a-bde6-12a08a01e2f5 +d8390c84-1e90-4759-bd72-2629ca4c51f4 +0c1d5e42-5070-4e51-8076-8e8c15148f99 +9374026b-0ec3-4c44-b656-36520d322eec +\N +\N +ad0aa9a0-122c-4025-b889-70778af8e6e4 +948d0dea-5d30-45da-8867-09d2e1fbedb9 +d295f644-5f4a-4808-b966-b14e73d9fa2a +2de79a17-f91b-4310-ad74-dfa0729ff94d +\N +23eda39b-4f4f-4355-aeba-72771f922d20 +bccc111d-6f15-42c4-95c2-2c3d3329e83c +c4f864e2-f81a-4b65-bd7f-cac745bd7f32 +10447976-1ef1-4599-85fe-d837074c6ea0 +8eecc1f2-77b2-4058-811c-11efb47eb662 +98ad70bc-39b0-4b33-a104-241c6970c4c3 +15c04e8c-69d9-44b7-a177-d08c5b181045 +c6de918d-6536-47b3-b80f-80d049cfe550 +548321e5-cde7-4d74-9bb3-929244806e13 +7e046db3-d1dc-46b3-81e2-fc867ee83298 +5dd5cc44-4ad1-4658-9b4f-509c35a4976e +1aecf136-7583-4d31-980f-7fa5c4610e50 +ddb79c27-a80d-483a-9a52-c61ae2f4f93f +e7aa3080-4d09-43ae-8a2b-5b539ffef909 +aeba0aac-6b8d-4d8e-a5b8-a37359c457c6 +64b5c8e8-118b-414f-9da3-73286249243c +8b72b6d2-d3bb-4e74-a79f-577ff8767777 +dccd9254-24b4-4269-bf07-cfbbd349df54 +51cb6ab8-06c7-4280-9e0f-b2b9dcf026a3 +1f3d16eb-895f-48dd-a9e2-18eda41dacea +3ecc08b6-2b67-442c-92ef-b74af4cb4f42 +b55c7f4a-3d05-43b9-846a-23b86a73d202 +4f54dfd8-39fd-4178-bafa-97b34f131552 +d6874b48-96c0-459f-8dd1-7f7dcbc44b22 +93a213c8-6e28-4eb7-8a94-e1b18e8bebd6 +0743014a-e4a0-4eb9-a9ab-23b0d0d96782 +a7ffb6c6-4e3d-4d08-9cbc-297d8b1bc29e +45e788c4-4054-4d80-886a-e4f006936b7d +c51c64be-f130-40c1-95b9-adb39ef25385 +3af1d3cb-df6b-4d26-910d-a314a57f8550 +\N +477c1cd3-e9c9-4252-bc7c-4db57368ffc6 +b49ecde1-7413-4d6e-80c1-5df680d25617 +1f127009-eb85-4e7d-bfb1-1d804d86609b +\N +25e6a224-70a3-44dc-8faf-50feecee3480 +79094fc2-36d9-4521-bf36-0f6e0850fb6d +cb9b1ab9-96e3-4ac5-aa51-8c3c47437893 +c09d4ab8-5f03-496f-ad31-4b9e7348b9da +d5ab17e1-7017-4fbf-a30e-73a97a85a852 +\N +8c937920-7219-43fe-8436-97aca339a5e5 +b8d001a3-036c-4f15-846d-32e229d66985 +bf2cae32-9096-4ae4-b0c7-efdafdd16e0d +d916d001-fc9b-4e05-985c-b172b873c217 +\N +\N +e2c40a42-ef79-4754-a089-e254345896c3 +953fb2d0-d53a-4cc9-ad48-547263b85399 +8e2b1b14-1c56-48b9-a2b6-7868b42e6efc +\N +\N +76b7f2cb-00e9-4936-bcd0-ebf1db9c6fec +e24d88fb-6219-44c7-9f70-d34e0b274cdc +fda48ed2-113d-4403-b2dc-57f7afe25842 +9aa44ba6-6c48-418d-8689-eab1112b3fbb +42936d25-09a3-4af5-ab98-8c1216c5eca9 +\N +a9ddecb1-b762-4cda-a785-cce77c51c67a +f733a811-b83c-4a29-a59c-786825ea2070 +95e94fe5-eea0-4656-bc34-15724a4785a2 +521c55e4-3a5c-4064-b42f-0e8023af9c94 +a3379a9d-4b32-4956-a814-57b46db4bdc2 +0e46cef6-49ed-4a54-a6be-debcb7546583 +\N +1b1f4424-5c05-4ee6-8eb6-1deb677c743f +\N +caaf024d-ef32-42e5-8875-6fe563e7e18a +\N +98e9ce77-8677-44aa-8703-4633dd089225 +66037e2f-634f-4096-8823-9ded5cb533bb +\N +f6726b9f-8a0b-498e-8479-3ffa7af0cd62 +\N +4fd27a3c-2dd1-4f3d-9f48-a50407e9d1a3 +75737e5a-a509-4239-a627-99e430a4c3d1 +6a8bd5d4-7545-4dc8-86c3-003321cfd437 +73e26e64-baa7-4a9b-8833-8bd822c47146 +\N +d6de86fd-3fae-4de2-9082-e86e53b6ad7c +1143e251-091a-408f-b7b2-c12d05b42c2c +4cc745fe-4667-4041-b941-4a3a23f9c727 +ae7b16f4-9ba8-4dc0-aae7-f78258d4993b +38c5d6b2-ae34-4054-aa07-9a689afb23e1 +3421dcd4-553b-4ba6-b661-5cbf687b982a +\N +75ab9d18-5ba5-479c-b3cc-bcf3c41618b8 +c5a768bc-7765-4973-bd8b-a9528279a0a7 +e7b98405-47e6-404d-ab16-bb76fcca2b1d +\N +6b764c11-a1f1-4ae2-ab87-88bdf7597cbe +\N +e439cb96-6502-4207-a0c8-2fa6bf583bd2 +\N +0d3089c0-3e26-4425-b48a-3e8f18c8b5a2 +1a625bc4-f83b-4735-bf01-a9f748080fa5 +1d1f382c-c702-4c59-84f1-4b4e2f01d1ca +8d3b094e-112e-4535-87b3-04272426a956 +616f4879-2a33-4a5f-a776-08462d27ee1b +e2c83f19-3163-487c-8fc1-7fc00d40ca75 +dbf94f48-5f01-46dd-a13d-def8f9dbb6e0 +8c37b9f1-9766-4a5f-a222-eded294dad76 +\N +4cb9a4a8-e4bf-4757-9d68-36a3e24d7b6f +071ba528-c84a-45fc-8ac5-2368e6c640ae +ad34c9ca-0a4b-4c23-b64c-ccffa1f4dcd8 +4915fbe9-84b6-4bec-b6b4-50ff0cc49e9f +622ef77b-e22d-4ca2-850b-298abb39d870 +3e1803c7-8109-43f2-a742-b4ae6e56be55 +\N +0de0b43f-3e59-4060-a43d-baee17dd2c57 +\N +1a8e57f0-e65b-4e55-b46a-207825e00287 +\N +\N +1877c37c-3567-46e6-9afe-222ac3ccf36f +8f4bab7d-9454-4081-b1af-8e4956ab940f +c1d23aab-ce09-4afc-a95c-b9b0f7b9c16a +04761b0d-40ce-4cb2-a7e0-b1f6d582be26 +a29059f5-d4bc-446f-b903-6f09ec57a410 +4f9fcf12-54ff-442e-a7e3-6aabf45fa474 +facf76d4-e82b-44a3-9d7c-50a84395a2dc +\N +84484041-8e47-4349-91d6-418e9b584750 +44b949a1-502e-4309-af86-90bdfb04a085 +c5da9f0a-cca7-4b5b-82c2-1108e10989df +98e487b8-9527-4fcd-a20a-812c909f1342 +e950bf7c-280d-4774-8891-6cf6da8c8ab4 +d5a28af5-f357-4de9-a8e3-3518a8ca5556 +ae450238-e659-4253-90ee-afd66a676515 +294509e2-b4e1-42a3-8982-b7dca385e4c9 +9546a087-6cac-47d6-b55f-3828df9aec99 +4a715abf-18d1-4ffc-9ea3-fd3f069502d9 +e0e07aef-ce8b-4499-befa-430724707d66 +4df8c6d6-278e-4ba4-af45-7240805ce0a0 +1637abb1-1d05-4055-98fa-296fe8f8bbf2 +e3850663-b73e-4f0a-8107-0775825783ef +7b2dbbbf-2c82-49dd-9383-0edd1aee6d89 +2b99818e-2c53-4ab2-8f78-29bdbaa208b2 +22787985-d2c1-4af4-8b90-7bc441123952 +153166fb-923e-41d4-8413-edad8959a8d5 +732fab66-3055-49f8-807e-463fae914988 +1c39ccc7-2b24-4760-93d3-3e687ed04e1c +33f3d119-ff2a-4341-8145-b69f67e11192 +0370cd89-a0c1-4d8c-85fe-3df1eddc734b +d5a5e95c-e99c-48f6-9ebb-765651b893f9 +3e73a023-7b53-4323-a1f3-511809d322dc +44d2a25c-933c-4345-8d19-cc52daaa9f11 +fc25f498-455d-4a13-9ca4-9ed2b843da17 +f9737152-aa7c-47a3-b96f-d0560ccea84f +\N +c1140446-9885-4a23-a709-593c0ba3818d +050474c6-e6f5-417f-bcb8-2cc288cc412c +9f07cb2f-aec4-4108-a07a-ea51366043f2 +4d9cf071-119b-497d-88de-0d05d21bac34 +7f5de1dd-3848-4ad8-a0e6-fb4039eec4f8 +f97c4e03-e8ce-443f-846b-82485c8bf04d +2354e08c-8415-4b24-8cc2-b7b7616b6588 +b0a5e7da-9382-4a9f-b64c-0c4e5c3755ca +c4812583-5689-4384-8044-e200f796afee +890b609b-ac04-4bf3-9070-492a5db11e1c +b0789be3-59ad-44e5-b1c6-394df496ae1f +512ee0f6-380b-4758-ae20-b10d578473a9 +ad84303a-90e0-44dd-a7a2-b6015e748067 +986947bf-e924-4fb8-bdec-bd91f1b9d634 +fa576306-7eb6-411d-aa92-9e67678530ee +\N +3d66ae17-f27e-45f0-8b95-d47821b1d09c +e8f40639-2c3e-4802-96e1-b8ccf15762f1 +482c9541-9fa9-444f-8155-ceb86056f2f7 +f8dbf3c1-c67f-4a71-8bb1-b754d2fc4d9d +4764906b-3b71-4ccf-80d2-fb985af7728a +628312b0-5f3d-4856-a3b0-6e7f60dd19ee +37b5c576-d2c0-40b5-91cf-712b41dea919 +e7eefe4c-e647-40ba-bde1-bf4aa780b0ab +\N +4089198b-e1f1-4cf5-aa74-1e32aef1f034 +\N +5133a3c7-b9cd-4659-ad99-0453b433f237 +7d6c0ec0-0c7c-4a98-9d75-74a937b7ecba +74faeb7e-e0ac-4505-b2c5-bad6a39c6abb +ad895aff-3527-423b-bc82-607d9586f5fc +19a14c87-ab30-4747-8dc4-7599b4015960 +44955907-c727-4cab-aec3-464332e444fa +\N +b5d4dc6b-b65b-4a36-8f16-cf9760631718 +\N +49b4a368-b530-49ec-b8ed-7664c2dda4cc +e70020b6-eeac-425b-a3ce-9f5ad642f371 +448473ab-2a0f-4200-b7a7-b7583002779e +\N +\N +8a8b74f9-f49a-4f77-b6ab-2011df5645ca +\N +67ae0fa2-08c6-4566-92c4-adae5be3c3cc +1453d200-133e-4df3-9723-eb43bd21d896 +a7f3072e-e567-4e23-9bd0-70aba0554281 +f9caa2e9-6d43-4559-a9e7-6e5d7e7b2769 +b6a0b42b-70a3-42e0-b623-9dee8e2d3b85 +\N +05f2c97f-4c81-43a2-9c7b-8a1cf8de2474 +9287055b-ef1d-4b7f-bc28-fd637adaf530 +e0ed08d4-2521-46b1-983a-03c3cf915e42 +285a1259-f929-4e37-b25b-62af93fb1ea1 +d76631da-ace1-472c-a23d-7d4f2702f771 +80f89372-02be-4ad0-a1e5-9a2490769427 +\N +3c1043f7-f77f-4788-abc7-5615804ccd69 +a3942f4f-27f4-44bf-bf28-6ae854d4d346 +3b741249-a9bd-452b-bd08-9ae337134f13 +5aea4b8f-7dbb-4b7a-ad1b-cee1d93a5393 +cae01e8c-e75d-4c3d-8d90-ee3ebdb011d0 +13aeefb0-dbba-4cee-b108-931f23e286b1 +\N +40aee193-6c24-4a13-a004-9f4dec1ab2cf +2b731fff-597d-4a6e-888c-2ec72fe0dbef +4581b196-149e-492e-9053-5040207dcc19 +68d07598-261e-40bc-a2f5-e8f72cc86104 +94c1ecf0-2bdc-4d0f-962f-226a9617b8cb +2fcfe646-edc5-4397-8032-c4b4cd88afce +2ed39277-375a-4e9a-846d-660fe531bed2 +1244efdd-5d49-403a-9649-2550abae81f2 +281b757c-a039-4668-adf1-ff020ecf17ae +10f75609-865a-4b80-b5b2-39c67aa70c33 +8fb26a73-0535-4603-961e-217353617786 +98a14b8f-9a24-4c1d-b823-26d07b3d0e30 +200890d3-e23d-42dd-85f2-e9e4961495e6 +0faebfcf-6202-4799-b302-40c258d546c3 +714a3c57-cfdf-4db4-81f7-8bc0b9119f51 +c4f0d33b-3b8d-46b7-af89-2f5cce9d495a +\N +0a00b315-9668-493d-ab38-48d20cb5756f +a0a20648-1759-4330-94a3-e39746fdb30a +02890263-b147-4323-b59b-d533ea9d436e +ed12380e-14ff-4ec2-a47d-1b57dcd9cd68 +6d23b5ae-ca06-4aa7-a282-d96315c3ba83 +dac7a9bc-97e3-46e5-a543-ee4071ae9f0a +65995243-a887-407f-b5de-f6b25b07e3a5 +4831f146-46fe-4d57-9569-80852b0f655b +46037b99-bc54-4266-94cb-384c062346e4 +10661769-8d53-476e-9be4-440258481fdd +5ddd5380-3f03-4a76-b682-b65e1ff1a431 +5e1fe5a5-14fd-46e9-afd2-54908de1582b +62c070e4-7647-4c21-a4dd-cd8203ee6b20 +\N +efd4c5c8-629d-41be-8982-e3b8352f96e2 +16a2c85a-f455-45bc-8a29-38f7f664fc7c +0360c257-6ea8-4d6e-834f-43f37a7d8f4e +e2562225-53fe-457e-b538-c089d3946aec +ddbaebbe-8294-4f8d-8452-4a46d1b43c53 +dca63b45-648f-4b4c-a36a-c53f3e0abe28 +04e91983-21f7-408b-b4eb-aa6d9359f37a +905c498a-4b99-49af-89cd-fd1d022193e7 +413d7f85-6bdd-4d50-8859-afd317c841c4 +412ca3ee-90e5-46ae-adf3-80aa6ceee633 +1a8020e7-8671-46e4-b2d3-705e206723bf +9414f47f-088c-4fb1-98f2-bc020b0d550b +8f48e9ef-ae2c-4d0a-acc6-8d4b18622df5 +614cbf04-fb3b-4678-834b-da05f70bd529 +0c96ce68-9135-4199-a351-05f9dfc641e2 +f3dfa1a0-c156-435e-9f2c-662c345b92db +\N +d7c6b8cc-2d67-41c6-bae0-3ab23f8ad65c +b8ae6503-dbed-4455-85ce-6b985b4338ff +3ba1530c-911b-463d-aa61-5d81850b5fcb +\N +0f71da52-80da-4bc5-88f7-013603f8ef06 +f59318ec-1851-4beb-b02e-6e9ca7f2391a +8b712321-af06-4af2-8654-1e174851ae59 +\N +\N +c30e01ed-17eb-495d-8381-dc87cd280002 +10537620-20b9-4706-a1fb-ec470349e4d8 +bc669e4a-bef3-4635-a3c6-47e70a307e70 +5d965491-8d0d-4f7b-bab4-b615dd97dbcd +fa76b0d2-1c46-4855-9381-3ec02b21b475 +311c71c5-e5e2-4224-aa57-fc79adb0d037 +82f18b3d-dd3c-402c-a54b-afe92a8b4582 +8a39cdbf-39c5-41a9-a4cb-c334cccc0414 +396ed0f3-8c28-40a8-a5d0-b41d2448c618 +4dc37b03-f161-4436-bb41-3e114f78bb96 +1de1ff4c-5b47-40d2-a002-ff331900c4ec +7b248f50-920b-45f3-b20b-19d75590ef3d +\N +eba46805-9b82-4ebd-84d2-5aa6cb3d8a48 +2fef1c4c-d97c-423f-923d-cbac15961fc5 +7ae4af7a-3759-4ecb-9d3f-ed5e124ab08c +f812e63b-20a4-4f58-90be-e6c7357d89fd +cd91b9fe-5daa-4087-94a7-459c54d24d39 +54f4f7a3-c581-4bff-9bc1-82d8aaec2d3c +\N +d900a862-a0b2-4776-b418-af075881c53d +593143fa-99b6-464b-b563-33e201668db0 +1d8ffc5d-1011-41d5-b3bc-18e0fe5b7375 +93773b54-be94-4b99-9bcc-e181f1b09978 +\N +7fe6a809-a67e-46ef-b686-4a982a6f6fc1 +00c65908-2e09-4974-8c61-37ec926e74fc +d6f2cc12-6d93-4159-b247-70db9120217b +29806fd9-24bb-4e50-a228-8ad6c17559ab +bd093e48-01be-4a09-a8f0-33a2bfcf23a7 +13d3db20-68e0-4cb2-8530-90648e6a756c +b5bb9551-bb70-4589-a12c-15350d85232b +6736895a-671b-435c-85b7-133c65b09cc8 +a609184a-9035-4b75-b10e-838465bace14 +98c084a0-9c30-4dc0-b8a2-2b818f650034 +122afe72-60e5-497a-92f7-c8139339f999 +6b2b6d77-f49c-4b37-a57f-c8ab6f8deff3 +6e133f18-5a70-4717-a750-1c2ee9ab459a +\N +\N +96e2dc30-cc7e-4c9e-bbbc-e4af1ce9b5f6 +0a430277-f67d-44df-88e6-3ae2e78b8a1e +15445ff0-2087-4fa6-857e-baba197a3ec9 +5a8aba7a-1feb-4acc-b57d-2520233ec15f +a17ef384-5204-4240-a493-7db5dc28a6b2 +bfa0fd7d-7d18-4c57-a066-c94be05d0730 +4de53a8d-d76e-446e-9b68-48618314f2c4 +21cb20a2-36ab-4756-8925-cf8bade61148 +a2376936-9836-4397-a3eb-e779e498ab2d +f4b3d211-79bb-4256-a460-26dac56d9755 +4c4be10f-5141-429a-9ea6-24eb1fbd5330 +a45bb987-b935-44ae-a410-c82b1f895eb5 +b341e29a-e069-4ba2-b2ba-279b53c1fcda +68a4e4f8-9d7b-48f0-8614-e2a2072c859e +dc20e7f1-b79d-4135-b90c-cd87a265169a +1c5bfa73-8814-4f5d-9718-a12417814c41 +3000c028-3656-4455-b095-0b9f5ab1dc9e +\N +3d13811e-7b7a-4779-ab87-5257a1c702d1 +a9ea4067-e53c-484c-87ae-bdb218fbec40 +9c3925cc-4dba-4dd4-8000-c646f45db751 +f2e7334b-9840-4e9c-aff4-d7ac58ecbd91 +889d6968-a515-4458-a353-4a3d8d7528bd +dc02f6d6-fec3-4c2f-ace2-6a124a61f079 +9f48ed94-313c-4607-9c23-d3a1b20eaca4 +\N +\N +2428f056-4dc2-4db1-a110-20bb54a3037a +3cc8dd23-fd3a-4855-8ffa-23d1efa4fdbc +bd3e2f1d-5869-42ff-b1c5-3f65ae2d1974 +\N +\N +8330c73d-0dc5-4caa-be02-10e136137804 +f202e559-6ab2-4b72-a6e3-1bf16cfe8bf5 +54e42957-25fa-46c7-a939-eaaa4b54a5b4 +\N +7c3b2d81-44ca-49d1-8b08-c33f691c4f3c +\N +5083bb0b-7fa7-4ee1-8e51-ce20ee53a16c +ffdd64de-2c27-4858-8baf-b179f0fa690c +6e4632ce-f908-4c13-a15a-ac5cdca38c76 +e428a015-0cca-4b09-bb5c-ee4bffbd2de7 +\N +\N +d7a270ce-7ac4-49bf-a531-e56960f56850 +80b82b49-3984-4b6f-8690-7578f992d987 +824d6c6a-273d-4bfe-8ca6-197c8477d6c1 +\N +\N +5dddc93c-c4c9-44fc-8916-a826089245a4 +21357386-e17f-457e-93b4-77295904e67f +f9598cd5-3c4c-457c-b6d8-11049bbc94b1 +05f7fbbb-1660-42da-a154-de4aa6cce4df +a3b6aeef-e8b5-4692-944f-eb5edfd6a0b5 +cb8f1dea-fefa-470f-9a9c-1e169df844ec +\N +4b12759f-10e0-478c-b2d4-7c71be9f837e +3688c161-bc5a-40a4-a9a9-6854b623a139 +\N +a6a6ac8a-b805-4f15-bc8e-71c3679221e2 +ddcdad12-0919-4b8b-acc8-e775aaf6b6a1 +0dcce500-a4d6-4d34-916b-686cc04ceaa9 +2190bba9-e7e6-413a-85e3-40735e791c1b +3f06e070-1530-47a7-8898-a94d82ea59b8 +bfb7ef50-9a5c-4341-a65d-c7b9ccb76d39 +70972a38-8f23-478c-abfd-9dbdca17dd01 +3c6ba50f-9197-4f0b-bf46-ca51aef246d1 +fb4598bf-ffa0-470e-b8eb-2d704fc08bd5 +fb2bd46e-6f43-4b2a-a122-dd6539ccc03c +49f8d0a6-b7b0-444b-90dd-1cb5d08e95e5 +\N +e8b02af9-8671-49d3-a1bf-86e222fe4ecc +\N +26c6af97-9ffc-43bd-a926-da45469c3c52 +773ec08a-9d02-4a8f-87e9-f4460d703952 +286f3446-6e5c-4b91-bb47-d6106346369f +\N +2e1c1f21-8cdb-4a33-96b2-85b3eec11e41 +d7b3ebc0-62a0-419a-a710-f9950d012f92 +3edde810-79fd-4f1f-aa10-4aa472d9384a +a13013f8-e1cf-4902-9c95-8177d8a220a9 +b7c226d3-d115-4bf8-b03c-e7f14eee2169 +91a75836-a7a6-488b-806a-e8f948c8eb46 +b30da379-97b3-4a94-b6a2-2064767d2e52 +befec357-cf20-4712-9805-34910608e2fd +ca95130e-1c44-4733-a872-c0ce24d1b3c1 +\N +f5001512-9140-43c9-a4c8-54e26f71f1cf +a3f2283e-50dd-40b9-83f3-b50dca485209 +3f7ac41e-bc09-4b74-8665-bdde3ebe47a7 +761e1883-d06e-4360-9df5-5c5caeef905c +98cffbaf-dc52-4674-ade0-f930a70b64b2 +370e189c-b821-41ae-b748-a60b6d7660eb +12563667-28cd-478d-b53c-442f7bf12c67 +\N +29fe6754-ba91-49b4-bfb7-12b3dc03081c +87aa5cc1-3332-4bf3-a669-5bb61e56a7ee +\N +8bcd3587-03fd-44a0-aab8-b8aca2bc9eb9 +9ae7c0f8-6038-48af-b01f-0c5bccac6c8f +\N +3e98dbdb-b10f-4f6d-a87a-c8f1f2b1e22a +cd53b5ef-38fa-4d68-a8ff-4eb07f4162b5 +514644a8-fe52-4bab-8bb8-4cb8c7ef7acc +\N +b01b6978-46f9-46ec-844d-1322be2cfcb2 +04675ff9-2d55-413d-a3d0-4ac0ff1d3a54 +a7ee0137-c56a-472c-b4cb-dc41f1177ef0 +bc41bfcd-e5f0-423d-82f7-ffb7da97b5dd +55824064-db88-4077-ad90-945d878e88ba +a30050dd-1a17-4659-b3a6-c4c182ff0184 +82ffa955-b664-4503-9b1c-095404dbff48 +91d67d53-ec53-4dae-95ca-da25c1cfae7d +da144505-c151-42a4-b8d5-19ff810ae6ea +cfa1ddf5-6149-4896-940e-5dc57e4ec766 +c3a56789-f97f-4d5a-b70a-7d24da43bc5f +4547a150-68f8-4984-b7b4-67ee92315b6a +6b6d5e01-b18d-4afd-8b6e-8c3af536efe4 +bf723c93-f506-4990-9e97-b65476044b30 +c3ec8969-1f70-4b19-977b-237ede99a6a5 +78f11bd7-7a10-42ae-9475-eb16ca80f1e0 +cb3bf2fe-2d6c-47e7-b1e7-ce3254d2f800 +3842e996-3d91-4cab-8cce-da007a08328b +4c55e078-603b-4d4e-9c77-c747960f6aff +16d9f806-448d-48a1-9473-4d30df05aab8 +4611148b-cf71-40e6-829f-95ae0f2c8094 +9f3bcfbc-24ea-4105-8cb8-37a0924ff5e2 +4fac2eaa-2bd1-4d9c-bd80-a7ae083efcf9 +dc5546ca-99fc-4c1d-9559-b2ed9cd3d2aa +29721775-9930-4f6c-af20-bb5b5f8d0d73 +f39a6eac-e7d5-4124-9a65-9508bfc53920 +\N +7bc9960c-cf4f-4cca-a752-b28c5805ae01 +0e1c03e3-2cca-4bc8-b160-d6c2e888c182 +b12bb0d6-45d3-4608-9992-be9804a09448 +31bc67c6-1293-47c0-9732-5094e0b996bc +a262ce01-cdf9-47f4-8f48-e94d4b9d73ce +bc7150a9-0593-444c-b7b7-cc142348f1b4 +2f1e9e36-7e1b-49a5-a83e-b330267b5051 +f919c11a-b74f-4543-9798-da31133f90b5 +8672777e-a462-4042-9604-4392bbbd3308 +37b53421-3c74-40f5-9884-b83033e9f596 +1f843010-c79a-4bd4-a0a5-0251e0389722 +c51ecb09-45b4-40ff-9934-877c168c5038 +131335ce-a059-49c4-81ae-c9d98211ff9c +f467b40d-0c6e-40b9-9959-2f7f466f18e2 +44076ea8-1103-4086-9e7d-8bfb9a65893d +79ca8799-36a4-4982-9cd7-bf93fca45d74 +82569d43-65a3-44d7-8836-2db6de03e6f7 +cb5380e0-b075-48e6-8a9a-eee854444d34 +db88f31c-ce62-43f3-9781-8a8404e6ba39 +78f33ab0-a744-4bf1-82a7-c0b319492607 +2e4b580e-7d69-42c8-9f1d-7f232a3ae74c +427b3d53-2792-47d3-9d45-087b30568413 +40518971-9590-45fb-9219-242ab3053547 +fe49087f-d8c1-4769-b814-fd8bc1611b5c +27f8a8ab-671e-4eb2-88b4-2ad41814df1c +39ebe842-6c44-4fb1-a629-3f86323ca5ea +4a341b56-3523-4163-8563-83b9db172673 +513dd3a8-7354-47e7-9eef-d2a9e59a0e18 +b8e38294-7be9-4c39-b80f-bf2c9acfe69f +e1fa23c2-b0b6-47b3-82c9-eef6e930af08 +1b86903e-c395-42c7-b9ad-1a71a1fb52d1 +632161a2-474e-450c-9b70-0f09f512bcba +73f00c2b-ea38-46bb-aae3-4cf205572baf +013839fd-03de-4fe5-a08c-466670de6cbd +5d951cfe-d988-4b69-bce8-37d66598cbc3 +4da7e8f1-edaf-404a-bd1b-e8dd3a838fe2 +1f2c1809-8b85-48e8-ada4-1fdb418fea0e +\N +1bde5bbb-5d63-4d00-b227-1a706315eaa1 +f7ebf8f8-609f-4ce9-b93c-54759305926e +\N +7c2dd991-9377-4001-8486-7f3c3a6bae9c +9fe1e97c-718b-4cf2-b270-4e0b664aaf27 +2141a8f5-da01-47cc-8104-6dd28874d8ac +304096e8-b118-41e0-8174-32dc8e1fc45d +9d5fac3d-e6f2-4341-9e59-9a155bef7b17 +b42cebe1-f01f-4409-bfc2-150aa9f13159 +91adc8a2-266c-4196-99ff-1de1c361c3ef +54d26aee-0309-4af7-9b12-bbb24eb3e4e1 +bd449351-c50a-43b2-9742-1bcb838d4d04 +9fe70798-e3bd-448a-b461-e462702a9aca +\N +c8ef8969-1332-481b-909a-340ff3fd4473 +64c68c64-f815-4bd7-b0aa-ba68bb15f611 +9f271158-ff4e-41a6-a883-913f2b36ae68 +\N +b1082d66-0065-41ac-9bc5-dcea0bbec070 +\N +3ac2d674-2e12-4db1-b998-2470cba43b11 +\N +3061f573-96e9-4307-a683-df8ab30531a5 +01ce8c0e-7672-4023-be71-5dfae5ffa7d2 +06a9e327-29ea-4913-b6b9-90781484eff4 +9735f9eb-89b3-4f42-bfb5-e2bb208b640a +21ef890c-1c8c-4890-8c6d-851eebe68f40 +c35686c4-cfcc-48ff-b6d9-7c8da68dceb1 +3f08e734-1f52-42b5-ba89-738582a7f5b4 +12975217-8a58-4a95-9ede-4ceb0a487a67 +97e186f8-28a7-4340-b781-cd13168daf99 +2336ce4b-3d57-46f4-b460-cdeb89c81fcd +e824b114-66e0-441f-aa94-27feb7a3f672 +b8bf5230-0174-4f16-9470-dd476b9675d6 diff --git a/contrib/btree_gist/expected/enum.out b/contrib/btree_gist/expected/enum.out new file mode 100644 index 0000000000..c4b769dd4b --- /dev/null +++ b/contrib/btree_gist/expected/enum.out @@ -0,0 +1,91 @@ +-- enum check +create type rainbow as enum ('r','o','y','g','b','i','v'); +CREATE TABLE enumtmp (a rainbow); +\copy enumtmp from 'data/enum.data' +SET enable_seqscan=on; +select a, count(*) from enumtmp group by a order by 1; + a | count +---+------- + r | 76 + o | 78 + y | 73 + g | 75 + b | 77 + i | 78 + v | 75 + | 63 +(8 rows) + +SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; + count +------- + 227 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow; + count +------- + 302 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow; + count +------- + 75 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; + count +------- + 305 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; + count +------- + 230 +(1 row) + +CREATE INDEX enumidx ON enumtmp USING gist ( a ); +SET enable_seqscan=off; +SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; + count +------- + 227 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow; + count +------- + 302 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow; + count +------- + 75 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; + count +------- + 305 +(1 row) + +SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; + count +------- + 230 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; + QUERY PLAN +----------------------------------------------- + Aggregate + -> Bitmap Heap Scan on enumtmp + Recheck Cond: (a >= 'g'::rainbow) + -> Bitmap Index Scan on enumidx + Index Cond: (a >= 'g'::rainbow) +(5 rows) + diff --git a/contrib/btree_gist/expected/init.out b/contrib/btree_gist/expected/init.out index afe0534682..ce4559d8b0 100644 --- a/contrib/btree_gist/expected/init.out +++ b/contrib/btree_gist/expected/init.out @@ -1 +1,9 @@ CREATE EXTENSION btree_gist; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + diff --git a/contrib/btree_gist/expected/macaddr8.out b/contrib/btree_gist/expected/macaddr8.out new file mode 100644 index 0000000000..e5ec6a5dea --- /dev/null +++ b/contrib/btree_gist/expected/macaddr8.out @@ -0,0 +1,89 @@ +-- macaddr check +CREATE TABLE macaddr8tmp (a macaddr8); +\copy macaddr8tmp from 'data/macaddr.data' +SET enable_seqscan=on; +SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'; + count +------- + 56 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a <= '22:00:5c:e5:9b:0d'; + count +------- + 60 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a = '22:00:5c:e5:9b:0d'; + count +------- + 4 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; + count +------- + 544 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; + count +------- + 540 +(1 row) + +CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); +SET enable_seqscan=off; +SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; + count +------- + 56 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a <= '22:00:5c:e5:9b:0d'::macaddr8; + count +------- + 60 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a = '22:00:5c:e5:9b:0d'::macaddr8; + count +------- + 4 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'::macaddr8; + count +------- + 544 +(1 row) + +SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'::macaddr8; + count +------- + 540 +(1 row) + +-- Test index-only scans +SET enable_bitmapscan=off; +EXPLAIN (COSTS OFF) +SELECT * FROM macaddr8tmp WHERE a < '02:03:04:05:06:07'::macaddr8; + QUERY PLAN +--------------------------------------------------------- + Index Only Scan using macaddr8idx on macaddr8tmp + Index Cond: (a < '02:03:04:ff:fe:05:06:07'::macaddr8) +(2 rows) + +SELECT * FROM macaddr8tmp WHERE a < '02:03:04:05:06:07'::macaddr8; + a +------------------------- + 01:02:37:ff:fe:05:4f:36 + 01:02:37:ff:fe:05:4f:36 + 01:02:37:ff:fe:05:4f:36 + 01:02:37:ff:fe:05:4f:36 + 01:43:b5:ff:fe:79:eb:0f + 01:43:b5:ff:fe:79:eb:0f + 01:43:b5:ff:fe:79:eb:0f + 01:43:b5:ff:fe:79:eb:0f +(8 rows) + diff --git a/contrib/btree_gist/expected/uuid.out b/contrib/btree_gist/expected/uuid.out new file mode 100644 index 0000000000..a34b024603 --- /dev/null +++ b/contrib/btree_gist/expected/uuid.out @@ -0,0 +1,66 @@ +-- uuid check +CREATE TABLE uuidtmp (a uuid); +\copy uuidtmp from 'data/uuid.data' +SET enable_seqscan=on; +SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + count +------- + 227 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + count +------- + 228 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + count +------- + 1 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + count +------- + 376 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + count +------- + 375 +(1 row) + +CREATE INDEX uuididx ON uuidtmp USING gist ( a ); +SET enable_seqscan=off; +SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + count +------- + 227 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + count +------- + 228 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + count +------- + 1 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + count +------- + 376 +(1 row) + +SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + count +------- + 375 +(1 row) + diff --git a/contrib/btree_gist/sql/enum.sql b/contrib/btree_gist/sql/enum.sql new file mode 100644 index 0000000000..476211e979 --- /dev/null +++ b/contrib/btree_gist/sql/enum.sql @@ -0,0 +1,38 @@ +-- enum check + +create type rainbow as enum ('r','o','y','g','b','i','v'); + +CREATE TABLE enumtmp (a rainbow); + +\copy enumtmp from 'data/enum.data' + +SET enable_seqscan=on; + +select a, count(*) from enumtmp group by a order by 1; + +SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; + +CREATE INDEX enumidx ON enumtmp USING gist ( a ); + +SET enable_seqscan=off; + +SELECT count(*) FROM enumtmp WHERE a < 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a <= 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a = 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; + +SELECT count(*) FROM enumtmp WHERE a > 'g'::rainbow; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM enumtmp WHERE a >= 'g'::rainbow; diff --git a/contrib/btree_gist/sql/init.sql b/contrib/btree_gist/sql/init.sql index afe0534682..a6d2cffc67 100644 --- a/contrib/btree_gist/sql/init.sql +++ b/contrib/btree_gist/sql/init.sql @@ -1 +1,6 @@ CREATE EXTENSION btree_gist; + +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); diff --git a/contrib/btree_gist/sql/macaddr8.sql b/contrib/btree_gist/sql/macaddr8.sql new file mode 100644 index 0000000000..61e7d7af40 --- /dev/null +++ b/contrib/btree_gist/sql/macaddr8.sql @@ -0,0 +1,37 @@ +-- macaddr check + +CREATE TABLE macaddr8tmp (a macaddr8); + +\copy macaddr8tmp from 'data/macaddr.data' + +SET enable_seqscan=on; + +SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'; + +SELECT count(*) FROM macaddr8tmp WHERE a <= '22:00:5c:e5:9b:0d'; + +SELECT count(*) FROM macaddr8tmp WHERE a = '22:00:5c:e5:9b:0d'; + +SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'; + +SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'; + +CREATE INDEX macaddr8idx ON macaddr8tmp USING gist ( a ); + +SET enable_seqscan=off; + +SELECT count(*) FROM macaddr8tmp WHERE a < '22:00:5c:e5:9b:0d'::macaddr8; + +SELECT count(*) FROM macaddr8tmp WHERE a <= '22:00:5c:e5:9b:0d'::macaddr8; + +SELECT count(*) FROM macaddr8tmp WHERE a = '22:00:5c:e5:9b:0d'::macaddr8; + +SELECT count(*) FROM macaddr8tmp WHERE a >= '22:00:5c:e5:9b:0d'::macaddr8; + +SELECT count(*) FROM macaddr8tmp WHERE a > '22:00:5c:e5:9b:0d'::macaddr8; + +-- Test index-only scans +SET enable_bitmapscan=off; +EXPLAIN (COSTS OFF) +SELECT * FROM macaddr8tmp WHERE a < '02:03:04:05:06:07'::macaddr8; +SELECT * FROM macaddr8tmp WHERE a < '02:03:04:05:06:07'::macaddr8; diff --git a/contrib/btree_gist/sql/uuid.sql b/contrib/btree_gist/sql/uuid.sql new file mode 100644 index 0000000000..3f7ad764e2 --- /dev/null +++ b/contrib/btree_gist/sql/uuid.sql @@ -0,0 +1,31 @@ +-- uuid check + +CREATE TABLE uuidtmp (a uuid); + +\copy uuidtmp from 'data/uuid.data' + +SET enable_seqscan=on; + +SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + +SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + +SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + +SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + +SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'; + +CREATE INDEX uuididx ON uuidtmp USING gist ( a ); + +SET enable_seqscan=off; + +SELECT count(*) FROM uuidtmp WHERE a < '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + +SELECT count(*) FROM uuidtmp WHERE a <= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + +SELECT count(*) FROM uuidtmp WHERE a = '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + +SELECT count(*) FROM uuidtmp WHERE a >= '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; + +SELECT count(*) FROM uuidtmp WHERE a > '55e65ca2-4136-4a4b-ba78-cd3fe4678203'::uuid; diff --git a/contrib/chkpass/chkpass.c b/contrib/chkpass/chkpass.c index 9425c089b5..3803ccff9a 100644 --- a/contrib/chkpass/chkpass.c +++ b/contrib/chkpass/chkpass.c @@ -17,6 +17,7 @@ #endif #include "fmgr.h" +#include "utils/backend_random.h" #include "utils/builtins.h" PG_MODULE_MAGIC; @@ -77,8 +78,12 @@ chkpass_in(PG_FUNCTION_ARGS) result = (chkpass *) palloc0(sizeof(chkpass)); - mysalt[0] = salt_chars[random() & 0x3f]; - mysalt[1] = salt_chars[random() & 0x3f]; + if (!pg_backend_random(mysalt, 2)) + ereport(ERROR, + (errmsg("could not generate random salt"))); + + mysalt[0] = salt_chars[mysalt[0] & 0x3f]; + mysalt[1] = salt_chars[mysalt[1] & 0x3f]; mysalt[2] = 0; /* technically the terminator is not necessary * but I like to play safe */ diff --git a/contrib/citext/Makefile b/contrib/citext/Makefile index 3623f9d91c..a6e026dafb 100755 --- a/contrib/citext/Makefile +++ b/contrib/citext/Makefile @@ -3,7 +3,8 @@ MODULES = citext EXTENSION = citext -DATA = citext--1.3.sql citext--1.2--1.3.sql citext--1.1--1.2.sql \ +DATA = citext--1.4.sql citext--1.3--1.4.sql \ + citext--1.2--1.3.sql citext--1.1--1.2.sql \ citext--1.0--1.1.sql citext--unpackaged--1.0.sql PGFILEDESC = "citext - case-insensitive character string data type" diff --git a/contrib/citext/citext--1.3--1.4.sql b/contrib/citext/citext--1.3--1.4.sql new file mode 100644 index 0000000000..7b36651186 --- /dev/null +++ b/contrib/citext/citext--1.3--1.4.sql @@ -0,0 +1,12 @@ +/* contrib/citext/citext--1.3--1.4.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION citext UPDATE TO '1.4'" to load this file. \quit + +CREATE FUNCTION regexp_match( citext, citext ) RETURNS TEXT[] AS $$ + SELECT pg_catalog.regexp_match( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION regexp_match( citext, citext, text ) RETURNS TEXT[] AS $$ + SELECT pg_catalog.regexp_match( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; diff --git a/contrib/citext/citext--1.3.sql b/contrib/citext/citext--1.4.sql index c2d0c0ce9b..7b06198935 100644 --- a/contrib/citext/citext--1.3.sql +++ b/contrib/citext/citext--1.4.sql @@ -1,4 +1,4 @@ -/* contrib/citext/citext--1.2.sql */ +/* contrib/citext/citext--1.4.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION citext" to load this file. \quit @@ -444,6 +444,14 @@ CREATE OPERATOR !~~* ( -- XXX TODO Ideally these would be implemented in C. -- +CREATE FUNCTION regexp_match( citext, citext ) RETURNS TEXT[] AS $$ + SELECT pg_catalog.regexp_match( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + +CREATE FUNCTION regexp_match( citext, citext, text ) RETURNS TEXT[] AS $$ + SELECT pg_catalog.regexp_match( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); +$$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE; + CREATE FUNCTION regexp_matches( citext, citext ) RETURNS SETOF TEXT[] AS $$ SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); $$ LANGUAGE SQL IMMUTABLE STRICT PARALLEL SAFE ROWS 1; diff --git a/contrib/citext/citext.c b/contrib/citext/citext.c index 1174b70aa7..04f604b15f 100644 --- a/contrib/citext/citext.c +++ b/contrib/citext/citext.c @@ -7,6 +7,7 @@ #include "catalog/pg_collation.h" #include "utils/builtins.h" #include "utils/formatting.h" +#include "utils/varlena.h" #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; diff --git a/contrib/citext/citext.control b/contrib/citext/citext.control index 5f080df3ee..17fce4e887 100644 --- a/contrib/citext/citext.control +++ b/contrib/citext/citext.control @@ -1,5 +1,5 @@ # citext extension comment = 'data type for case-insensitive character strings' -default_version = '1.3' +default_version = '1.4' module_pathname = '$libdir/citext' relocatable = true diff --git a/contrib/citext/expected/citext.out b/contrib/citext/expected/citext.out index 6541b24b5f..9cc94f4c1b 100644 --- a/contrib/citext/expected/citext.out +++ b/contrib/citext/expected/citext.out @@ -2,6 +2,14 @@ -- Test citext datatype -- CREATE EXTENSION citext; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + -- Test the operators and indexing functions -- Test = and <>. SELECT 'a'::citext = 'a'::citext AS t; @@ -1770,6 +1778,60 @@ SELECT quote_literal( name ) = quote_literal( name::text ) AS t FROM srt; t (4 rows) +SELECT regexp_match('foobarbequebaz'::citext, '(bar)(beque)') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext) = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)', '') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz', '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, ''::citext) = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +-- c forces case-sensitive +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "no result"; + no result +----------- + +(1 row) + +-- g is not allowed +SELECT regexp_match('foobarbequebazmorebarbequetoo'::citext, '(BAR)(BEQUE)'::citext, 'g') AS "error"; +ERROR: regexp_match does not support the global option +HINT: Use the regexp_matches function instead. +CONTEXT: SQL function "regexp_match" statement 1 SELECT regexp_matches('foobarbequebaz'::citext, '(bar)(beque)') = ARRAY[ 'bar', 'beque' ] AS t; t --- @@ -2274,8 +2336,8 @@ SELECT * WHERE t.id IS NULL OR m.id IS NULL; id | name | id | name ----+------+----+------ - | | 2 | Two 2 | two | | + | | 2 | Two (2 rows) REFRESH MATERIALIZED VIEW CONCURRENTLY citext_matview; diff --git a/contrib/citext/expected/citext_1.out b/contrib/citext/expected/citext_1.out index 6515fbd571..fe745a1f28 100644 --- a/contrib/citext/expected/citext_1.out +++ b/contrib/citext/expected/citext_1.out @@ -2,6 +2,14 @@ -- Test citext datatype -- CREATE EXTENSION citext; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + -- Test the operators and indexing functions -- Test = and <>. SELECT 'a'::citext = 'a'::citext AS t; @@ -1770,6 +1778,60 @@ SELECT quote_literal( name ) = quote_literal( name::text ) AS t FROM srt; t (4 rows) +SELECT regexp_match('foobarbequebaz'::citext, '(bar)(beque)') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext) = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)', '') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz', '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, ''::citext) = ARRAY[ 'bar', 'beque' ] AS t; + t +--- + t +(1 row) + +-- c forces case-sensitive +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "no result"; + no result +----------- + +(1 row) + +-- g is not allowed +SELECT regexp_match('foobarbequebazmorebarbequetoo'::citext, '(BAR)(BEQUE)'::citext, 'g') AS "error"; +ERROR: regexp_match does not support the global option +HINT: Use the regexp_matches function instead. +CONTEXT: SQL function "regexp_match" statement 1 SELECT regexp_matches('foobarbequebaz'::citext, '(bar)(beque)') = ARRAY[ 'bar', 'beque' ] AS t; t --- @@ -2274,8 +2336,8 @@ SELECT * WHERE t.id IS NULL OR m.id IS NULL; id | name | id | name ----+------+----+------ - | | 2 | Two 2 | two | | + | | 2 | Two (2 rows) REFRESH MATERIALIZED VIEW CONCURRENTLY citext_matview; diff --git a/contrib/citext/sql/citext.sql b/contrib/citext/sql/citext.sql index ee6ad2951c..3bf7da2ff4 100644 --- a/contrib/citext/sql/citext.sql +++ b/contrib/citext/sql/citext.sql @@ -4,6 +4,11 @@ CREATE EXTENSION citext; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + -- Test the operators and indexing functions -- Test = and <>. @@ -592,6 +597,18 @@ SELECT md5( name ) = md5( name::text ) AS t FROM srt; SELECT quote_ident( name ) = quote_ident( name::text ) AS t FROM srt; SELECT quote_literal( name ) = quote_literal( name::text ) AS t FROM srt; +SELECT regexp_match('foobarbequebaz'::citext, '(bar)(beque)') = ARRAY[ 'bar', 'beque' ] AS t; +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)') = ARRAY[ 'bar', 'beque' ] AS t; +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext) = ARRAY[ 'bar', 'beque' ] AS t; +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)', '') = ARRAY[ 'bar', 'beque' ] AS t; +SELECT regexp_match('foobarbequebaz', '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, ''::citext) = ARRAY[ 'bar', 'beque' ] AS t; +-- c forces case-sensitive +SELECT regexp_match('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "no result"; +-- g is not allowed +SELECT regexp_match('foobarbequebazmorebarbequetoo'::citext, '(BAR)(BEQUE)'::citext, 'g') AS "error"; + SELECT regexp_matches('foobarbequebaz'::citext, '(bar)(beque)') = ARRAY[ 'bar', 'beque' ] AS t; SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)') = ARRAY[ 'bar', 'beque' ] AS t; SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext) = ARRAY[ 'bar', 'beque' ] AS t; diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index 3feddef8f3..2bb2ed029d 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -122,7 +122,7 @@ cube_in(PG_FUNCTION_ARGS) cube_scanner_init(str); if (cube_yyparse(&result) != 0) - cube_yyerror(&result, "bogus input"); + cube_yyerror(&result, "cube parser failed"); cube_scanner_finish(); @@ -254,12 +254,9 @@ cube_subset(PG_FUNCTION_ARGS) for (i = 0; i < dim; i++) { if ((dx[i] <= 0) || (dx[i] > DIM(c))) - { - pfree(result); ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("Index out of bounds"))); - } result->x[i] = c->x[dx[i] - 1]; if (!IS_POINT(c)) result->x[i + dim] = c->x[dx[i] + DIM(c) - 1]; @@ -276,27 +273,15 @@ cube_out(PG_FUNCTION_ARGS) StringInfoData buf; int dim = DIM(cube); int i; - int ndig; initStringInfo(&buf); - /* - * Get the number of digits to display. - */ - ndig = DBL_DIG + extra_float_digits; - if (ndig < 1) - ndig = 1; - - /* - * while printing the first (LL) corner, check if it is equal to the - * second one - */ appendStringInfoChar(&buf, '('); for (i = 0; i < dim; i++) { if (i > 0) appendStringInfoString(&buf, ", "); - appendStringInfo(&buf, "%.*g", ndig, LL_COORD(cube, i)); + appendStringInfoString(&buf, float8out_internal(LL_COORD(cube, i))); } appendStringInfoChar(&buf, ')'); @@ -307,7 +292,7 @@ cube_out(PG_FUNCTION_ARGS) { if (i > 0) appendStringInfoString(&buf, ", "); - appendStringInfo(&buf, "%.*g", ndig, UR_COORD(cube, i)); + appendStringInfoString(&buf, float8out_internal(UR_COORD(cube, i))); } appendStringInfoChar(&buf, ')'); } @@ -370,9 +355,6 @@ g_cube_union(PG_FUNCTION_ARGS) NDBOX *tmp; int i; - /* - * fprintf(stderr, "union\n"); - */ tmp = DatumGetNDBOX(entryvec->vector[0].key); /* @@ -441,9 +423,6 @@ g_cube_penalty(PG_FUNCTION_ARGS) rt_cube_size(DatumGetNDBOX(origentry->key), &tmp2); *result = (float) (tmp1 - tmp2); - /* - * fprintf(stderr, "penalty\n"); fprintf(stderr, "\t%g\n", *result); - */ PG_RETURN_FLOAT8(*result); } @@ -484,9 +463,6 @@ g_cube_picksplit(PG_FUNCTION_ARGS) *right; OffsetNumber maxoff; - /* - * fprintf(stderr, "picksplit\n"); - */ maxoff = entryvec->n - 2; nbytes = (maxoff + 2) * sizeof(OffsetNumber); v->spl_left = (OffsetNumber *) palloc(nbytes); @@ -617,9 +593,6 @@ g_cube_same(PG_FUNCTION_ARGS) else *result = FALSE; - /* - * fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE" )); - */ PG_RETURN_NDBOX(result); } @@ -633,9 +606,6 @@ g_cube_leaf_consistent(NDBOX *key, { bool retval; - /* - * fprintf(stderr, "leaf_consistent, %d\n", strategy); - */ switch (strategy) { case RTOverlapStrategyNumber: @@ -665,9 +635,6 @@ g_cube_internal_consistent(NDBOX *key, { bool retval; - /* - * fprintf(stderr, "internal_consistent, %d\n", strategy); - */ switch (strategy) { case RTOverlapStrategyNumber: @@ -865,12 +832,8 @@ cube_size(PG_FUNCTION_ARGS) { NDBOX *a = PG_GETARG_NDBOX(0); double result; - int i; - - result = 1.0; - for (i = 0; i < DIM(a); i++) - result = result * Abs((LL_COORD(a, i) - UR_COORD(a, i))); + rt_cube_size(a, &result); PG_FREE_IF_COPY(a, 0); PG_RETURN_FLOAT8(result); } @@ -878,17 +841,26 @@ cube_size(PG_FUNCTION_ARGS) void rt_cube_size(NDBOX *a, double *size) { + double result; int i; if (a == (NDBOX *) NULL) - *size = 0.0; + { + /* special case for GiST */ + result = 0.0; + } + else if (IS_POINT(a) || DIM(a) == 0) + { + /* necessarily has zero size */ + result = 0.0; + } else { - *size = 1.0; + result = 1.0; for (i = 0; i < DIM(a); i++) - *size = (*size) * Abs(UR_COORD(a, i) - LL_COORD(a, i)); + result *= Abs(UR_COORD(a, i) - LL_COORD(a, i)); } - return; + *size = result; } /* make up a metric in which one box will be 'lower' than the other @@ -1155,10 +1127,6 @@ cube_overlap_v0(NDBOX *a, NDBOX *b) { int i; - /* - * This *very bad* error was found in the source: if ( (a==NULL) || - * (b=NULL) ) return(FALSE); - */ if ((a == NULL) || (b == NULL)) return (FALSE); @@ -1370,7 +1338,9 @@ g_cube_distance(PG_FUNCTION_ARGS) { int coord = PG_GETARG_INT32(1); - if (IS_POINT(cube)) + if (DIM(cube) == 0) + retval = 0.0; + else if (IS_POINT(cube)) retval = cube->x[(coord - 1) % DIM(cube)]; else retval = Min(cube->x[(coord - 1) % DIM(cube)], diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h index 7eaac39640..af02464178 100644 --- a/contrib/cube/cubedata.h +++ b/contrib/cube/cubedata.h @@ -1,5 +1,9 @@ /* contrib/cube/cubedata.h */ +/* + * This limit is pretty arbitrary, but don't make it so large that you + * risk overflow in sizing calculations. + */ #define CUBE_MAX_DIM (100) typedef struct NDBOX @@ -29,6 +33,7 @@ typedef struct NDBOX double x[FLEXIBLE_ARRAY_MEMBER]; } NDBOX; +/* NDBOX access macros */ #define POINT_BIT 0x80000000 #define DIM_MASK 0x7fffffff @@ -43,10 +48,12 @@ typedef struct NDBOX #define POINT_SIZE(_dim) (offsetof(NDBOX, x) + sizeof(double)*(_dim)) #define CUBE_SIZE(_dim) (offsetof(NDBOX, x) + sizeof(double)*(_dim)*2) +/* fmgr interface macros */ #define DatumGetNDBOX(x) ((NDBOX *) PG_DETOAST_DATUM(x)) #define PG_GETARG_NDBOX(x) DatumGetNDBOX(PG_GETARG_DATUM(x)) #define PG_RETURN_NDBOX(x) PG_RETURN_POINTER(x) +/* GiST operator strategy numbers */ #define CubeKNNDistanceCoord 15 /* ~> */ #define CubeKNNDistanceTaxicab 16 /* <#> */ #define CubeKNNDistanceEuclid 17 /* <-> */ diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y index 33606c741c..1b65fa967c 100644 --- a/contrib/cube/cubeparse.y +++ b/contrib/cube/cubeparse.y @@ -4,12 +4,13 @@ /* NdBox = [(lowerleft),(upperright)] */ /* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */ -#define YYSTYPE char * -#define YYDEBUG 1 - #include "postgres.h" #include "cubedata.h" +#include "utils/builtins.h" + +/* All grammar constructs return strings */ +#define YYSTYPE char * /* * Bison doesn't allocate anything that needs to live across parser calls, @@ -25,9 +26,9 @@ static char *scanbuf; static int scanbuflen; -static int delim_count(char *s, char delim); -static NDBOX * write_box(unsigned int dim, char *str1, char *str2); -static NDBOX * write_point_as_box(char *s, int dim); +static int item_count(const char *s, char delim); +static NDBOX *write_box(int dim, char *str1, char *str2); +static NDBOX *write_point_as_box(int dim, char *str); %} @@ -46,47 +47,48 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET { int dim; - dim = delim_count($2, ',') + 1; - if ((delim_count($4, ',') + 1) != dim) + dim = item_count($2, ','); + if (item_count($4, ',') != dim) { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), errdetail("Different point dimensions in (%s) and (%s).", $2, $4))); YYABORT; } - if (dim > CUBE_MAX_DIM) { + if (dim > CUBE_MAX_DIM) + { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), errdetail("A cube cannot have more than %d dimensions.", CUBE_MAX_DIM))); YYABORT; } *result = write_box( dim, $2, $4 ); - } | paren_list COMMA paren_list { int dim; - dim = delim_count($1, ',') + 1; - - if ( (delim_count($3, ',') + 1) != dim ) { + dim = item_count($1, ','); + if (item_count($3, ',') != dim) + { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), errdetail("Different point dimensions in (%s) and (%s).", $1, $3))); YYABORT; } - if (dim > CUBE_MAX_DIM) { + if (dim > CUBE_MAX_DIM) + { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), errdetail("A cube cannot have more than %d dimensions.", CUBE_MAX_DIM))); YYABORT; @@ -99,33 +101,36 @@ box: O_BRACKET paren_list COMMA paren_list C_BRACKET { int dim; - dim = delim_count($1, ',') + 1; - if (dim > CUBE_MAX_DIM) { + dim = item_count($1, ','); + if (dim > CUBE_MAX_DIM) + { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), errdetail("A cube cannot have more than %d dimensions.", CUBE_MAX_DIM))); YYABORT; } - *result = write_point_as_box($1, dim); + *result = write_point_as_box(dim, $1); } | list { int dim; - dim = delim_count($1, ',') + 1; - if (dim > CUBE_MAX_DIM) { + dim = item_count($1, ','); + if (dim > CUBE_MAX_DIM) + { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), errdetail("A cube cannot have more than %d dimensions.", CUBE_MAX_DIM))); YYABORT; } - *result = write_point_as_box($1, dim); + + *result = write_point_as_box(dim, $1); } ; @@ -133,6 +138,10 @@ paren_list: O_PAREN list C_PAREN { $$ = $2; } + | O_PAREN C_PAREN + { + $$ = pstrdup(""); + } ; list: CUBEFLOAT @@ -151,24 +160,30 @@ list: CUBEFLOAT %% +/* This assumes the string has been normalized by productions above */ static int -delim_count(char *s, char delim) +item_count(const char *s, char delim) { - int ndelim = 0; + int nitems = 0; - while ((s = strchr(s, delim)) != NULL) + if (s[0] != '\0') { - ndelim++; - s++; + nitems++; + while ((s = strchr(s, delim)) != NULL) + { + nitems++; + s++; + } } - return (ndelim); + return nitems; } static NDBOX * -write_box(unsigned int dim, char *str1, char *str2) +write_box(int dim, char *str1, char *str2) { NDBOX *bp; char *s; + char *endptr; int i; int size = CUBE_SIZE(dim); bool point = true; @@ -178,50 +193,58 @@ write_box(unsigned int dim, char *str1, char *str2) SET_DIM(bp, dim); s = str1; - bp->x[i=0] = strtod(s, NULL); + i = 0; + if (dim > 0) + bp->x[i++] = float8in_internal(s, &endptr, "cube", str1); while ((s = strchr(s, ',')) != NULL) { - s++; i++; - bp->x[i] = strtod(s, NULL); + s++; + bp->x[i++] = float8in_internal(s, &endptr, "cube", str1); } + Assert(i == dim); s = str2; - bp->x[i=dim] = strtod(s, NULL); - if (bp->x[dim] != bp->x[0]) - point = false; + if (dim > 0) + { + bp->x[i] = float8in_internal(s, &endptr, "cube", str2); + /* code this way to do right thing with NaN */ + point &= (bp->x[i] == bp->x[0]); + i++; + } while ((s = strchr(s, ',')) != NULL) { - s++; i++; - bp->x[i] = strtod(s, NULL); - if (bp->x[i] != bp->x[i-dim]) - point = false; + s++; + bp->x[i] = float8in_internal(s, &endptr, "cube", str2); + point &= (bp->x[i] == bp->x[i - dim]); + i++; } + Assert(i == dim * 2); if (point) { /* * The value turned out to be a point, ie. all the upper-right * coordinates were equal to the lower-left coordinates. Resize the - * the cube we constructed. Note: we don't bother to repalloc() it - * smaller, it's unlikely that the tiny amount of memory free'd that - * way would be useful. + * cube we constructed. Note: we don't bother to repalloc() it + * smaller, as it's unlikely that the tiny amount of memory freed + * that way would be useful, and the output is always short-lived. */ size = POINT_SIZE(dim); SET_VARSIZE(bp, size); SET_POINT_BIT(bp); } - return(bp); + return bp; } static NDBOX * -write_point_as_box(char *str, int dim) +write_point_as_box(int dim, char *str) { NDBOX *bp; int i, size; - double x; - char *s = str; + char *s; + char *endptr; size = POINT_SIZE(dim); bp = palloc0(size); @@ -229,17 +252,18 @@ write_point_as_box(char *str, int dim) SET_DIM(bp, dim); SET_POINT_BIT(bp); + s = str; i = 0; - x = strtod(s, NULL); - bp->x[0] = x; + if (dim > 0) + bp->x[i++] = float8in_internal(s, &endptr, "cube", str); while ((s = strchr(s, ',')) != NULL) { - s++; i++; - x = strtod(s, NULL); - bp->x[i] = x; + s++; + bp->x[i++] = float8in_internal(s, &endptr, "cube", str); } + Assert(i == dim); - return(bp); + return bp; } #include "cubescan.c" diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l index 4408e28387..dada917820 100644 --- a/contrib/cube/cubescan.l +++ b/contrib/cube/cubescan.l @@ -38,36 +38,41 @@ n [0-9]+ integer [+-]?{n} real [+-]?({n}\.{n}?|\.{n}) float ({integer}|{real})([eE]{integer})? +infinity [+-]?[iI][nN][fF]([iI][nN][iI][tT][yY])? +NaN [nN][aA][nN] %% {float} yylval = yytext; return CUBEFLOAT; +{infinity} yylval = yytext; return CUBEFLOAT; +{NaN} yylval = yytext; return CUBEFLOAT; \[ yylval = "("; return O_BRACKET; \] yylval = ")"; return C_BRACKET; \( yylval = "("; return O_PAREN; \) yylval = ")"; return C_PAREN; -\, yylval = ")"; return COMMA; +\, yylval = ","; return COMMA; [ \t\n\r\f]+ /* discard spaces */ . return yytext[0]; /* alert parser of the garbage */ %% +/* result is not used, but Bison expects this signature */ void yyerror(NDBOX **result, const char *message) { if (*yytext == YY_END_OF_BUFFER_CHAR) { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), /* translator: %s is typically "syntax error" */ errdetail("%s at end of input", message))); } else { ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("bad cube representation"), + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for cube"), /* translator: first %s is typically "syntax error" */ errdetail("%s at or near \"%s\"", message, yytext))); } diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out index e9e2c0f15b..328b3b5f5d 100644 --- a/contrib/cube/expected/cube.out +++ b/contrib/cube/expected/cube.out @@ -2,6 +2,14 @@ -- Test cube datatype -- CREATE EXTENSION cube; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + -- -- testing the input and output functions -- @@ -126,16 +134,34 @@ SELECT '-1.0e-7'::cube AS cube; (-1e-07) (1 row) -SELECT '1e-700'::cube AS cube; - cube ------- - (0) +SELECT '1e-300'::cube AS cube; + cube +---------- + (1e-300) (1 row) -SELECT '-1e-700'::cube AS cube; - cube ------- - (0) +SELECT '-1e-300'::cube AS cube; + cube +----------- + (-1e-300) +(1 row) + +SELECT 'infinity'::cube AS cube; + cube +------------ + (Infinity) +(1 row) + +SELECT '-infinity'::cube AS cube; + cube +------------- + (-Infinity) +(1 row) + +SELECT 'NaN'::cube AS cube; + cube +------- + (NaN) (1 row) SELECT '1234567890123456'::cube AS cube; @@ -175,6 +201,12 @@ SELECT '-.1234567890123456'::cube AS cube; (1 row) -- simple lists (points) +SELECT '()'::cube AS cube; + cube +------ + () +(1 row) + SELECT '1,2'::cube AS cube; cube -------- @@ -200,6 +232,12 @@ SELECT '(1,2,3,4,5)'::cube AS cube; (1 row) -- double lists (cubes) +SELECT '(),()'::cube AS cube; + cube +------ + () +(1 row) + SELECT '(0),(0)'::cube AS cube; cube ------ @@ -250,146 +288,145 @@ SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; -- invalid input: parse errors SELECT ''::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT ''::cube AS cube; ^ DETAIL: syntax error at end of input SELECT 'ABC'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT 'ABC'::cube AS cube; ^ DETAIL: syntax error at or near "A" -SELECT '()'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '()'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" SELECT '[]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[]'::cube AS cube; ^ DETAIL: syntax error at or near "]" SELECT '[()]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[()]'::cube AS cube; ^ -DETAIL: syntax error at or near ")" +DETAIL: syntax error at or near "]" SELECT '[(1)]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1)]'::cube AS cube; ^ DETAIL: syntax error at or near "]" SELECT '[(1),]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),]'::cube AS cube; ^ DETAIL: syntax error at or near "]" SELECT '[(1),2]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),2]'::cube AS cube; ^ DETAIL: syntax error at or near "2" SELECT '[(1),(2),(3)]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),(2),(3)]'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '1,'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,'::cube AS cube; ^ DETAIL: syntax error at end of input SELECT '1,2,'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,2,'::cube AS cube; ^ DETAIL: syntax error at end of input SELECT '1,,2'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,,2'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '(1,)'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,)'::cube AS cube; ^ DETAIL: syntax error at or near ")" SELECT '(1,2,)'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,)'::cube AS cube; ^ DETAIL: syntax error at or near ")" SELECT '(1,,2)'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,,2)'::cube AS cube; ^ DETAIL: syntax error at or near "," -- invalid input: semantic errors and trailing garbage SELECT '[(1),(2)],'::cube AS cube; -- 0 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),(2)],'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '[(1,2,3),(2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1,2,3),(2,3)]'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2,3) and (2,3). SELECT '[(1,2),(1,2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1,2),(1,2,3)]'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2) and (1,2,3). SELECT '(1),(2),'::cube AS cube; -- 2 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1),(2),'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '(1,2,3),(2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,3),(2,3)'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2,3) and (2,3). SELECT '(1,2),(1,2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2),(1,2,3)'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2) and (1,2,3). SELECT '(1,2,3)ab'::cube AS cube; -- 4 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,3)ab'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '(1,2,3)a'::cube AS cube; -- 5 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,3)a'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '(1,2)('::cube AS cube; -- 5 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2)('::cube AS cube; ^ DETAIL: syntax error at or near "(" SELECT '1,2ab'::cube AS cube; -- 6 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,2ab'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '1 e7'::cube AS cube; -- 6 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1 e7'::cube AS cube; ^ DETAIL: syntax error at or near "e" SELECT '1,2a'::cube AS cube; -- 7 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,2a'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '1..2'::cube AS cube; -- 7 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1..2'::cube AS cube; ^ DETAIL: syntax error at or near ".2" +SELECT '-1e-700'::cube AS cube; -- out of range +ERROR: "-1e-700" is out of range for type double precision +LINE 1: SELECT '-1e-700'::cube AS cube; + ^ -- -- Testing building cubes from float8 values -- @@ -556,12 +593,12 @@ SELECT cube(cube(1,2), 42, 24); -- cube_c_f8_f8 -- Testing limit of CUBE_MAX_DIM dimensions check in cube_in. -- select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... ^ DETAIL: A cube cannot have more than 100 dimensions. select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... ^ DETAIL: A cube cannot have more than 100 dimensions. @@ -1019,7 +1056,7 @@ SELECT cube_dim('(4,8,15,16,23),(4,8,15,16,23)'::cube); 5 (1 row) --- Test of cube_ll_coord function (retrieves LL coodinate values) +-- Test of cube_ll_coord function (retrieves LL coordinate values) -- SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 1); cube_ll_coord @@ -1075,7 +1112,7 @@ SELECT cube_ll_coord('(42,137)'::cube, 3); 0 (1 row) --- Test of cube_ur_coord function (retrieves UR coodinate values) +-- Test of cube_ur_coord function (retrieves UR coordinate values) -- SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 1); cube_ur_coord diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out deleted file mode 100644 index c40fabcd46..0000000000 --- a/contrib/cube/expected/cube_1.out +++ /dev/null @@ -1,1710 +0,0 @@ --- --- Test cube datatype --- -CREATE EXTENSION cube; --- --- testing the input and output functions --- --- Any number (a one-dimensional point) -SELECT '1'::cube AS cube; - cube ------- - (1) -(1 row) - -SELECT '-1'::cube AS cube; - cube ------- - (-1) -(1 row) - -SELECT '1.'::cube AS cube; - cube ------- - (1) -(1 row) - -SELECT '-1.'::cube AS cube; - cube ------- - (-1) -(1 row) - -SELECT '.1'::cube AS cube; - cube -------- - (0.1) -(1 row) - -SELECT '-.1'::cube AS cube; - cube --------- - (-0.1) -(1 row) - -SELECT '1.0'::cube AS cube; - cube ------- - (1) -(1 row) - -SELECT '-1.0'::cube AS cube; - cube ------- - (-1) -(1 row) - -SELECT '1e27'::cube AS cube; - cube ---------- - (1e+27) -(1 row) - -SELECT '-1e27'::cube AS cube; - cube ----------- - (-1e+27) -(1 row) - -SELECT '1.0e27'::cube AS cube; - cube ---------- - (1e+27) -(1 row) - -SELECT '-1.0e27'::cube AS cube; - cube ----------- - (-1e+27) -(1 row) - -SELECT '1e+27'::cube AS cube; - cube ---------- - (1e+27) -(1 row) - -SELECT '-1e+27'::cube AS cube; - cube ----------- - (-1e+27) -(1 row) - -SELECT '1.0e+27'::cube AS cube; - cube ---------- - (1e+27) -(1 row) - -SELECT '-1.0e+27'::cube AS cube; - cube ----------- - (-1e+27) -(1 row) - -SELECT '1e-7'::cube AS cube; - cube ---------- - (1e-07) -(1 row) - -SELECT '-1e-7'::cube AS cube; - cube ----------- - (-1e-07) -(1 row) - -SELECT '1.0e-7'::cube AS cube; - cube ---------- - (1e-07) -(1 row) - -SELECT '-1.0e-7'::cube AS cube; - cube ----------- - (-1e-07) -(1 row) - -SELECT '1e-700'::cube AS cube; - cube ------- - (0) -(1 row) - -SELECT '-1e-700'::cube AS cube; - cube ------- - (-0) -(1 row) - -SELECT '1234567890123456'::cube AS cube; - cube ------------------------- - (1.23456789012346e+15) -(1 row) - -SELECT '+1234567890123456'::cube AS cube; - cube ------------------------- - (1.23456789012346e+15) -(1 row) - -SELECT '-1234567890123456'::cube AS cube; - cube -------------------------- - (-1.23456789012346e+15) -(1 row) - -SELECT '.1234567890123456'::cube AS cube; - cube ---------------------- - (0.123456789012346) -(1 row) - -SELECT '+.1234567890123456'::cube AS cube; - cube ---------------------- - (0.123456789012346) -(1 row) - -SELECT '-.1234567890123456'::cube AS cube; - cube ----------------------- - (-0.123456789012346) -(1 row) - --- simple lists (points) -SELECT '1,2'::cube AS cube; - cube --------- - (1, 2) -(1 row) - -SELECT '(1,2)'::cube AS cube; - cube --------- - (1, 2) -(1 row) - -SELECT '1,2,3,4,5'::cube AS cube; - cube ------------------ - (1, 2, 3, 4, 5) -(1 row) - -SELECT '(1,2,3,4,5)'::cube AS cube; - cube ------------------ - (1, 2, 3, 4, 5) -(1 row) - --- double lists (cubes) -SELECT '(0),(0)'::cube AS cube; - cube ------- - (0) -(1 row) - -SELECT '(0),(1)'::cube AS cube; - cube ---------- - (0),(1) -(1 row) - -SELECT '[(0),(0)]'::cube AS cube; - cube ------- - (0) -(1 row) - -SELECT '[(0),(1)]'::cube AS cube; - cube ---------- - (0),(1) -(1 row) - -SELECT '(0,0,0,0),(0,0,0,0)'::cube AS cube; - cube --------------- - (0, 0, 0, 0) -(1 row) - -SELECT '(0,0,0,0),(1,0,0,0)'::cube AS cube; - cube ---------------------------- - (0, 0, 0, 0),(1, 0, 0, 0) -(1 row) - -SELECT '[(0,0,0,0),(0,0,0,0)]'::cube AS cube; - cube --------------- - (0, 0, 0, 0) -(1 row) - -SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; - cube ---------------------------- - (0, 0, 0, 0),(1, 0, 0, 0) -(1 row) - --- invalid input: parse errors -SELECT ''::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT ''::cube AS cube; - ^ -DETAIL: syntax error at end of input -SELECT 'ABC'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT 'ABC'::cube AS cube; - ^ -DETAIL: syntax error at or near "A" -SELECT '()'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '()'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '[]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[]'::cube AS cube; - ^ -DETAIL: syntax error at or near "]" -SELECT '[()]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[()]'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '[(1)]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1)]'::cube AS cube; - ^ -DETAIL: syntax error at or near "]" -SELECT '[(1),]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1),]'::cube AS cube; - ^ -DETAIL: syntax error at or near "]" -SELECT '[(1),2]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1),2]'::cube AS cube; - ^ -DETAIL: syntax error at or near "2" -SELECT '[(1),(2),(3)]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1),(2),(3)]'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '1,'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '1,'::cube AS cube; - ^ -DETAIL: syntax error at end of input -SELECT '1,2,'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '1,2,'::cube AS cube; - ^ -DETAIL: syntax error at end of input -SELECT '1,,2'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '1,,2'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '(1,)'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '(1,)'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '(1,2,)'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '(1,2,)'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '(1,,2)'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '(1,,2)'::cube AS cube; - ^ -DETAIL: syntax error at or near "," --- invalid input: semantic errors and trailing garbage -SELECT '[(1),(2)],'::cube AS cube; -- 0 -ERROR: bad cube representation -LINE 1: SELECT '[(1),(2)],'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '[(1,2,3),(2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation -LINE 1: SELECT '[(1,2,3),(2,3)]'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2,3) and (2,3). -SELECT '[(1,2),(1,2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation -LINE 1: SELECT '[(1,2),(1,2,3)]'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2) and (1,2,3). -SELECT '(1),(2),'::cube AS cube; -- 2 -ERROR: bad cube representation -LINE 1: SELECT '(1),(2),'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '(1,2,3),(2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation -LINE 1: SELECT '(1,2,3),(2,3)'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2,3) and (2,3). -SELECT '(1,2),(1,2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation -LINE 1: SELECT '(1,2),(1,2,3)'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2) and (1,2,3). -SELECT '(1,2,3)ab'::cube AS cube; -- 4 -ERROR: bad cube representation -LINE 1: SELECT '(1,2,3)ab'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '(1,2,3)a'::cube AS cube; -- 5 -ERROR: bad cube representation -LINE 1: SELECT '(1,2,3)a'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '(1,2)('::cube AS cube; -- 5 -ERROR: bad cube representation -LINE 1: SELECT '(1,2)('::cube AS cube; - ^ -DETAIL: syntax error at or near "(" -SELECT '1,2ab'::cube AS cube; -- 6 -ERROR: bad cube representation -LINE 1: SELECT '1,2ab'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '1 e7'::cube AS cube; -- 6 -ERROR: bad cube representation -LINE 1: SELECT '1 e7'::cube AS cube; - ^ -DETAIL: syntax error at or near "e" -SELECT '1,2a'::cube AS cube; -- 7 -ERROR: bad cube representation -LINE 1: SELECT '1,2a'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '1..2'::cube AS cube; -- 7 -ERROR: bad cube representation -LINE 1: SELECT '1..2'::cube AS cube; - ^ -DETAIL: syntax error at or near ".2" --- --- Testing building cubes from float8 values --- -SELECT cube(0::float8); - cube ------- - (0) -(1 row) - -SELECT cube(1::float8); - cube ------- - (1) -(1 row) - -SELECT cube(1,2); - cube ---------- - (1),(2) -(1 row) - -SELECT cube(cube(1,2),3); - cube ---------------- - (1, 3),(2, 3) -(1 row) - -SELECT cube(cube(1,2),3,4); - cube ---------------- - (1, 3),(2, 4) -(1 row) - -SELECT cube(cube(cube(1,2),3,4),5); - cube ---------------------- - (1, 3, 5),(2, 4, 5) -(1 row) - -SELECT cube(cube(cube(1,2),3,4),5,6); - cube ---------------------- - (1, 3, 5),(2, 4, 6) -(1 row) - --- --- Test that the text -> cube cast was installed. --- -SELECT '(0)'::text::cube; - cube ------- - (0) -(1 row) - --- --- Test the float[] -> cube cast --- -SELECT cube('{0,1,2}'::float[], '{3,4,5}'::float[]); - cube ---------------------- - (0, 1, 2),(3, 4, 5) -(1 row) - -SELECT cube('{0,1,2}'::float[], '{3}'::float[]); -ERROR: UR and LL arrays must be of same length -SELECT cube(NULL::float[], '{3}'::float[]); - cube ------- - -(1 row) - -SELECT cube('{0,1,2}'::float[]); - cube ------------ - (0, 1, 2) -(1 row) - -SELECT cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[3,2,1,1]); - cube_subset ---------------------------- - (5, 3, 1, 1),(8, 7, 6, 6) -(1 row) - -SELECT cube_subset(cube('(1,3,5),(1,3,5)'), ARRAY[3,2,1,1]); - cube_subset --------------- - (5, 3, 1, 1) -(1 row) - -SELECT cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[4,0]); -ERROR: Index out of bounds -SELECT cube_subset(cube('(6,7,8),(6,7,8)'), ARRAY[4,0]); -ERROR: Index out of bounds --- --- Test point processing --- -SELECT cube('(1,2),(1,2)'); -- cube_in - cube --------- - (1, 2) -(1 row) - -SELECT cube('{0,1,2}'::float[], '{0,1,2}'::float[]); -- cube_a_f8_f8 - cube ------------ - (0, 1, 2) -(1 row) - -SELECT cube('{5,6,7,8}'::float[]); -- cube_a_f8 - cube --------------- - (5, 6, 7, 8) -(1 row) - -SELECT cube(1.37); -- cube_f8 - cube --------- - (1.37) -(1 row) - -SELECT cube(1.37, 1.37); -- cube_f8_f8 - cube --------- - (1.37) -(1 row) - -SELECT cube(cube(1,1), 42); -- cube_c_f8 - cube ---------- - (1, 42) -(1 row) - -SELECT cube(cube(1,2), 42); -- cube_c_f8 - cube ------------------ - (1, 42),(2, 42) -(1 row) - -SELECT cube(cube(1,1), 42, 42); -- cube_c_f8_f8 - cube ---------- - (1, 42) -(1 row) - -SELECT cube(cube(1,1), 42, 24); -- cube_c_f8_f8 - cube ------------------ - (1, 42),(1, 24) -(1 row) - -SELECT cube(cube(1,2), 42, 42); -- cube_c_f8_f8 - cube ------------------ - (1, 42),(2, 42) -(1 row) - -SELECT cube(cube(1,2), 42, 24); -- cube_c_f8_f8 - cube ------------------ - (1, 42),(2, 24) -(1 row) - --- --- Testing limit of CUBE_MAX_DIM dimensions check in cube_in. --- -select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation -LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... - ^ -DETAIL: A cube cannot have more than 100 dimensions. -select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation -LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... - ^ -DETAIL: A cube cannot have more than 100 dimensions. --- --- testing the operators --- --- equality/inequality: --- -SELECT '24, 33.20'::cube = '24, 33.20'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '24, 33.20'::cube != '24, 33.20'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '24, 33.20'::cube = '24, 33.21'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '24, 33.20'::cube != '24, 33.21'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; - bool ------- - f -(1 row) - --- "lower than" / "greater than" --- (these operators are not useful for anything but ordering) --- -SELECT '1'::cube > '2'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '1'::cube < '2'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '1,1'::cube > '1,2'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '1,1'::cube < '1,2'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube > '(2,0),(3,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube < '(2,0),(3,1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; - bool ------- - f -(1 row) - --- "overlap" --- -SELECT '1'::cube && '1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '1'::cube && '2'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1,1,1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1,1),(2,2,2)]'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1),(2,2)]'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(2,1,1),(2,2,2)]'::cube AS bool; - bool ------- - f -(1 row) - --- "contained in" (the left operand is the cube entirely enclosed by --- the right operand): --- -SELECT '0'::cube <@ '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,0'::cube <@ '0,0,0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0'::cube <@ '0,0,1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,0'::cube <@ '0,0,1'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '1,0,0'::cube <@ '0,0,1'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube <@ '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube <@ '(-1,-1,-1),(1,1,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube <@ '(-1,-1,-1,-1),(1,1,1,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '1'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '-1'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube <@ '(-1,-1),(1,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-2),(1)'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(-2),(1)'::cube <@ '(-1,-1),(1,1)'::cube AS bool; - bool ------- - f -(1 row) - --- "contains" (the left operand is the cube that entirely encloses the --- right operand) --- -SELECT '0'::cube @> '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,0'::cube @> '0,0,0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,1'::cube @> '0,0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,1'::cube @> '0,0,0'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '0,0,1'::cube @> '1,0,0'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube @> '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1,-1,-1),(1,1,1)'::cube @> '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1,-1,-1,-1),(1,1,1,1)'::cube @> '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '-1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1,-1),(1,1)'::cube @> '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '(-2),(1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(-1,-1),(1,1)'::cube @> '(-2),(1)'::cube AS bool; - bool ------- - f -(1 row) - --- Test of distance function --- -SELECT cube_distance('(0)'::cube,'(2,2,2,2)'::cube); - cube_distance ---------------- - 4 -(1 row) - -SELECT cube_distance('(0)'::cube,'(.3,.4)'::cube); - cube_distance ---------------- - 0.5 -(1 row) - -SELECT cube_distance('(2,3,4)'::cube,'(2,3,4)'::cube); - cube_distance ---------------- - 0 -(1 row) - -SELECT cube_distance('(42,42,42,42)'::cube,'(137,137,137,137)'::cube); - cube_distance ---------------- - 190 -(1 row) - -SELECT cube_distance('(42,42,42)'::cube,'(137,137)'::cube); - cube_distance ------------------- - 140.762210837994 -(1 row) - --- Test of cube function (text to cube) --- -SELECT cube('(1,1.2)'::text); - cube ----------- - (1, 1.2) -(1 row) - -SELECT cube(NULL); - cube ------- - -(1 row) - --- Test of cube_dim function (dimensions stored in cube) --- -SELECT cube_dim('(0)'::cube); - cube_dim ----------- - 1 -(1 row) - -SELECT cube_dim('(0,0)'::cube); - cube_dim ----------- - 2 -(1 row) - -SELECT cube_dim('(0,0,0)'::cube); - cube_dim ----------- - 3 -(1 row) - -SELECT cube_dim('(42,42,42),(42,42,42)'::cube); - cube_dim ----------- - 3 -(1 row) - -SELECT cube_dim('(4,8,15,16,23),(4,8,15,16,23)'::cube); - cube_dim ----------- - 5 -(1 row) - --- Test of cube_ll_coord function (retrieves LL coodinate values) --- -SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 1); - cube_ll_coord ---------------- - -1 -(1 row) - -SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 2); - cube_ll_coord ---------------- - -2 -(1 row) - -SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 3); - cube_ll_coord ---------------- - 0 -(1 row) - -SELECT cube_ll_coord('(1,2),(1,2)'::cube, 1); - cube_ll_coord ---------------- - 1 -(1 row) - -SELECT cube_ll_coord('(1,2),(1,2)'::cube, 2); - cube_ll_coord ---------------- - 2 -(1 row) - -SELECT cube_ll_coord('(1,2),(1,2)'::cube, 3); - cube_ll_coord ---------------- - 0 -(1 row) - -SELECT cube_ll_coord('(42,137)'::cube, 1); - cube_ll_coord ---------------- - 42 -(1 row) - -SELECT cube_ll_coord('(42,137)'::cube, 2); - cube_ll_coord ---------------- - 137 -(1 row) - -SELECT cube_ll_coord('(42,137)'::cube, 3); - cube_ll_coord ---------------- - 0 -(1 row) - --- Test of cube_ur_coord function (retrieves UR coodinate values) --- -SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 1); - cube_ur_coord ---------------- - 2 -(1 row) - -SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 2); - cube_ur_coord ---------------- - 1 -(1 row) - -SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 3); - cube_ur_coord ---------------- - 0 -(1 row) - -SELECT cube_ur_coord('(1,2),(1,2)'::cube, 1); - cube_ur_coord ---------------- - 1 -(1 row) - -SELECT cube_ur_coord('(1,2),(1,2)'::cube, 2); - cube_ur_coord ---------------- - 2 -(1 row) - -SELECT cube_ur_coord('(1,2),(1,2)'::cube, 3); - cube_ur_coord ---------------- - 0 -(1 row) - -SELECT cube_ur_coord('(42,137)'::cube, 1); - cube_ur_coord ---------------- - 42 -(1 row) - -SELECT cube_ur_coord('(42,137)'::cube, 2); - cube_ur_coord ---------------- - 137 -(1 row) - -SELECT cube_ur_coord('(42,137)'::cube, 3); - cube_ur_coord ---------------- - 0 -(1 row) - --- Test of cube_is_point --- -SELECT cube_is_point('(0)'::cube); - cube_is_point ---------------- - t -(1 row) - -SELECT cube_is_point('(0,1,2)'::cube); - cube_is_point ---------------- - t -(1 row) - -SELECT cube_is_point('(0,1,2),(0,1,2)'::cube); - cube_is_point ---------------- - t -(1 row) - -SELECT cube_is_point('(0,1,2),(-1,1,2)'::cube); - cube_is_point ---------------- - f -(1 row) - -SELECT cube_is_point('(0,1,2),(0,-1,2)'::cube); - cube_is_point ---------------- - f -(1 row) - -SELECT cube_is_point('(0,1,2),(0,1,-2)'::cube); - cube_is_point ---------------- - f -(1 row) - --- Test of cube_enlarge (enlarging and shrinking cubes) --- -SELECT cube_enlarge('(0)'::cube, 0, 0); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 0, 1); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 0, 2); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(2),(-2)'::cube, 0, 4); - cube_enlarge --------------- - (-2),(2) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 1, 0); - cube_enlarge --------------- - (-1),(1) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 1, 1); - cube_enlarge --------------- - (-1),(1) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 1, 2); - cube_enlarge ------------------ - (-1, -1),(1, 1) -(1 row) - -SELECT cube_enlarge('(2),(-2)'::cube, 1, 4); - cube_enlarge -------------------------------- - (-3, -1, -1, -1),(3, 1, 1, 1) -(1 row) - -SELECT cube_enlarge('(0)'::cube, -1, 0); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, -1, 1); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, -1, 2); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(2),(-2)'::cube, -1, 4); - cube_enlarge --------------- - (-1),(1) -(1 row) - -SELECT cube_enlarge('(0,0,0)'::cube, 1, 0); - cube_enlarge ------------------------- - (-1, -1, -1),(1, 1, 1) -(1 row) - -SELECT cube_enlarge('(0,0,0)'::cube, 1, 2); - cube_enlarge ------------------------- - (-1, -1, -1),(1, 1, 1) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, 1, 2); - cube_enlarge ------------------ - (-4, -3),(3, 8) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, 3, 2); - cube_enlarge ------------------- - (-6, -5),(5, 10) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, -1, 2); - cube_enlarge ------------------ - (-2, -1),(1, 6) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, -3, 2); - cube_enlarge ---------------------- - (-0.5, 1),(-0.5, 4) -(1 row) - -SELECT cube_enlarge('(42,-23,-23),(42,23,23)'::cube, -23, 5); - cube_enlarge --------------- - (42, 0, 0) -(1 row) - -SELECT cube_enlarge('(42,-23,-23),(42,23,23)'::cube, -24, 5); - cube_enlarge --------------- - (42, 0, 0) -(1 row) - --- Test of cube_union (MBR for two cubes) --- -SELECT cube_union('(1,2),(3,4)'::cube, '(5,6,7),(8,9,10)'::cube); - cube_union ----------------------- - (1, 2, 0),(8, 9, 10) -(1 row) - -SELECT cube_union('(1,2)'::cube, '(4,2,0,0)'::cube); - cube_union ---------------------------- - (1, 2, 0, 0),(4, 2, 0, 0) -(1 row) - -SELECT cube_union('(1,2),(1,2)'::cube, '(4,2),(4,2)'::cube); - cube_union ---------------- - (1, 2),(4, 2) -(1 row) - -SELECT cube_union('(1,2),(1,2)'::cube, '(1,2),(1,2)'::cube); - cube_union ------------- - (1, 2) -(1 row) - -SELECT cube_union('(1,2),(1,2)'::cube, '(1,2,0),(1,2,0)'::cube); - cube_union ------------- - (1, 2, 0) -(1 row) - --- Test of cube_inter --- -SELECT cube_inter('(1,2),(10,11)'::cube, '(3,4), (16,15)'::cube); -- intersects - cube_inter ------------------ - (3, 4),(10, 11) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(3,4), (6,5)'::cube); -- includes - cube_inter ---------------- - (3, 4),(6, 5) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(13,14), (16,15)'::cube); -- no intersection - cube_inter -------------------- - (13, 14),(10, 11) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(3,14), (16,15)'::cube); -- no intersection, but one dimension intersects - cube_inter ------------------- - (3, 14),(10, 11) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(10,11), (16,15)'::cube); -- point intersection - cube_inter ------------- - (10, 11) -(1 row) - -SELECT cube_inter('(1,2,3)'::cube, '(1,2,3)'::cube); -- point args - cube_inter ------------- - (1, 2, 3) -(1 row) - -SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args - cube_inter ---------------------- - (5, 6, 3),(1, 2, 3) -(1 row) - --- Test of cube_size --- -SELECT cube_size('(4,8),(15,16)'::cube); - cube_size ------------ - 88 -(1 row) - -SELECT cube_size('(42,137)'::cube); - cube_size ------------ - 0 -(1 row) - --- Test of distances --- -SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube); - cube_distance ---------------- - 5 -(1 row) - -SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e; - d_e ------ - 5 -(1 row) - -SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube); - distance_chebyshev --------------------- - 4 -(1 row) - -SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c; - d_c ------ - 4 -(1 row) - -SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube); - distance_taxicab ------------------- - 7 -(1 row) - -SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t; - d_t ------ - 7 -(1 row) - --- zero for overlapping -SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube); - cube_distance ---------------- - 0 -(1 row) - -SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube); - distance_chebyshev --------------------- - 0 -(1 row) - -SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube); - distance_taxicab ------------------- - 0 -(1 row) - --- coordinate access -SELECT cube(array[10,20,30], array[40,50,60])->1; - ?column? ----------- - 10 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])->1; - ?column? ----------- - 40 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])->6; - ?column? ----------- - 60 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])->0; -ERROR: cube index 0 is out of bounds -SELECT cube(array[10,20,30], array[40,50,60])->7; -ERROR: cube index 7 is out of bounds -SELECT cube(array[10,20,30], array[40,50,60])->-1; -ERROR: cube index -1 is out of bounds -SELECT cube(array[10,20,30], array[40,50,60])->-6; -ERROR: cube index -6 is out of bounds -SELECT cube(array[10,20,30])->3; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[10,20,30])->6; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[10,20,30])->-6; -ERROR: cube index -6 is out of bounds --- "normalized" coordinate access -SELECT cube(array[10,20,30], array[40,50,60])~>1; - ?column? ----------- - 10 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>1; - ?column? ----------- - 10 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])~>2; - ?column? ----------- - 20 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>2; - ?column? ----------- - 20 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])~>3; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>3; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>0; -ERROR: cube index 0 is out of bounds -SELECT cube(array[40,50,60], array[10,20,30])~>4; - ?column? ----------- - 40 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>(-1); -ERROR: cube index -1 is out of bounds --- Load some example data and build the index --- -CREATE TABLE test_cube (c cube); -\copy test_cube from 'data/test_cube.data' -CREATE INDEX test_cube_ix ON test_cube USING gist (c); -SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' ORDER BY c; - c --------------------------- - (337, 455),(240, 359) - (759, 187),(662, 163) - (1444, 403),(1346, 344) - (1594, 1043),(1517, 971) - (2424, 160),(2424, 81) -(5 rows) - --- Test sorting -SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c; - c --------------------------- - (337, 455),(240, 359) - (759, 187),(662, 163) - (1444, 403),(1346, 344) - (1594, 1043),(1517, 971) - (2424, 160),(2424, 81) -(5 rows) - --- kNN with index -SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5; - c | dist --------------------------+------------------ - (337, 455),(240, 359) | 0 - (759, 187),(662, 163) | 162 - (948, 1201),(907, 1156) | 772.000647668122 - (1444, 403),(1346, 344) | 846 - (369, 1457),(278, 1409) | 909 -(5 rows) - -SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5; - c | dist --------------------------+------ - (337, 455),(240, 359) | 0 - (759, 187),(662, 163) | 162 - (948, 1201),(907, 1156) | 656 - (1444, 403),(1346, 344) | 846 - (369, 1457),(278, 1409) | 909 -(5 rows) - -SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5; - c | dist --------------------------+------ - (337, 455),(240, 359) | 0 - (759, 187),(662, 163) | 162 - (1444, 403),(1346, 344) | 846 - (369, 1457),(278, 1409) | 909 - (948, 1201),(907, 1156) | 1063 -(5 rows) - --- kNN-based sorting -SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner - c ---------------------------- - (54, 38679),(3, 38602) - (83, 10271),(15, 10265) - (122, 46832),(64, 46762) - (167, 17214),(92, 17184) - (161, 24465),(107, 24374) - (162, 26040),(120, 25963) - (154, 4019),(138, 3990) - (259, 1850),(175, 1820) - (207, 40886),(179, 40879) - (288, 49588),(204, 49571) - (270, 32616),(226, 32607) - (318, 31489),(235, 31404) - (337, 455),(240, 359) - (270, 29508),(264, 29440) - (369, 1457),(278, 1409) -(15 rows) - -SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner - c ---------------------------- - (30333, 50),(30273, 6) - (43301, 75),(43227, 43) - (19650, 142),(19630, 51) - (2424, 160),(2424, 81) - (3449, 171),(3354, 108) - (18037, 155),(17941, 109) - (28511, 208),(28479, 114) - (19946, 217),(19941, 118) - (16906, 191),(16816, 139) - (759, 187),(662, 163) - (22684, 266),(22656, 181) - (24423, 255),(24360, 213) - (45989, 249),(45910, 222) - (11399, 377),(11360, 294) - (12162, 389),(12103, 309) -(15 rows) - -SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner - c -------------------------------- - (50027, 49230),(49951, 49214) - (49980, 35004),(49937, 34963) - (49985, 6436),(49927, 6338) - (49999, 27218),(49908, 27176) - (49954, 1340),(49905, 1294) - (49944, 25163),(49902, 25153) - (49981, 34876),(49898, 34786) - (49957, 43390),(49897, 43384) - (49853, 18504),(49848, 18503) - (49902, 41752),(49818, 41746) - (49907, 30225),(49810, 30158) - (49843, 5175),(49808, 5145) - (49887, 24274),(49805, 24184) - (49847, 7128),(49798, 7067) - (49820, 7990),(49771, 7967) -(15 rows) - -SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner - c -------------------------------- - (36311, 50073),(36258, 49987) - (30746, 50040),(30727, 49992) - (2168, 50012),(2108, 49914) - (21551, 49983),(21492, 49885) - (17954, 49975),(17865, 49915) - (3531, 49962),(3463, 49934) - (19128, 49932),(19112, 49849) - (31287, 49923),(31236, 49913) - (43925, 49912),(43888, 49878) - (29261, 49910),(29247, 49818) - (14913, 49873),(14849, 49836) - (20007, 49858),(19921, 49778) - (38266, 49852),(38233, 49844) - (37595, 49849),(37581, 49834) - (46151, 49848),(46058, 49830) -(15 rows) - --- same thing for index with points -CREATE TABLE test_point(c cube); -INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube); -CREATE INDEX ON test_point USING gist(c); -SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate - c --------------------------- - (54, 38679, 3, 38602) - (83, 10271, 15, 10265) - (122, 46832, 64, 46762) - (154, 4019, 138, 3990) - (161, 24465, 107, 24374) - (162, 26040, 120, 25963) - (167, 17214, 92, 17184) - (207, 40886, 179, 40879) - (259, 1850, 175, 1820) - (270, 29508, 264, 29440) - (270, 32616, 226, 32607) - (288, 49588, 204, 49571) - (318, 31489, 235, 31404) - (326, 18837, 285, 18817) - (337, 455, 240, 359) -(15 rows) - -SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate - c ------------------------------- - (30746, 50040, 30727, 49992) - (36311, 50073, 36258, 49987) - (3531, 49962, 3463, 49934) - (17954, 49975, 17865, 49915) - (2168, 50012, 2108, 49914) - (31287, 49923, 31236, 49913) - (21551, 49983, 21492, 49885) - (43925, 49912, 43888, 49878) - (19128, 49932, 19112, 49849) - (38266, 49852, 38233, 49844) - (14913, 49873, 14849, 49836) - (37595, 49849, 37581, 49834) - (46151, 49848, 46058, 49830) - (29261, 49910, 29247, 49818) - (19233, 49824, 19185, 49794) -(15 rows) - diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out index fef749c698..1aa5cf2f98 100644 --- a/contrib/cube/expected/cube_2.out +++ b/contrib/cube/expected/cube_2.out @@ -2,6 +2,14 @@ -- Test cube datatype -- CREATE EXTENSION cube; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + -- -- testing the input and output functions -- @@ -126,16 +134,34 @@ SELECT '-1.0e-7'::cube AS cube; (-1e-007) (1 row) -SELECT '1e-700'::cube AS cube; - cube ------- - (0) +SELECT '1e-300'::cube AS cube; + cube +---------- + (1e-300) (1 row) -SELECT '-1e-700'::cube AS cube; - cube ------- - (0) +SELECT '-1e-300'::cube AS cube; + cube +----------- + (-1e-300) +(1 row) + +SELECT 'infinity'::cube AS cube; + cube +------------ + (Infinity) +(1 row) + +SELECT '-infinity'::cube AS cube; + cube +------------- + (-Infinity) +(1 row) + +SELECT 'NaN'::cube AS cube; + cube +------- + (NaN) (1 row) SELECT '1234567890123456'::cube AS cube; @@ -175,6 +201,12 @@ SELECT '-.1234567890123456'::cube AS cube; (1 row) -- simple lists (points) +SELECT '()'::cube AS cube; + cube +------ + () +(1 row) + SELECT '1,2'::cube AS cube; cube -------- @@ -200,6 +232,12 @@ SELECT '(1,2,3,4,5)'::cube AS cube; (1 row) -- double lists (cubes) +SELECT '(),()'::cube AS cube; + cube +------ + () +(1 row) + SELECT '(0),(0)'::cube AS cube; cube ------ @@ -250,146 +288,145 @@ SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; -- invalid input: parse errors SELECT ''::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT ''::cube AS cube; ^ DETAIL: syntax error at end of input SELECT 'ABC'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT 'ABC'::cube AS cube; ^ DETAIL: syntax error at or near "A" -SELECT '()'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '()'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" SELECT '[]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[]'::cube AS cube; ^ DETAIL: syntax error at or near "]" SELECT '[()]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[()]'::cube AS cube; ^ -DETAIL: syntax error at or near ")" +DETAIL: syntax error at or near "]" SELECT '[(1)]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1)]'::cube AS cube; ^ DETAIL: syntax error at or near "]" SELECT '[(1),]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),]'::cube AS cube; ^ DETAIL: syntax error at or near "]" SELECT '[(1),2]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),2]'::cube AS cube; ^ DETAIL: syntax error at or near "2" SELECT '[(1),(2),(3)]'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),(2),(3)]'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '1,'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,'::cube AS cube; ^ DETAIL: syntax error at end of input SELECT '1,2,'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,2,'::cube AS cube; ^ DETAIL: syntax error at end of input SELECT '1,,2'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,,2'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '(1,)'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,)'::cube AS cube; ^ DETAIL: syntax error at or near ")" SELECT '(1,2,)'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,)'::cube AS cube; ^ DETAIL: syntax error at or near ")" SELECT '(1,,2)'::cube AS cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,,2)'::cube AS cube; ^ DETAIL: syntax error at or near "," -- invalid input: semantic errors and trailing garbage SELECT '[(1),(2)],'::cube AS cube; -- 0 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1),(2)],'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '[(1,2,3),(2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1,2,3),(2,3)]'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2,3) and (2,3). SELECT '[(1,2),(1,2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '[(1,2),(1,2,3)]'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2) and (1,2,3). SELECT '(1),(2),'::cube AS cube; -- 2 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1),(2),'::cube AS cube; ^ DETAIL: syntax error at or near "," SELECT '(1,2,3),(2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,3),(2,3)'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2,3) and (2,3). SELECT '(1,2),(1,2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2),(1,2,3)'::cube AS cube; ^ DETAIL: Different point dimensions in (1,2) and (1,2,3). SELECT '(1,2,3)ab'::cube AS cube; -- 4 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,3)ab'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '(1,2,3)a'::cube AS cube; -- 5 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2,3)a'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '(1,2)('::cube AS cube; -- 5 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '(1,2)('::cube AS cube; ^ DETAIL: syntax error at or near "(" SELECT '1,2ab'::cube AS cube; -- 6 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,2ab'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '1 e7'::cube AS cube; -- 6 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1 e7'::cube AS cube; ^ DETAIL: syntax error at or near "e" SELECT '1,2a'::cube AS cube; -- 7 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1,2a'::cube AS cube; ^ DETAIL: syntax error at or near "a" SELECT '1..2'::cube AS cube; -- 7 -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: SELECT '1..2'::cube AS cube; ^ DETAIL: syntax error at or near ".2" +SELECT '-1e-700'::cube AS cube; -- out of range +ERROR: "-1e-700" is out of range for type double precision +LINE 1: SELECT '-1e-700'::cube AS cube; + ^ -- -- Testing building cubes from float8 values -- @@ -556,12 +593,12 @@ SELECT cube(cube(1,2), 42, 24); -- cube_c_f8_f8 -- Testing limit of CUBE_MAX_DIM dimensions check in cube_in. -- select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... ^ DETAIL: A cube cannot have more than 100 dimensions. select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation +ERROR: invalid input syntax for cube LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... ^ DETAIL: A cube cannot have more than 100 dimensions. @@ -1019,7 +1056,7 @@ SELECT cube_dim('(4,8,15,16,23),(4,8,15,16,23)'::cube); 5 (1 row) --- Test of cube_ll_coord function (retrieves LL coodinate values) +-- Test of cube_ll_coord function (retrieves LL coordinate values) -- SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 1); cube_ll_coord @@ -1075,7 +1112,7 @@ SELECT cube_ll_coord('(42,137)'::cube, 3); 0 (1 row) --- Test of cube_ur_coord function (retrieves UR coodinate values) +-- Test of cube_ur_coord function (retrieves UR coordinate values) -- SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 1); cube_ur_coord diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out deleted file mode 100644 index 31d2d1a64e..0000000000 --- a/contrib/cube/expected/cube_3.out +++ /dev/null @@ -1,1710 +0,0 @@ --- --- Test cube datatype --- -CREATE EXTENSION cube; --- --- testing the input and output functions --- --- Any number (a one-dimensional point) -SELECT '1'::cube AS cube; - cube ------- - (1) -(1 row) - -SELECT '-1'::cube AS cube; - cube ------- - (-1) -(1 row) - -SELECT '1.'::cube AS cube; - cube ------- - (1) -(1 row) - -SELECT '-1.'::cube AS cube; - cube ------- - (-1) -(1 row) - -SELECT '.1'::cube AS cube; - cube -------- - (0.1) -(1 row) - -SELECT '-.1'::cube AS cube; - cube --------- - (-0.1) -(1 row) - -SELECT '1.0'::cube AS cube; - cube ------- - (1) -(1 row) - -SELECT '-1.0'::cube AS cube; - cube ------- - (-1) -(1 row) - -SELECT '1e27'::cube AS cube; - cube ----------- - (1e+027) -(1 row) - -SELECT '-1e27'::cube AS cube; - cube ------------ - (-1e+027) -(1 row) - -SELECT '1.0e27'::cube AS cube; - cube ----------- - (1e+027) -(1 row) - -SELECT '-1.0e27'::cube AS cube; - cube ------------ - (-1e+027) -(1 row) - -SELECT '1e+27'::cube AS cube; - cube ----------- - (1e+027) -(1 row) - -SELECT '-1e+27'::cube AS cube; - cube ------------ - (-1e+027) -(1 row) - -SELECT '1.0e+27'::cube AS cube; - cube ----------- - (1e+027) -(1 row) - -SELECT '-1.0e+27'::cube AS cube; - cube ------------ - (-1e+027) -(1 row) - -SELECT '1e-7'::cube AS cube; - cube ----------- - (1e-007) -(1 row) - -SELECT '-1e-7'::cube AS cube; - cube ------------ - (-1e-007) -(1 row) - -SELECT '1.0e-7'::cube AS cube; - cube ----------- - (1e-007) -(1 row) - -SELECT '-1.0e-7'::cube AS cube; - cube ------------ - (-1e-007) -(1 row) - -SELECT '1e-700'::cube AS cube; - cube ------- - (0) -(1 row) - -SELECT '-1e-700'::cube AS cube; - cube ------- - (-0) -(1 row) - -SELECT '1234567890123456'::cube AS cube; - cube -------------------------- - (1.23456789012346e+015) -(1 row) - -SELECT '+1234567890123456'::cube AS cube; - cube -------------------------- - (1.23456789012346e+015) -(1 row) - -SELECT '-1234567890123456'::cube AS cube; - cube --------------------------- - (-1.23456789012346e+015) -(1 row) - -SELECT '.1234567890123456'::cube AS cube; - cube ---------------------- - (0.123456789012346) -(1 row) - -SELECT '+.1234567890123456'::cube AS cube; - cube ---------------------- - (0.123456789012346) -(1 row) - -SELECT '-.1234567890123456'::cube AS cube; - cube ----------------------- - (-0.123456789012346) -(1 row) - --- simple lists (points) -SELECT '1,2'::cube AS cube; - cube --------- - (1, 2) -(1 row) - -SELECT '(1,2)'::cube AS cube; - cube --------- - (1, 2) -(1 row) - -SELECT '1,2,3,4,5'::cube AS cube; - cube ------------------ - (1, 2, 3, 4, 5) -(1 row) - -SELECT '(1,2,3,4,5)'::cube AS cube; - cube ------------------ - (1, 2, 3, 4, 5) -(1 row) - --- double lists (cubes) -SELECT '(0),(0)'::cube AS cube; - cube ------- - (0) -(1 row) - -SELECT '(0),(1)'::cube AS cube; - cube ---------- - (0),(1) -(1 row) - -SELECT '[(0),(0)]'::cube AS cube; - cube ------- - (0) -(1 row) - -SELECT '[(0),(1)]'::cube AS cube; - cube ---------- - (0),(1) -(1 row) - -SELECT '(0,0,0,0),(0,0,0,0)'::cube AS cube; - cube --------------- - (0, 0, 0, 0) -(1 row) - -SELECT '(0,0,0,0),(1,0,0,0)'::cube AS cube; - cube ---------------------------- - (0, 0, 0, 0),(1, 0, 0, 0) -(1 row) - -SELECT '[(0,0,0,0),(0,0,0,0)]'::cube AS cube; - cube --------------- - (0, 0, 0, 0) -(1 row) - -SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; - cube ---------------------------- - (0, 0, 0, 0),(1, 0, 0, 0) -(1 row) - --- invalid input: parse errors -SELECT ''::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT ''::cube AS cube; - ^ -DETAIL: syntax error at end of input -SELECT 'ABC'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT 'ABC'::cube AS cube; - ^ -DETAIL: syntax error at or near "A" -SELECT '()'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '()'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '[]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[]'::cube AS cube; - ^ -DETAIL: syntax error at or near "]" -SELECT '[()]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[()]'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '[(1)]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1)]'::cube AS cube; - ^ -DETAIL: syntax error at or near "]" -SELECT '[(1),]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1),]'::cube AS cube; - ^ -DETAIL: syntax error at or near "]" -SELECT '[(1),2]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1),2]'::cube AS cube; - ^ -DETAIL: syntax error at or near "2" -SELECT '[(1),(2),(3)]'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '[(1),(2),(3)]'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '1,'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '1,'::cube AS cube; - ^ -DETAIL: syntax error at end of input -SELECT '1,2,'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '1,2,'::cube AS cube; - ^ -DETAIL: syntax error at end of input -SELECT '1,,2'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '1,,2'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '(1,)'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '(1,)'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '(1,2,)'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '(1,2,)'::cube AS cube; - ^ -DETAIL: syntax error at or near ")" -SELECT '(1,,2)'::cube AS cube; -ERROR: bad cube representation -LINE 1: SELECT '(1,,2)'::cube AS cube; - ^ -DETAIL: syntax error at or near "," --- invalid input: semantic errors and trailing garbage -SELECT '[(1),(2)],'::cube AS cube; -- 0 -ERROR: bad cube representation -LINE 1: SELECT '[(1),(2)],'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '[(1,2,3),(2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation -LINE 1: SELECT '[(1,2,3),(2,3)]'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2,3) and (2,3). -SELECT '[(1,2),(1,2,3)]'::cube AS cube; -- 1 -ERROR: bad cube representation -LINE 1: SELECT '[(1,2),(1,2,3)]'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2) and (1,2,3). -SELECT '(1),(2),'::cube AS cube; -- 2 -ERROR: bad cube representation -LINE 1: SELECT '(1),(2),'::cube AS cube; - ^ -DETAIL: syntax error at or near "," -SELECT '(1,2,3),(2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation -LINE 1: SELECT '(1,2,3),(2,3)'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2,3) and (2,3). -SELECT '(1,2),(1,2,3)'::cube AS cube; -- 3 -ERROR: bad cube representation -LINE 1: SELECT '(1,2),(1,2,3)'::cube AS cube; - ^ -DETAIL: Different point dimensions in (1,2) and (1,2,3). -SELECT '(1,2,3)ab'::cube AS cube; -- 4 -ERROR: bad cube representation -LINE 1: SELECT '(1,2,3)ab'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '(1,2,3)a'::cube AS cube; -- 5 -ERROR: bad cube representation -LINE 1: SELECT '(1,2,3)a'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '(1,2)('::cube AS cube; -- 5 -ERROR: bad cube representation -LINE 1: SELECT '(1,2)('::cube AS cube; - ^ -DETAIL: syntax error at or near "(" -SELECT '1,2ab'::cube AS cube; -- 6 -ERROR: bad cube representation -LINE 1: SELECT '1,2ab'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '1 e7'::cube AS cube; -- 6 -ERROR: bad cube representation -LINE 1: SELECT '1 e7'::cube AS cube; - ^ -DETAIL: syntax error at or near "e" -SELECT '1,2a'::cube AS cube; -- 7 -ERROR: bad cube representation -LINE 1: SELECT '1,2a'::cube AS cube; - ^ -DETAIL: syntax error at or near "a" -SELECT '1..2'::cube AS cube; -- 7 -ERROR: bad cube representation -LINE 1: SELECT '1..2'::cube AS cube; - ^ -DETAIL: syntax error at or near ".2" --- --- Testing building cubes from float8 values --- -SELECT cube(0::float8); - cube ------- - (0) -(1 row) - -SELECT cube(1::float8); - cube ------- - (1) -(1 row) - -SELECT cube(1,2); - cube ---------- - (1),(2) -(1 row) - -SELECT cube(cube(1,2),3); - cube ---------------- - (1, 3),(2, 3) -(1 row) - -SELECT cube(cube(1,2),3,4); - cube ---------------- - (1, 3),(2, 4) -(1 row) - -SELECT cube(cube(cube(1,2),3,4),5); - cube ---------------------- - (1, 3, 5),(2, 4, 5) -(1 row) - -SELECT cube(cube(cube(1,2),3,4),5,6); - cube ---------------------- - (1, 3, 5),(2, 4, 6) -(1 row) - --- --- Test that the text -> cube cast was installed. --- -SELECT '(0)'::text::cube; - cube ------- - (0) -(1 row) - --- --- Test the float[] -> cube cast --- -SELECT cube('{0,1,2}'::float[], '{3,4,5}'::float[]); - cube ---------------------- - (0, 1, 2),(3, 4, 5) -(1 row) - -SELECT cube('{0,1,2}'::float[], '{3}'::float[]); -ERROR: UR and LL arrays must be of same length -SELECT cube(NULL::float[], '{3}'::float[]); - cube ------- - -(1 row) - -SELECT cube('{0,1,2}'::float[]); - cube ------------ - (0, 1, 2) -(1 row) - -SELECT cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[3,2,1,1]); - cube_subset ---------------------------- - (5, 3, 1, 1),(8, 7, 6, 6) -(1 row) - -SELECT cube_subset(cube('(1,3,5),(1,3,5)'), ARRAY[3,2,1,1]); - cube_subset --------------- - (5, 3, 1, 1) -(1 row) - -SELECT cube_subset(cube('(1,3,5),(6,7,8)'), ARRAY[4,0]); -ERROR: Index out of bounds -SELECT cube_subset(cube('(6,7,8),(6,7,8)'), ARRAY[4,0]); -ERROR: Index out of bounds --- --- Test point processing --- -SELECT cube('(1,2),(1,2)'); -- cube_in - cube --------- - (1, 2) -(1 row) - -SELECT cube('{0,1,2}'::float[], '{0,1,2}'::float[]); -- cube_a_f8_f8 - cube ------------ - (0, 1, 2) -(1 row) - -SELECT cube('{5,6,7,8}'::float[]); -- cube_a_f8 - cube --------------- - (5, 6, 7, 8) -(1 row) - -SELECT cube(1.37); -- cube_f8 - cube --------- - (1.37) -(1 row) - -SELECT cube(1.37, 1.37); -- cube_f8_f8 - cube --------- - (1.37) -(1 row) - -SELECT cube(cube(1,1), 42); -- cube_c_f8 - cube ---------- - (1, 42) -(1 row) - -SELECT cube(cube(1,2), 42); -- cube_c_f8 - cube ------------------ - (1, 42),(2, 42) -(1 row) - -SELECT cube(cube(1,1), 42, 42); -- cube_c_f8_f8 - cube ---------- - (1, 42) -(1 row) - -SELECT cube(cube(1,1), 42, 24); -- cube_c_f8_f8 - cube ------------------ - (1, 42),(1, 24) -(1 row) - -SELECT cube(cube(1,2), 42, 42); -- cube_c_f8_f8 - cube ------------------ - (1, 42),(2, 42) -(1 row) - -SELECT cube(cube(1,2), 42, 24); -- cube_c_f8_f8 - cube ------------------ - (1, 42),(2, 24) -(1 row) - --- --- Testing limit of CUBE_MAX_DIM dimensions check in cube_in. --- -select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation -LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... - ^ -DETAIL: A cube cannot have more than 100 dimensions. -select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)'::cube; -ERROR: bad cube representation -LINE 1: select '(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0... - ^ -DETAIL: A cube cannot have more than 100 dimensions. --- --- testing the operators --- --- equality/inequality: --- -SELECT '24, 33.20'::cube = '24, 33.20'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '24, 33.20'::cube != '24, 33.20'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '24, 33.20'::cube = '24, 33.21'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '24, 33.20'::cube != '24, 33.21'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; - bool ------- - f -(1 row) - --- "lower than" / "greater than" --- (these operators are not useful for anything but ordering) --- -SELECT '1'::cube > '2'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '1'::cube < '2'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '1,1'::cube > '1,2'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '1,1'::cube < '1,2'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube > '(2,0),(3,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube < '(2,0),(3,1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; - bool ------- - f -(1 row) - --- "overlap" --- -SELECT '1'::cube && '1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '1'::cube && '2'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1,1,1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1,1),(2,2,2)]'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1),(2,2)]'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(2,1,1),(2,2,2)]'::cube AS bool; - bool ------- - f -(1 row) - --- "contained in" (the left operand is the cube entirely enclosed by --- the right operand): --- -SELECT '0'::cube <@ '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,0'::cube <@ '0,0,0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0'::cube <@ '0,0,1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,0'::cube <@ '0,0,1'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '1,0,0'::cube <@ '0,0,1'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube <@ '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube <@ '(-1,-1,-1),(1,1,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube <@ '(-1,-1,-1,-1),(1,1,1,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '1'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '-1'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube <@ '(-1,-1),(1,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-2),(1)'::cube <@ '(-1),(1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(-2),(1)'::cube <@ '(-1,-1),(1,1)'::cube AS bool; - bool ------- - f -(1 row) - --- "contains" (the left operand is the cube that entirely encloses the --- right operand) --- -SELECT '0'::cube @> '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,0'::cube @> '0,0,0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,1'::cube @> '0,0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '0,0,1'::cube @> '0,0,0'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '0,0,1'::cube @> '1,0,0'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(1,0,0),(0,0,1)'::cube @> '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1,-1,-1),(1,1,1)'::cube @> '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1,-1,-1,-1),(1,1,1,1)'::cube @> '(1,0,0),(0,0,1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '0'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '-1'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1,-1),(1,1)'::cube @> '(-1),(1)'::cube AS bool; - bool ------- - t -(1 row) - -SELECT '(-1),(1)'::cube @> '(-2),(1)'::cube AS bool; - bool ------- - f -(1 row) - -SELECT '(-1,-1),(1,1)'::cube @> '(-2),(1)'::cube AS bool; - bool ------- - f -(1 row) - --- Test of distance function --- -SELECT cube_distance('(0)'::cube,'(2,2,2,2)'::cube); - cube_distance ---------------- - 4 -(1 row) - -SELECT cube_distance('(0)'::cube,'(.3,.4)'::cube); - cube_distance ---------------- - 0.5 -(1 row) - -SELECT cube_distance('(2,3,4)'::cube,'(2,3,4)'::cube); - cube_distance ---------------- - 0 -(1 row) - -SELECT cube_distance('(42,42,42,42)'::cube,'(137,137,137,137)'::cube); - cube_distance ---------------- - 190 -(1 row) - -SELECT cube_distance('(42,42,42)'::cube,'(137,137)'::cube); - cube_distance ------------------- - 140.762210837994 -(1 row) - --- Test of cube function (text to cube) --- -SELECT cube('(1,1.2)'::text); - cube ----------- - (1, 1.2) -(1 row) - -SELECT cube(NULL); - cube ------- - -(1 row) - --- Test of cube_dim function (dimensions stored in cube) --- -SELECT cube_dim('(0)'::cube); - cube_dim ----------- - 1 -(1 row) - -SELECT cube_dim('(0,0)'::cube); - cube_dim ----------- - 2 -(1 row) - -SELECT cube_dim('(0,0,0)'::cube); - cube_dim ----------- - 3 -(1 row) - -SELECT cube_dim('(42,42,42),(42,42,42)'::cube); - cube_dim ----------- - 3 -(1 row) - -SELECT cube_dim('(4,8,15,16,23),(4,8,15,16,23)'::cube); - cube_dim ----------- - 5 -(1 row) - --- Test of cube_ll_coord function (retrieves LL coodinate values) --- -SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 1); - cube_ll_coord ---------------- - -1 -(1 row) - -SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 2); - cube_ll_coord ---------------- - -2 -(1 row) - -SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 3); - cube_ll_coord ---------------- - 0 -(1 row) - -SELECT cube_ll_coord('(1,2),(1,2)'::cube, 1); - cube_ll_coord ---------------- - 1 -(1 row) - -SELECT cube_ll_coord('(1,2),(1,2)'::cube, 2); - cube_ll_coord ---------------- - 2 -(1 row) - -SELECT cube_ll_coord('(1,2),(1,2)'::cube, 3); - cube_ll_coord ---------------- - 0 -(1 row) - -SELECT cube_ll_coord('(42,137)'::cube, 1); - cube_ll_coord ---------------- - 42 -(1 row) - -SELECT cube_ll_coord('(42,137)'::cube, 2); - cube_ll_coord ---------------- - 137 -(1 row) - -SELECT cube_ll_coord('(42,137)'::cube, 3); - cube_ll_coord ---------------- - 0 -(1 row) - --- Test of cube_ur_coord function (retrieves UR coodinate values) --- -SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 1); - cube_ur_coord ---------------- - 2 -(1 row) - -SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 2); - cube_ur_coord ---------------- - 1 -(1 row) - -SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 3); - cube_ur_coord ---------------- - 0 -(1 row) - -SELECT cube_ur_coord('(1,2),(1,2)'::cube, 1); - cube_ur_coord ---------------- - 1 -(1 row) - -SELECT cube_ur_coord('(1,2),(1,2)'::cube, 2); - cube_ur_coord ---------------- - 2 -(1 row) - -SELECT cube_ur_coord('(1,2),(1,2)'::cube, 3); - cube_ur_coord ---------------- - 0 -(1 row) - -SELECT cube_ur_coord('(42,137)'::cube, 1); - cube_ur_coord ---------------- - 42 -(1 row) - -SELECT cube_ur_coord('(42,137)'::cube, 2); - cube_ur_coord ---------------- - 137 -(1 row) - -SELECT cube_ur_coord('(42,137)'::cube, 3); - cube_ur_coord ---------------- - 0 -(1 row) - --- Test of cube_is_point --- -SELECT cube_is_point('(0)'::cube); - cube_is_point ---------------- - t -(1 row) - -SELECT cube_is_point('(0,1,2)'::cube); - cube_is_point ---------------- - t -(1 row) - -SELECT cube_is_point('(0,1,2),(0,1,2)'::cube); - cube_is_point ---------------- - t -(1 row) - -SELECT cube_is_point('(0,1,2),(-1,1,2)'::cube); - cube_is_point ---------------- - f -(1 row) - -SELECT cube_is_point('(0,1,2),(0,-1,2)'::cube); - cube_is_point ---------------- - f -(1 row) - -SELECT cube_is_point('(0,1,2),(0,1,-2)'::cube); - cube_is_point ---------------- - f -(1 row) - --- Test of cube_enlarge (enlarging and shrinking cubes) --- -SELECT cube_enlarge('(0)'::cube, 0, 0); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 0, 1); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 0, 2); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(2),(-2)'::cube, 0, 4); - cube_enlarge --------------- - (-2),(2) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 1, 0); - cube_enlarge --------------- - (-1),(1) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 1, 1); - cube_enlarge --------------- - (-1),(1) -(1 row) - -SELECT cube_enlarge('(0)'::cube, 1, 2); - cube_enlarge ------------------ - (-1, -1),(1, 1) -(1 row) - -SELECT cube_enlarge('(2),(-2)'::cube, 1, 4); - cube_enlarge -------------------------------- - (-3, -1, -1, -1),(3, 1, 1, 1) -(1 row) - -SELECT cube_enlarge('(0)'::cube, -1, 0); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, -1, 1); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(0)'::cube, -1, 2); - cube_enlarge --------------- - (0) -(1 row) - -SELECT cube_enlarge('(2),(-2)'::cube, -1, 4); - cube_enlarge --------------- - (-1),(1) -(1 row) - -SELECT cube_enlarge('(0,0,0)'::cube, 1, 0); - cube_enlarge ------------------------- - (-1, -1, -1),(1, 1, 1) -(1 row) - -SELECT cube_enlarge('(0,0,0)'::cube, 1, 2); - cube_enlarge ------------------------- - (-1, -1, -1),(1, 1, 1) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, 1, 2); - cube_enlarge ------------------ - (-4, -3),(3, 8) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, 3, 2); - cube_enlarge ------------------- - (-6, -5),(5, 10) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, -1, 2); - cube_enlarge ------------------ - (-2, -1),(1, 6) -(1 row) - -SELECT cube_enlarge('(2,-2),(-3,7)'::cube, -3, 2); - cube_enlarge ---------------------- - (-0.5, 1),(-0.5, 4) -(1 row) - -SELECT cube_enlarge('(42,-23,-23),(42,23,23)'::cube, -23, 5); - cube_enlarge --------------- - (42, 0, 0) -(1 row) - -SELECT cube_enlarge('(42,-23,-23),(42,23,23)'::cube, -24, 5); - cube_enlarge --------------- - (42, 0, 0) -(1 row) - --- Test of cube_union (MBR for two cubes) --- -SELECT cube_union('(1,2),(3,4)'::cube, '(5,6,7),(8,9,10)'::cube); - cube_union ----------------------- - (1, 2, 0),(8, 9, 10) -(1 row) - -SELECT cube_union('(1,2)'::cube, '(4,2,0,0)'::cube); - cube_union ---------------------------- - (1, 2, 0, 0),(4, 2, 0, 0) -(1 row) - -SELECT cube_union('(1,2),(1,2)'::cube, '(4,2),(4,2)'::cube); - cube_union ---------------- - (1, 2),(4, 2) -(1 row) - -SELECT cube_union('(1,2),(1,2)'::cube, '(1,2),(1,2)'::cube); - cube_union ------------- - (1, 2) -(1 row) - -SELECT cube_union('(1,2),(1,2)'::cube, '(1,2,0),(1,2,0)'::cube); - cube_union ------------- - (1, 2, 0) -(1 row) - --- Test of cube_inter --- -SELECT cube_inter('(1,2),(10,11)'::cube, '(3,4), (16,15)'::cube); -- intersects - cube_inter ------------------ - (3, 4),(10, 11) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(3,4), (6,5)'::cube); -- includes - cube_inter ---------------- - (3, 4),(6, 5) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(13,14), (16,15)'::cube); -- no intersection - cube_inter -------------------- - (13, 14),(10, 11) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(3,14), (16,15)'::cube); -- no intersection, but one dimension intersects - cube_inter ------------------- - (3, 14),(10, 11) -(1 row) - -SELECT cube_inter('(1,2),(10,11)'::cube, '(10,11), (16,15)'::cube); -- point intersection - cube_inter ------------- - (10, 11) -(1 row) - -SELECT cube_inter('(1,2,3)'::cube, '(1,2,3)'::cube); -- point args - cube_inter ------------- - (1, 2, 3) -(1 row) - -SELECT cube_inter('(1,2,3)'::cube, '(5,6,3)'::cube); -- point args - cube_inter ---------------------- - (5, 6, 3),(1, 2, 3) -(1 row) - --- Test of cube_size --- -SELECT cube_size('(4,8),(15,16)'::cube); - cube_size ------------ - 88 -(1 row) - -SELECT cube_size('(42,137)'::cube); - cube_size ------------ - 0 -(1 row) - --- Test of distances --- -SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube); - cube_distance ---------------- - 5 -(1 row) - -SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e; - d_e ------ - 5 -(1 row) - -SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube); - distance_chebyshev --------------------- - 4 -(1 row) - -SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c; - d_c ------ - 4 -(1 row) - -SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube); - distance_taxicab ------------------- - 7 -(1 row) - -SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t; - d_t ------ - 7 -(1 row) - --- zero for overlapping -SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube); - cube_distance ---------------- - 0 -(1 row) - -SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube); - distance_chebyshev --------------------- - 0 -(1 row) - -SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube); - distance_taxicab ------------------- - 0 -(1 row) - --- coordinate access -SELECT cube(array[10,20,30], array[40,50,60])->1; - ?column? ----------- - 10 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])->1; - ?column? ----------- - 40 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])->6; - ?column? ----------- - 60 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])->0; -ERROR: cube index 0 is out of bounds -SELECT cube(array[10,20,30], array[40,50,60])->7; -ERROR: cube index 7 is out of bounds -SELECT cube(array[10,20,30], array[40,50,60])->-1; -ERROR: cube index -1 is out of bounds -SELECT cube(array[10,20,30], array[40,50,60])->-6; -ERROR: cube index -6 is out of bounds -SELECT cube(array[10,20,30])->3; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[10,20,30])->6; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[10,20,30])->-6; -ERROR: cube index -6 is out of bounds --- "normalized" coordinate access -SELECT cube(array[10,20,30], array[40,50,60])~>1; - ?column? ----------- - 10 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>1; - ?column? ----------- - 10 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])~>2; - ?column? ----------- - 20 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>2; - ?column? ----------- - 20 -(1 row) - -SELECT cube(array[10,20,30], array[40,50,60])~>3; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>3; - ?column? ----------- - 30 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>0; -ERROR: cube index 0 is out of bounds -SELECT cube(array[40,50,60], array[10,20,30])~>4; - ?column? ----------- - 40 -(1 row) - -SELECT cube(array[40,50,60], array[10,20,30])~>(-1); -ERROR: cube index -1 is out of bounds --- Load some example data and build the index --- -CREATE TABLE test_cube (c cube); -\copy test_cube from 'data/test_cube.data' -CREATE INDEX test_cube_ix ON test_cube USING gist (c); -SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' ORDER BY c; - c --------------------------- - (337, 455),(240, 359) - (759, 187),(662, 163) - (1444, 403),(1346, 344) - (1594, 1043),(1517, 971) - (2424, 160),(2424, 81) -(5 rows) - --- Test sorting -SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c; - c --------------------------- - (337, 455),(240, 359) - (759, 187),(662, 163) - (1444, 403),(1346, 344) - (1594, 1043),(1517, 971) - (2424, 160),(2424, 81) -(5 rows) - --- kNN with index -SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5; - c | dist --------------------------+------------------ - (337, 455),(240, 359) | 0 - (759, 187),(662, 163) | 162 - (948, 1201),(907, 1156) | 772.000647668122 - (1444, 403),(1346, 344) | 846 - (369, 1457),(278, 1409) | 909 -(5 rows) - -SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5; - c | dist --------------------------+------ - (337, 455),(240, 359) | 0 - (759, 187),(662, 163) | 162 - (948, 1201),(907, 1156) | 656 - (1444, 403),(1346, 344) | 846 - (369, 1457),(278, 1409) | 909 -(5 rows) - -SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5; - c | dist --------------------------+------ - (337, 455),(240, 359) | 0 - (759, 187),(662, 163) | 162 - (1444, 403),(1346, 344) | 846 - (369, 1457),(278, 1409) | 909 - (948, 1201),(907, 1156) | 1063 -(5 rows) - --- kNN-based sorting -SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner - c ---------------------------- - (54, 38679),(3, 38602) - (83, 10271),(15, 10265) - (122, 46832),(64, 46762) - (167, 17214),(92, 17184) - (161, 24465),(107, 24374) - (162, 26040),(120, 25963) - (154, 4019),(138, 3990) - (259, 1850),(175, 1820) - (207, 40886),(179, 40879) - (288, 49588),(204, 49571) - (270, 32616),(226, 32607) - (318, 31489),(235, 31404) - (337, 455),(240, 359) - (270, 29508),(264, 29440) - (369, 1457),(278, 1409) -(15 rows) - -SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner - c ---------------------------- - (30333, 50),(30273, 6) - (43301, 75),(43227, 43) - (19650, 142),(19630, 51) - (2424, 160),(2424, 81) - (3449, 171),(3354, 108) - (18037, 155),(17941, 109) - (28511, 208),(28479, 114) - (19946, 217),(19941, 118) - (16906, 191),(16816, 139) - (759, 187),(662, 163) - (22684, 266),(22656, 181) - (24423, 255),(24360, 213) - (45989, 249),(45910, 222) - (11399, 377),(11360, 294) - (12162, 389),(12103, 309) -(15 rows) - -SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner - c -------------------------------- - (50027, 49230),(49951, 49214) - (49980, 35004),(49937, 34963) - (49985, 6436),(49927, 6338) - (49999, 27218),(49908, 27176) - (49954, 1340),(49905, 1294) - (49944, 25163),(49902, 25153) - (49981, 34876),(49898, 34786) - (49957, 43390),(49897, 43384) - (49853, 18504),(49848, 18503) - (49902, 41752),(49818, 41746) - (49907, 30225),(49810, 30158) - (49843, 5175),(49808, 5145) - (49887, 24274),(49805, 24184) - (49847, 7128),(49798, 7067) - (49820, 7990),(49771, 7967) -(15 rows) - -SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner - c -------------------------------- - (36311, 50073),(36258, 49987) - (30746, 50040),(30727, 49992) - (2168, 50012),(2108, 49914) - (21551, 49983),(21492, 49885) - (17954, 49975),(17865, 49915) - (3531, 49962),(3463, 49934) - (19128, 49932),(19112, 49849) - (31287, 49923),(31236, 49913) - (43925, 49912),(43888, 49878) - (29261, 49910),(29247, 49818) - (14913, 49873),(14849, 49836) - (20007, 49858),(19921, 49778) - (38266, 49852),(38233, 49844) - (37595, 49849),(37581, 49834) - (46151, 49848),(46058, 49830) -(15 rows) - --- same thing for index with points -CREATE TABLE test_point(c cube); -INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube); -CREATE INDEX ON test_point USING gist(c); -SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate - c --------------------------- - (54, 38679, 3, 38602) - (83, 10271, 15, 10265) - (122, 46832, 64, 46762) - (154, 4019, 138, 3990) - (161, 24465, 107, 24374) - (162, 26040, 120, 25963) - (167, 17214, 92, 17184) - (207, 40886, 179, 40879) - (259, 1850, 175, 1820) - (270, 29508, 264, 29440) - (270, 32616, 226, 32607) - (288, 49588, 204, 49571) - (318, 31489, 235, 31404) - (326, 18837, 285, 18817) - (337, 455, 240, 359) -(15 rows) - -SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate - c ------------------------------- - (30746, 50040, 30727, 49992) - (36311, 50073, 36258, 49987) - (3531, 49962, 3463, 49934) - (17954, 49975, 17865, 49915) - (2168, 50012, 2108, 49914) - (31287, 49923, 31236, 49913) - (21551, 49983, 21492, 49885) - (43925, 49912, 43888, 49878) - (19128, 49932, 19112, 49849) - (38266, 49852, 38233, 49844) - (14913, 49873, 14849, 49836) - (37595, 49849, 37581, 49834) - (46151, 49848, 46058, 49830) - (29261, 49910, 29247, 49818) - (19233, 49824, 19185, 49794) -(15 rows) - diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql index e225fb7da1..58ea3ad811 100644 --- a/contrib/cube/sql/cube.sql +++ b/contrib/cube/sql/cube.sql @@ -4,6 +4,11 @@ CREATE EXTENSION cube; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + -- -- testing the input and output functions -- @@ -29,8 +34,11 @@ SELECT '1e-7'::cube AS cube; SELECT '-1e-7'::cube AS cube; SELECT '1.0e-7'::cube AS cube; SELECT '-1.0e-7'::cube AS cube; -SELECT '1e-700'::cube AS cube; -SELECT '-1e-700'::cube AS cube; +SELECT '1e-300'::cube AS cube; +SELECT '-1e-300'::cube AS cube; +SELECT 'infinity'::cube AS cube; +SELECT '-infinity'::cube AS cube; +SELECT 'NaN'::cube AS cube; SELECT '1234567890123456'::cube AS cube; SELECT '+1234567890123456'::cube AS cube; SELECT '-1234567890123456'::cube AS cube; @@ -39,12 +47,14 @@ SELECT '+.1234567890123456'::cube AS cube; SELECT '-.1234567890123456'::cube AS cube; -- simple lists (points) +SELECT '()'::cube AS cube; SELECT '1,2'::cube AS cube; SELECT '(1,2)'::cube AS cube; SELECT '1,2,3,4,5'::cube AS cube; SELECT '(1,2,3,4,5)'::cube AS cube; -- double lists (cubes) +SELECT '(),()'::cube AS cube; SELECT '(0),(0)'::cube AS cube; SELECT '(0),(1)'::cube AS cube; SELECT '[(0),(0)]'::cube AS cube; @@ -57,7 +67,6 @@ SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; -- invalid input: parse errors SELECT ''::cube AS cube; SELECT 'ABC'::cube AS cube; -SELECT '()'::cube AS cube; SELECT '[]'::cube AS cube; SELECT '[()]'::cube AS cube; SELECT '[(1)]'::cube AS cube; @@ -85,6 +94,7 @@ SELECT '1,2ab'::cube AS cube; -- 6 SELECT '1 e7'::cube AS cube; -- 6 SELECT '1,2a'::cube AS cube; -- 7 SELECT '1..2'::cube AS cube; -- 7 +SELECT '-1e-700'::cube AS cube; -- out of range -- -- Testing building cubes from float8 values @@ -246,7 +256,7 @@ SELECT cube_dim('(0,0,0)'::cube); SELECT cube_dim('(42,42,42),(42,42,42)'::cube); SELECT cube_dim('(4,8,15,16,23),(4,8,15,16,23)'::cube); --- Test of cube_ll_coord function (retrieves LL coodinate values) +-- Test of cube_ll_coord function (retrieves LL coordinate values) -- SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 1); SELECT cube_ll_coord('(-1,1),(2,-2)'::cube, 2); @@ -258,7 +268,7 @@ SELECT cube_ll_coord('(42,137)'::cube, 1); SELECT cube_ll_coord('(42,137)'::cube, 2); SELECT cube_ll_coord('(42,137)'::cube, 3); --- Test of cube_ur_coord function (retrieves UR coodinate values) +-- Test of cube_ur_coord function (retrieves UR coordinate values) -- SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 1); SELECT cube_ur_coord('(-1,1),(2,-2)'::cube, 2); diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c index 9c8e308358..a6a3c09ff8 100644 --- a/contrib/dblink/dblink.c +++ b/contrib/dblink/dblink.c @@ -9,7 +9,7 @@ * Shridhar Daithankar <shridhar_daithankar@persistent.co.in> * * contrib/dblink/dblink.c - * Copyright (c) 2001-2016, PostgreSQL Global Development Group + * Copyright (c) 2001-2017, PostgreSQL Global Development Group * ALL RIGHTS RESERVED; * * Permission to use, copy, modify, and distribute this software and its @@ -40,6 +40,7 @@ #include "access/reloptions.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_type.h" #include "catalog/pg_user_mapping.h" @@ -58,8 +59,7 @@ #include "utils/memutils.h" #include "utils/rel.h" #include "utils/tqual.h" - -#include "dblink.h" +#include "utils/varlena.h" PG_MODULE_MAGIC; @@ -112,7 +112,8 @@ static Relation get_rel_from_relname(text *relname_text, LOCKMODE lockmode, AclM static char *generate_relation_name(Relation rel); static void dblink_connstr_check(const char *connstr); static void dblink_security_check(PGconn *conn, remoteConn *rconn); -static void dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail); +static void dblink_res_error(PGconn *conn, const char *conname, PGresult *res, + const char *dblink_context_msg, bool fail); static char *get_connect_string(const char *servername); static char *escape_param_str(const char *from); static void validate_pkattnums(Relation rel, @@ -143,98 +144,108 @@ typedef struct remoteConnHashEnt /* initial number of connection hashes */ #define NUMCONN 16 -/* general utility */ -#define xpfree(var_) \ - do { \ - if (var_ != NULL) \ - { \ - pfree(var_); \ - var_ = NULL; \ - } \ - } while (0) - -#define xpstrdup(var_c, var_) \ - do { \ - if (var_ != NULL) \ - var_c = pstrdup(var_); \ - else \ - var_c = NULL; \ - } while (0) - -#define DBLINK_RES_INTERNALERROR(p2) \ - do { \ - msg = pstrdup(PQerrorMessage(conn)); \ - if (res) \ - PQclear(res); \ - elog(ERROR, "%s: %s", p2, msg); \ - } while (0) - -#define DBLINK_CONN_NOT_AVAIL \ - do { \ - if(conname) \ - ereport(ERROR, \ - (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), \ - errmsg("connection \"%s\" not available", conname))); \ - else \ - ereport(ERROR, \ - (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), \ - errmsg("connection not available"))); \ - } while (0) - -#define DBLINK_GET_CONN \ - do { \ - char *conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(0)); \ - rconn = getConnectionByName(conname_or_str); \ - if (rconn) \ - { \ - conn = rconn->conn; \ - conname = conname_or_str; \ - } \ - else \ - { \ - connstr = get_connect_string(conname_or_str); \ - if (connstr == NULL) \ - { \ - connstr = conname_or_str; \ - } \ - dblink_connstr_check(connstr); \ - conn = PQconnectdb(connstr); \ - if (PQstatus(conn) == CONNECTION_BAD) \ - { \ - msg = pstrdup(PQerrorMessage(conn)); \ - PQfinish(conn); \ - ereport(ERROR, \ - (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), \ - errmsg("could not establish connection"), \ - errdetail_internal("%s", msg))); \ - } \ - dblink_security_check(conn, rconn); \ - if (PQclientEncoding(conn) != GetDatabaseEncoding()) \ - PQsetClientEncoding(conn, GetDatabaseEncodingName()); \ - freeconn = true; \ - } \ - } while (0) - -#define DBLINK_GET_NAMED_CONN \ - do { \ - conname = text_to_cstring(PG_GETARG_TEXT_PP(0)); \ - rconn = getConnectionByName(conname); \ - if (rconn) \ - conn = rconn->conn; \ - else \ - DBLINK_CONN_NOT_AVAIL; \ - } while (0) - -#define DBLINK_INIT \ - do { \ - if (!pconn) \ - { \ - pconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext, sizeof(remoteConn)); \ - pconn->conn = NULL; \ - pconn->openCursorCount = 0; \ - pconn->newXactForCursor = FALSE; \ - } \ - } while (0) +static char * +xpstrdup(const char *in) +{ + if (in == NULL) + return NULL; + return pstrdup(in); +} + +static void +pg_attribute_noreturn() +dblink_res_internalerror(PGconn *conn, PGresult *res, const char *p2) +{ + char *msg = pchomp(PQerrorMessage(conn)); + + if (res) + PQclear(res); + elog(ERROR, "%s: %s", p2, msg); +} + +static void +pg_attribute_noreturn() +dblink_conn_not_avail(const char *conname) +{ + if (conname) + ereport(ERROR, + (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), + errmsg("connection \"%s\" not available", conname))); + else + ereport(ERROR, + (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), + errmsg("connection not available"))); +} + +static void +dblink_get_conn(char *conname_or_str, + PGconn *volatile * conn_p, char **conname_p, volatile bool *freeconn_p) +{ + remoteConn *rconn = getConnectionByName(conname_or_str); + PGconn *conn; + char *conname; + bool freeconn; + + if (rconn) + { + conn = rconn->conn; + conname = conname_or_str; + freeconn = false; + } + else + { + const char *connstr; + + connstr = get_connect_string(conname_or_str); + if (connstr == NULL) + connstr = conname_or_str; + dblink_connstr_check(connstr); + conn = PQconnectdb(connstr); + if (PQstatus(conn) == CONNECTION_BAD) + { + char *msg = pchomp(PQerrorMessage(conn)); + + PQfinish(conn); + ereport(ERROR, + (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), + errmsg("could not establish connection"), + errdetail_internal("%s", msg))); + } + dblink_security_check(conn, rconn); + if (PQclientEncoding(conn) != GetDatabaseEncoding()) + PQsetClientEncoding(conn, GetDatabaseEncodingName()); + freeconn = true; + conname = NULL; + } + + *conn_p = conn; + *conname_p = conname; + *freeconn_p = freeconn; +} + +static PGconn * +dblink_get_named_conn(const char *conname) +{ + remoteConn *rconn = getConnectionByName(conname); + + if (rconn) + return rconn->conn; + + dblink_conn_not_avail(conname); + return NULL; /* keep compiler quiet */ +} + +static void +dblink_init(void) +{ + if (!pconn) + { + pconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext, sizeof(remoteConn)); + pconn->conn = NULL; + pconn->openCursorCount = 0; + pconn->newXactForCursor = FALSE; + } +} /* * Create a persistent connection to another database @@ -250,7 +261,7 @@ dblink_connect(PG_FUNCTION_ARGS) PGconn *conn = NULL; remoteConn *rconn = NULL; - DBLINK_INIT; + dblink_init(); if (PG_NARGS() == 2) { @@ -275,7 +286,7 @@ dblink_connect(PG_FUNCTION_ARGS) if (PQstatus(conn) == CONNECTION_BAD) { - msg = pstrdup(PQerrorMessage(conn)); + msg = pchomp(PQerrorMessage(conn)); PQfinish(conn); if (rconn) pfree(rconn); @@ -299,7 +310,11 @@ dblink_connect(PG_FUNCTION_ARGS) createNewConnection(connname, rconn); } else + { + if (pconn->conn) + PQfinish(pconn->conn); pconn->conn = conn; + } PG_RETURN_TEXT_P(cstring_to_text("OK")); } @@ -315,7 +330,7 @@ dblink_disconnect(PG_FUNCTION_ARGS) remoteConn *rconn = NULL; PGconn *conn = NULL; - DBLINK_INIT; + dblink_init(); if (PG_NARGS() == 1) { @@ -328,7 +343,7 @@ dblink_disconnect(PG_FUNCTION_ARGS) conn = pconn->conn; if (!conn) - DBLINK_CONN_NOT_AVAIL; + dblink_conn_not_avail(conname); PQfinish(conn); if (rconn) @@ -349,9 +364,8 @@ PG_FUNCTION_INFO_V1(dblink_open); Datum dblink_open(PG_FUNCTION_ARGS) { - char *msg; PGresult *res = NULL; - PGconn *conn = NULL; + PGconn *conn; char *curname = NULL; char *sql = NULL; char *conname = NULL; @@ -359,7 +373,7 @@ dblink_open(PG_FUNCTION_ARGS) remoteConn *rconn = NULL; bool fail = true; /* default to backward compatible behavior */ - DBLINK_INIT; + dblink_init(); initStringInfo(&buf); if (PG_NARGS() == 2) @@ -398,16 +412,16 @@ dblink_open(PG_FUNCTION_ARGS) } if (!rconn || !rconn->conn) - DBLINK_CONN_NOT_AVAIL; - else - conn = rconn->conn; + dblink_conn_not_avail(conname); + + conn = rconn->conn; /* If we are not in a transaction, start one */ if (PQtransactionStatus(conn) == PQTRANS_IDLE) { res = PQexec(conn, "BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK) - DBLINK_RES_INTERNALERROR("begin error"); + dblink_res_internalerror(conn, res, "begin error"); PQclear(res); rconn->newXactForCursor = TRUE; @@ -427,7 +441,7 @@ dblink_open(PG_FUNCTION_ARGS) res = PQexec(conn, buf.data); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { - dblink_res_error(conname, res, "could not open cursor", fail); + dblink_res_error(conn, conname, res, "could not open cursor", fail); PG_RETURN_TEXT_P(cstring_to_text("ERROR")); } @@ -442,16 +456,15 @@ PG_FUNCTION_INFO_V1(dblink_close); Datum dblink_close(PG_FUNCTION_ARGS) { - PGconn *conn = NULL; + PGconn *conn; PGresult *res = NULL; char *curname = NULL; char *conname = NULL; StringInfoData buf; - char *msg; remoteConn *rconn = NULL; bool fail = true; /* default to backward compatible behavior */ - DBLINK_INIT; + dblink_init(); initStringInfo(&buf); if (PG_NARGS() == 1) @@ -486,9 +499,9 @@ dblink_close(PG_FUNCTION_ARGS) } if (!rconn || !rconn->conn) - DBLINK_CONN_NOT_AVAIL; - else - conn = rconn->conn; + dblink_conn_not_avail(conname); + + conn = rconn->conn; appendStringInfo(&buf, "CLOSE %s", curname); @@ -496,7 +509,7 @@ dblink_close(PG_FUNCTION_ARGS) res = PQexec(conn, buf.data); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { - dblink_res_error(conname, res, "could not close cursor", fail); + dblink_res_error(conn, conname, res, "could not close cursor", fail); PG_RETURN_TEXT_P(cstring_to_text("ERROR")); } @@ -514,7 +527,7 @@ dblink_close(PG_FUNCTION_ARGS) res = PQexec(conn, "COMMIT"); if (PQresultStatus(res) != PGRES_COMMAND_OK) - DBLINK_RES_INTERNALERROR("commit error"); + dblink_res_internalerror(conn, res, "commit error"); PQclear(res); } } @@ -540,7 +553,7 @@ dblink_fetch(PG_FUNCTION_ARGS) prepTuplestoreResult(fcinfo); - DBLINK_INIT; + dblink_init(); if (PG_NARGS() == 4) { @@ -584,7 +597,7 @@ dblink_fetch(PG_FUNCTION_ARGS) } if (!conn) - DBLINK_CONN_NOT_AVAIL; + dblink_conn_not_avail(conname); initStringInfo(&buf); appendStringInfo(&buf, "FETCH %d FROM %s", howmany, curname); @@ -599,7 +612,8 @@ dblink_fetch(PG_FUNCTION_ARGS) (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) { - dblink_res_error(conname, res, "could not fetch from cursor", fail); + dblink_res_error(conn, conname, res, + "could not fetch from cursor", fail); return (Datum) 0; } else if (PQresultStatus(res) == PGRES_COMMAND_OK) @@ -629,15 +643,13 @@ PG_FUNCTION_INFO_V1(dblink_send_query); Datum dblink_send_query(PG_FUNCTION_ARGS) { - char *conname = NULL; - PGconn *conn = NULL; - char *sql = NULL; - remoteConn *rconn = NULL; + PGconn *conn; + char *sql; int retval; if (PG_NARGS() == 2) { - DBLINK_GET_NAMED_CONN; + conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0))); sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); } else @@ -647,7 +659,7 @@ dblink_send_query(PG_FUNCTION_ARGS) /* async query send */ retval = PQsendQuery(conn, sql); if (retval != 1) - elog(NOTICE, "could not send query: %s", PQerrorMessage(conn)); + elog(NOTICE, "could not send query: %s", pchomp(PQerrorMessage(conn))); PG_RETURN_INT32(retval); } @@ -667,15 +679,12 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async) prepTuplestoreResult(fcinfo); - DBLINK_INIT; + dblink_init(); PG_TRY(); { - char *msg; - char *connstr = NULL; char *sql = NULL; char *conname = NULL; - remoteConn *rconn = NULL; bool fail = true; /* default to backward compatible */ if (!is_async) @@ -683,23 +692,25 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async) if (PG_NARGS() == 3) { /* text,text,bool */ - DBLINK_GET_CONN; + conname = text_to_cstring(PG_GETARG_TEXT_PP(0)); sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); fail = PG_GETARG_BOOL(2); + dblink_get_conn(conname, &conn, &conname, &freeconn); } else if (PG_NARGS() == 2) { /* text,text or text,bool */ if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID) { - conn = pconn->conn; sql = text_to_cstring(PG_GETARG_TEXT_PP(0)); fail = PG_GETARG_BOOL(1); + conn = pconn->conn; } else { - DBLINK_GET_CONN; + conname = text_to_cstring(PG_GETARG_TEXT_PP(0)); sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); + dblink_get_conn(conname, &conn, &conname, &freeconn); } } else if (PG_NARGS() == 1) @@ -715,16 +726,18 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async) else /* is_async */ { /* get async result */ + conname = text_to_cstring(PG_GETARG_TEXT_PP(0)); + if (PG_NARGS() == 2) { /* text,bool */ - DBLINK_GET_NAMED_CONN; fail = PG_GETARG_BOOL(1); + conn = dblink_get_named_conn(conname); } else if (PG_NARGS() == 1) { /* text */ - DBLINK_GET_NAMED_CONN; + conn = dblink_get_named_conn(conname); } else /* shouldn't happen */ @@ -732,7 +745,7 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async) } if (!conn) - DBLINK_CONN_NOT_AVAIL; + dblink_conn_not_avail(conname); if (!is_async) { @@ -750,8 +763,8 @@ dblink_record_internal(FunctionCallInfo fcinfo, bool is_async) if (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK) { - dblink_res_error(conname, res, "could not execute query", - fail); + dblink_res_error(conn, conname, res, + "could not execute query", fail); /* if fail isn't set, we'll return an empty query result */ } else @@ -980,9 +993,7 @@ materializeQueryResult(FunctionCallInfo fcinfo, /* Create short-lived memory context for data conversions */ sinfo.tmpcontext = AllocSetContextCreate(CurrentMemoryContext, "dblink temporary context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); /* execute query, collecting any tuples into the tuplestore */ res = storeQueryResult(&sinfo, conn, sql); @@ -998,7 +1009,8 @@ materializeQueryResult(FunctionCallInfo fcinfo, PGresult *res1 = res; res = NULL; - dblink_res_error(conname, res1, "could not execute query", fail); + dblink_res_error(conn, conname, res1, + "could not execute query", fail); /* if fail isn't set, we'll return an empty query result */ } else if (PQresultStatus(res) == PGRES_COMMAND_OK) @@ -1084,7 +1096,7 @@ storeQueryResult(volatile storeInfo *sinfo, PGconn *conn, const char *sql) PGresult *res; if (!PQsendQuery(conn, sql)) - elog(ERROR, "could not send query: %s", PQerrorMessage(conn)); + elog(ERROR, "could not send query: %s", pchomp(PQerrorMessage(conn))); if (!PQsetSingleRowMode(conn)) /* shouldn't fail */ elog(ERROR, "failed to set single-row mode for dblink query"); @@ -1294,12 +1306,10 @@ PG_FUNCTION_INFO_V1(dblink_is_busy); Datum dblink_is_busy(PG_FUNCTION_ARGS) { - char *conname = NULL; - PGconn *conn = NULL; - remoteConn *rconn = NULL; + PGconn *conn; - DBLINK_INIT; - DBLINK_GET_NAMED_CONN; + dblink_init(); + conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0))); PQconsumeInput(conn); PG_RETURN_INT32(PQisBusy(conn)); @@ -1320,15 +1330,13 @@ PG_FUNCTION_INFO_V1(dblink_cancel_query); Datum dblink_cancel_query(PG_FUNCTION_ARGS) { - int res = 0; - char *conname = NULL; - PGconn *conn = NULL; - remoteConn *rconn = NULL; + int res; + PGconn *conn; PGcancel *cancel; char errbuf[256]; - DBLINK_INIT; - DBLINK_GET_NAMED_CONN; + dblink_init(); + conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0))); cancel = PQgetCancel(conn); res = PQcancel(cancel, errbuf, 256); @@ -1356,18 +1364,16 @@ Datum dblink_error_message(PG_FUNCTION_ARGS) { char *msg; - char *conname = NULL; - PGconn *conn = NULL; - remoteConn *rconn = NULL; + PGconn *conn; - DBLINK_INIT; - DBLINK_GET_NAMED_CONN; + dblink_init(); + conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0))); msg = PQerrorMessage(conn); if (msg == NULL || msg[0] == '\0') PG_RETURN_TEXT_P(cstring_to_text("OK")); else - PG_RETURN_TEXT_P(cstring_to_text(msg)); + PG_RETURN_TEXT_P(cstring_to_text(pchomp(msg))); } /* @@ -1381,38 +1387,37 @@ dblink_exec(PG_FUNCTION_ARGS) PGconn *volatile conn = NULL; volatile bool freeconn = false; - DBLINK_INIT; + dblink_init(); PG_TRY(); { - char *msg; PGresult *res = NULL; - char *connstr = NULL; char *sql = NULL; char *conname = NULL; - remoteConn *rconn = NULL; bool fail = true; /* default to backward compatible behavior */ if (PG_NARGS() == 3) { /* must be text,text,bool */ - DBLINK_GET_CONN; + conname = text_to_cstring(PG_GETARG_TEXT_PP(0)); sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); fail = PG_GETARG_BOOL(2); + dblink_get_conn(conname, &conn, &conname, &freeconn); } else if (PG_NARGS() == 2) { /* might be text,text or text,bool */ if (get_fn_expr_argtype(fcinfo->flinfo, 1) == BOOLOID) { - conn = pconn->conn; sql = text_to_cstring(PG_GETARG_TEXT_PP(0)); fail = PG_GETARG_BOOL(1); + conn = pconn->conn; } else { - DBLINK_GET_CONN; + conname = text_to_cstring(PG_GETARG_TEXT_PP(0)); sql = text_to_cstring(PG_GETARG_TEXT_PP(1)); + dblink_get_conn(conname, &conn, &conname, &freeconn); } } else if (PG_NARGS() == 1) @@ -1426,14 +1431,15 @@ dblink_exec(PG_FUNCTION_ARGS) elog(ERROR, "wrong number of arguments"); if (!conn) - DBLINK_CONN_NOT_AVAIL; + dblink_conn_not_avail(conname); res = PQexec(conn, sql); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK && PQresultStatus(res) != PGRES_TUPLES_OK)) { - dblink_res_error(conname, res, "could not execute command", fail); + dblink_res_error(conn, conname, res, + "could not execute command", fail); /* * and save a copy of the command status string to return as our @@ -1508,7 +1514,7 @@ dblink_get_pkey(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* open target relation */ - rel = get_rel_from_relname(PG_GETARG_TEXT_P(0), AccessShareLock, ACL_SELECT); + rel = get_rel_from_relname(PG_GETARG_TEXT_PP(0), AccessShareLock, ACL_SELECT); /* get the array of attnums */ results = get_pkey_attnames(rel, &numatts); @@ -1609,7 +1615,7 @@ PG_FUNCTION_INFO_V1(dblink_build_sql_insert); Datum dblink_build_sql_insert(PG_FUNCTION_ARGS) { - text *relname_text = PG_GETARG_TEXT_P(0); + text *relname_text = PG_GETARG_TEXT_PP(0); int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1); int32 pknumatts_arg = PG_GETARG_INT32(2); ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); @@ -1700,7 +1706,7 @@ PG_FUNCTION_INFO_V1(dblink_build_sql_delete); Datum dblink_build_sql_delete(PG_FUNCTION_ARGS) { - text *relname_text = PG_GETARG_TEXT_P(0); + text *relname_text = PG_GETARG_TEXT_PP(0); int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1); int32 pknumatts_arg = PG_GETARG_INT32(2); ArrayType *tgt_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); @@ -1777,7 +1783,7 @@ PG_FUNCTION_INFO_V1(dblink_build_sql_update); Datum dblink_build_sql_update(PG_FUNCTION_ARGS) { - text *relname_text = PG_GETARG_TEXT_P(0); + text *relname_text = PG_GETARG_TEXT_PP(0); int2vector *pkattnums_arg = (int2vector *) PG_GETARG_POINTER(1); int32 pknumatts_arg = PG_GETARG_INT32(2); ArrayType *src_pkattvals_arry = PG_GETARG_ARRAYTYPE_P(3); @@ -1876,9 +1882,7 @@ PG_FUNCTION_INFO_V1(dblink_get_notify); Datum dblink_get_notify(PG_FUNCTION_ARGS) { - char *conname = NULL; - PGconn *conn = NULL; - remoteConn *rconn = NULL; + PGconn *conn; PGnotify *notify; ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; @@ -1888,9 +1892,9 @@ dblink_get_notify(PG_FUNCTION_ARGS) prepTuplestoreResult(fcinfo); - DBLINK_INIT; + dblink_init(); if (PG_NARGS() == 1) - DBLINK_GET_NAMED_CONN; + conn = dblink_get_named_conn(text_to_cstring(PG_GETARG_TEXT_PP(0))); else conn = pconn->conn; @@ -2346,7 +2350,7 @@ quote_ident_cstr(char *rawstr) char *result; rawstr_text = cstring_to_text(rawstr); - result_text = DatumGetTextP(DirectFunctionCall1(quote_ident, + result_text = DatumGetTextPP(DirectFunctionCall1(quote_ident, PointerGetDatum(rawstr_text))); result = text_to_cstring(result_text); @@ -2664,7 +2668,8 @@ dblink_connstr_check(const char *connstr) } static void -dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_msg, bool fail) +dblink_res_error(PGconn *conn, const char *conname, PGresult *res, + const char *dblink_context_msg, bool fail) { int level; char *pg_diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); @@ -2693,10 +2698,18 @@ dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_ else sqlstate = ERRCODE_CONNECTION_FAILURE; - xpstrdup(message_primary, pg_diag_message_primary); - xpstrdup(message_detail, pg_diag_message_detail); - xpstrdup(message_hint, pg_diag_message_hint); - xpstrdup(message_context, pg_diag_context); + message_primary = xpstrdup(pg_diag_message_primary); + message_detail = xpstrdup(pg_diag_message_detail); + message_hint = xpstrdup(pg_diag_message_hint); + message_context = xpstrdup(pg_diag_context); + + /* + * If we don't get a message from the PGresult, try the PGconn. This is + * needed because for connection-level failures, PQexec may just return + * NULL, not a PGresult at all. + */ + if (message_primary == NULL) + message_primary = pchomp(PQerrorMessage(conn)); if (res) PQclear(res); @@ -2707,7 +2720,7 @@ dblink_res_error(const char *conname, PGresult *res, const char *dblink_context_ ereport(level, (errcode(sqlstate), message_primary ? errmsg_internal("%s", message_primary) : - errmsg("unknown error"), + errmsg("could not obtain message string for remote error"), message_detail ? errdetail_internal("%s", message_detail) : 0, message_hint ? errhint("%s", message_hint) : 0, message_context ? errcontext("%s", message_context) : 0, @@ -2724,11 +2737,32 @@ get_connect_string(const char *servername) ForeignServer *foreign_server = NULL; UserMapping *user_mapping; ListCell *cell; - StringInfo buf = makeStringInfo(); + StringInfoData buf; ForeignDataWrapper *fdw; AclResult aclresult; char *srvname; + static const PQconninfoOption *options = NULL; + + initStringInfo(&buf); + + /* + * Get list of valid libpq options. + * + * To avoid unnecessary work, we get the list once and use it throughout + * the lifetime of this backend process. We don't need to care about + * memory context issues, because PQconndefaults allocates with malloc. + */ + if (!options) + { + options = PQconndefaults(); + if (!options) /* assume reason for failure is OOM */ + ereport(ERROR, + (errcode(ERRCODE_FDW_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("could not get libpq's default connection options"))); + } + /* first gather the server connstr options */ srvname = pstrdup(servername); truncate_identifier(srvname, strlen(srvname), false); @@ -2752,16 +2786,18 @@ get_connect_string(const char *servername) { DefElem *def = lfirst(cell); - appendStringInfo(buf, "%s='%s' ", def->defname, - escape_param_str(strVal(def->arg))); + if (is_valid_dblink_option(options, def->defname, ForeignDataWrapperRelationId)) + appendStringInfo(&buf, "%s='%s' ", def->defname, + escape_param_str(strVal(def->arg))); } foreach(cell, foreign_server->options) { DefElem *def = lfirst(cell); - appendStringInfo(buf, "%s='%s' ", def->defname, - escape_param_str(strVal(def->arg))); + if (is_valid_dblink_option(options, def->defname, ForeignServerRelationId)) + appendStringInfo(&buf, "%s='%s' ", def->defname, + escape_param_str(strVal(def->arg))); } foreach(cell, user_mapping->options) @@ -2769,11 +2805,12 @@ get_connect_string(const char *servername) DefElem *def = lfirst(cell); - appendStringInfo(buf, "%s='%s' ", def->defname, - escape_param_str(strVal(def->arg))); + if (is_valid_dblink_option(options, def->defname, UserMappingRelationId)) + appendStringInfo(&buf, "%s='%s' ", def->defname, + escape_param_str(strVal(def->arg))); } - return buf->data; + return buf.data; } else return NULL; @@ -2788,16 +2825,18 @@ static char * escape_param_str(const char *str) { const char *cp; - StringInfo buf = makeStringInfo(); + StringInfoData buf; + + initStringInfo(&buf); for (cp = str; *cp; cp++) { if (*cp == '\\' || *cp == '\'') - appendStringInfoChar(buf, '\\'); - appendStringInfoChar(buf, *cp); + appendStringInfoChar(&buf, '\\'); + appendStringInfoChar(&buf, *cp); } - return buf->data; + return buf.data; } /* diff --git a/contrib/dblink/dblink.h b/contrib/dblink/dblink.h deleted file mode 100644 index 1b94912152..0000000000 --- a/contrib/dblink/dblink.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * dblink.h - * - * Functions returning results from a remote database - * - * Joe Conway <mail@joeconway.com> - * And contributors: - * Darko Prenosil <Darko.Prenosil@finteh.hr> - * Shridhar Daithankar <shridhar_daithankar@persistent.co.in> - * - * contrib/dblink/dblink.h - * Copyright (c) 2001-2016, PostgreSQL Global Development Group - * ALL RIGHTS RESERVED; - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE AUTHOR OR DISTRIBUTORS HAVE BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - */ - -#ifndef DBLINK_H -#define DBLINK_H - -#include "fmgr.h" - -/* - * External declarations - */ -extern Datum dblink_connect(PG_FUNCTION_ARGS); -extern Datum dblink_disconnect(PG_FUNCTION_ARGS); -extern Datum dblink_open(PG_FUNCTION_ARGS); -extern Datum dblink_close(PG_FUNCTION_ARGS); -extern Datum dblink_fetch(PG_FUNCTION_ARGS); -extern Datum dblink_record(PG_FUNCTION_ARGS); -extern Datum dblink_send_query(PG_FUNCTION_ARGS); -extern Datum dblink_get_result(PG_FUNCTION_ARGS); -extern Datum dblink_get_connections(PG_FUNCTION_ARGS); -extern Datum dblink_is_busy(PG_FUNCTION_ARGS); -extern Datum dblink_cancel_query(PG_FUNCTION_ARGS); -extern Datum dblink_error_message(PG_FUNCTION_ARGS); -extern Datum dblink_exec(PG_FUNCTION_ARGS); -extern Datum dblink_get_pkey(PG_FUNCTION_ARGS); -extern Datum dblink_build_sql_insert(PG_FUNCTION_ARGS); -extern Datum dblink_build_sql_delete(PG_FUNCTION_ARGS); -extern Datum dblink_build_sql_update(PG_FUNCTION_ARGS); -extern Datum dblink_current_query(PG_FUNCTION_ARGS); -extern Datum dblink_get_notify(PG_FUNCTION_ARGS); -extern Datum dblink_fdw_validator(PG_FUNCTION_ARGS); - -#endif /* DBLINK_H */ diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out index 5acaaf225a..4b6d26e574 100644 --- a/contrib/dblink/expected/dblink.out +++ b/contrib/dblink/expected/dblink.out @@ -377,7 +377,6 @@ FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]) WHERE t.a > 7; ERROR: could not establish connection DETAIL: missing "=" after "myconn" in connection info string - -- create a named persistent connection SELECT dblink_connect('myconn',connection_parameters()); dblink_connect @@ -604,7 +603,6 @@ FROM dblink('myconn','SELECT * FROM foo') AS t(a int, b text, c text[]) WHERE t.a > 7; ERROR: could not establish connection DETAIL: missing "=" after "myconn" in connection info string - -- create a named persistent connection SELECT dblink_connect('myconn',connection_parameters()); dblink_connect diff --git a/contrib/dict_int/dict_int.c b/contrib/dict_int/dict_int.c index 935dbd4c82..55427c4bc7 100644 --- a/contrib/dict_int/dict_int.c +++ b/contrib/dict_int/dict_int.c @@ -3,7 +3,7 @@ * dict_int.c * Text search dictionary for integers * - * Copyright (c) 2007-2016, PostgreSQL Global Development Group + * Copyright (c) 2007-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/dict_int/dict_int.c diff --git a/contrib/dict_xsyn/dict_xsyn.c b/contrib/dict_xsyn/dict_xsyn.c index 63b0383ccc..fcf541ee0f 100644 --- a/contrib/dict_xsyn/dict_xsyn.c +++ b/contrib/dict_xsyn/dict_xsyn.c @@ -3,7 +3,7 @@ * dict_xsyn.c * Extended synonym dictionary * - * Copyright (c) 2007-2016, PostgreSQL Global Development Group + * Copyright (c) 2007-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/dict_xsyn/dict_xsyn.c diff --git a/contrib/earthdistance/earthdistance--1.1.sql b/contrib/earthdistance/earthdistance--1.1.sql index 657d328ebb..9136a54a7b 100644 --- a/contrib/earthdistance/earthdistance--1.1.sql +++ b/contrib/earthdistance/earthdistance--1.1.sql @@ -11,7 +11,7 @@ CREATE FUNCTION earth() RETURNS float8 LANGUAGE SQL IMMUTABLE PARALLEL SAFE AS 'SELECT ''6378168''::float8'; --- Astromers may want to change the earth function so that distances will be +-- Astronomers may want to change the earth function so that distances will be -- returned in degrees. To do this comment out the above definition and -- uncomment the one below. Note that doing this will break the regression -- tests. diff --git a/contrib/earthdistance/earthdistance.c b/contrib/earthdistance/earthdistance.c index 861b166373..6ad6d87ce8 100644 --- a/contrib/earthdistance/earthdistance.c +++ b/contrib/earthdistance/earthdistance.c @@ -88,16 +88,8 @@ geo_distance_internal(Point *pt1, Point *pt2) * * returns: float8 * distance between the points in miles on earth's surface - * - * If float8 is passed-by-value, the oldstyle version-0 calling convention - * is unportable, so we use version-1. However, if it's passed-by-reference, - * continue to use oldstyle. This is just because we'd like earthdistance - * to serve as a canary for any unintentional breakage of version-0 functions - * with float8 results. ******************************************************/ -#ifdef USE_FLOAT8_BYVAL - PG_FUNCTION_INFO_V1(geo_distance); Datum @@ -110,17 +102,3 @@ geo_distance(PG_FUNCTION_ARGS) result = geo_distance_internal(pt1, pt2); PG_RETURN_FLOAT8(result); } -#else /* !USE_FLOAT8_BYVAL */ - -double *geo_distance(Point *pt1, Point *pt2); - -double * -geo_distance(Point *pt1, Point *pt2) -{ - double *resultp = palloc(sizeof(double)); - - *resultp = geo_distance_internal(pt1, pt2); - return resultp; -} - -#endif /* USE_FLOAT8_BYVAL */ diff --git a/contrib/earthdistance/expected/earthdistance.out b/contrib/earthdistance/expected/earthdistance.out index e9daa8488e..89022491cb 100644 --- a/contrib/earthdistance/expected/earthdistance.out +++ b/contrib/earthdistance/expected/earthdistance.out @@ -1049,10 +1049,10 @@ HINT: Use DROP ... CASCADE to drop the dependent objects too. drop extension cube cascade; NOTICE: drop cascades to table foo column f1 \d foo - Table "public.foo" - Column | Type | Modifiers ---------+---------+----------- - f2 | integer | + Table "public.foo" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + f2 | integer | | | -- list what's installed \dT public.* diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index c0491318c0..277639f6e9 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -1,9 +1,9 @@ /*------------------------------------------------------------------------- * * file_fdw.c - * foreign-data wrapper for server-side flat files. + * foreign-data wrapper for server-side flat files (or programs). * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/file_fdw/file_fdw.c @@ -57,8 +57,9 @@ struct FileFdwOption * fileGetOptions(), which currently doesn't bother to look at user mappings. */ static const struct FileFdwOption valid_options[] = { - /* File options */ + /* Data source options */ {"filename", ForeignTableRelationId}, + {"program", ForeignTableRelationId}, /* Format options */ /* oids option is not supported */ @@ -85,10 +86,12 @@ static const struct FileFdwOption valid_options[] = { */ typedef struct FileFdwPlanState { - char *filename; /* file to read */ - List *options; /* merged COPY options, excluding filename */ + char *filename; /* file or program to read from */ + bool is_program; /* true if filename represents an OS command */ + List *options; /* merged COPY options, excluding filename and + * is_program */ BlockNumber pages; /* estimate of file's physical size */ - double ntuples; /* estimate of number of rows in file */ + double ntuples; /* estimate of number of data rows */ } FileFdwPlanState; /* @@ -96,9 +99,11 @@ typedef struct FileFdwPlanState */ typedef struct FileFdwExecutionState { - char *filename; /* file to read */ - List *options; /* merged COPY options, excluding filename */ - CopyState cstate; /* state of reading file */ + char *filename; /* file or program to read from */ + bool is_program; /* true if filename represents an OS command */ + List *options; /* merged COPY options, excluding filename and + * is_program */ + CopyState cstate; /* COPY execution state */ } FileFdwExecutionState; /* @@ -139,7 +144,9 @@ static bool fileIsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel, */ static bool is_valid_option(const char *option, Oid context); static void fileGetOptions(Oid foreigntableid, - char **filename, List **other_options); + char **filename, + bool *is_program, + List **other_options); static List *get_file_fdw_attribute_options(Oid relid); static bool check_selective_binary_conversion(RelOptInfo *baserel, Oid foreigntableid, @@ -196,16 +203,16 @@ file_fdw_validator(PG_FUNCTION_ARGS) /* * Only superusers are allowed to set options of a file_fdw foreign table. - * This is because the filename is one of those options, and we don't want - * non-superusers to be able to determine which file gets read. + * This is because we don't want non-superusers to be able to control + * which file gets read or which program gets executed. * * Putting this sort of permissions check in a validator is a bit of a * crock, but there doesn't seem to be any other place that can enforce * the check more cleanly. * - * Note that the valid_options[] array disallows setting filename at any - * options level other than foreign table --- otherwise there'd still be a - * security hole. + * Note that the valid_options[] array disallows setting filename and + * program at any options level other than foreign table --- otherwise + * there'd still be a security hole. */ if (catalog == ForeignTableRelationId && !superuser()) ereport(ERROR, @@ -247,11 +254,11 @@ file_fdw_validator(PG_FUNCTION_ARGS) } /* - * Separate out filename and column-specific options, since + * Separate out filename, program, and column-specific options, since * ProcessCopyOptions won't accept them. */ - - if (strcmp(def->defname, "filename") == 0) + if (strcmp(def->defname, "filename") == 0 || + strcmp(def->defname, "program") == 0) { if (filename) ereport(ERROR, @@ -293,15 +300,16 @@ file_fdw_validator(PG_FUNCTION_ARGS) /* * Now apply the core COPY code's validation logic for more checks. */ - ProcessCopyOptions(NULL, true, other_options); + ProcessCopyOptions(NULL, NULL, true, other_options); /* - * Filename option is required for file_fdw foreign tables. + * Either filename or program option is required for file_fdw foreign + * tables. */ if (catalog == ForeignTableRelationId && filename == NULL) ereport(ERROR, (errcode(ERRCODE_FDW_DYNAMIC_PARAMETER_VALUE_NEEDED), - errmsg("filename is required for file_fdw foreign tables"))); + errmsg("either filename or program is required for file_fdw foreign tables"))); PG_RETURN_VOID(); } @@ -326,12 +334,12 @@ is_valid_option(const char *option, Oid context) /* * Fetch the options for a file_fdw foreign table. * - * We have to separate out "filename" from the other options because - * it must not appear in the options list passed to the core COPY code. + * We have to separate out filename/program from the other options because + * those must not appear in the options list passed to the core COPY code. */ static void fileGetOptions(Oid foreigntableid, - char **filename, List **other_options) + char **filename, bool *is_program, List **other_options) { ForeignTable *table; ForeignServer *server; @@ -359,9 +367,11 @@ fileGetOptions(Oid foreigntableid, options = list_concat(options, get_file_fdw_attribute_options(foreigntableid)); /* - * Separate out the filename. + * Separate out the filename or program option (we assume there is only + * one). */ *filename = NULL; + *is_program = false; prev = NULL; foreach(lc, options) { @@ -373,15 +383,22 @@ fileGetOptions(Oid foreigntableid, options = list_delete_cell(options, lc, prev); break; } + else if (strcmp(def->defname, "program") == 0) + { + *filename = defGetString(def); + *is_program = true; + options = list_delete_cell(options, lc, prev); + break; + } prev = lc; } /* - * The validator should have checked that a filename was included in the - * options, but check again, just in case. + * The validator should have checked that filename or program was included + * in the options, but check again, just in case. */ if (*filename == NULL) - elog(ERROR, "filename is required for file_fdw foreign tables"); + elog(ERROR, "either filename or program is required for file_fdw foreign tables"); *other_options = options; } @@ -455,10 +472,10 @@ get_file_fdw_attribute_options(Oid relid) * force_null options set */ if (fnncolumns != NIL) - options = lappend(options, makeDefElem("force_not_null", (Node *) fnncolumns)); + options = lappend(options, makeDefElem("force_not_null", (Node *) fnncolumns, -1)); if (fncolumns != NIL) - options = lappend(options, makeDefElem("force_null", (Node *) fncolumns)); + options = lappend(options, makeDefElem("force_null", (Node *) fncolumns, -1)); return options; } @@ -475,12 +492,15 @@ fileGetForeignRelSize(PlannerInfo *root, FileFdwPlanState *fdw_private; /* - * Fetch options. We only need filename at this point, but we might as - * well get everything and not need to re-fetch it later in planning. + * Fetch options. We only need filename (or program) at this point, but + * we might as well get everything and not need to re-fetch it later in + * planning. */ fdw_private = (FileFdwPlanState *) palloc(sizeof(FileFdwPlanState)); fileGetOptions(foreigntableid, - &fdw_private->filename, &fdw_private->options); + &fdw_private->filename, + &fdw_private->is_program, + &fdw_private->options); baserel->fdw_private = (void *) fdw_private; /* Estimate relation size */ @@ -511,7 +531,7 @@ fileGetForeignPaths(PlannerInfo *root, foreigntableid, &columns)) coptions = list_make1(makeDefElem("convert_selectively", - (Node *) columns)); + (Node *) columns, -1)); /* Estimate costs */ estimate_costs(root, baserel, fdw_private, @@ -583,20 +603,25 @@ static void fileExplainForeignScan(ForeignScanState *node, ExplainState *es) { char *filename; + bool is_program; List *options; - /* Fetch options --- we only need filename at this point */ + /* Fetch options --- we only need filename and is_program at this point */ fileGetOptions(RelationGetRelid(node->ss.ss_currentRelation), - &filename, &options); + &filename, &is_program, &options); - ExplainPropertyText("Foreign File", filename, es); + if (is_program) + ExplainPropertyText("Foreign Program", filename, es); + else + ExplainPropertyText("Foreign File", filename, es); /* Suppress file size if we're not showing cost details */ if (es->costs) { struct stat stat_buf; - if (stat(filename, &stat_buf) == 0) + if (!is_program && + stat(filename, &stat_buf) == 0) ExplainPropertyLong("Foreign File Size", (long) stat_buf.st_size, es); } @@ -611,6 +636,7 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) { ForeignScan *plan = (ForeignScan *) node->ss.ps.plan; char *filename; + bool is_program; List *options; CopyState cstate; FileFdwExecutionState *festate; @@ -623,7 +649,7 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) /* Fetch options of foreign table */ fileGetOptions(RelationGetRelid(node->ss.ss_currentRelation), - &filename, &options); + &filename, &is_program, &options); /* Add any options from the plan (currently only convert_selectively) */ options = list_concat(options, plan->fdw_private); @@ -632,9 +658,11 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) * Create CopyState from FDW options. We always acquire all columns, so * as to match the expected ScanTupleSlot signature. */ - cstate = BeginCopyFrom(node->ss.ss_currentRelation, + cstate = BeginCopyFrom(NULL, + node->ss.ss_currentRelation, filename, - false, + is_program, + NULL, NIL, options); @@ -644,6 +672,7 @@ fileBeginForeignScan(ForeignScanState *node, int eflags) */ festate = (FileFdwExecutionState *) palloc(sizeof(FileFdwExecutionState)); festate->filename = filename; + festate->is_program = is_program; festate->options = options; festate->cstate = cstate; @@ -705,9 +734,11 @@ fileReScanForeignScan(ForeignScanState *node) EndCopyFrom(festate->cstate); - festate->cstate = BeginCopyFrom(node->ss.ss_currentRelation, + festate->cstate = BeginCopyFrom(NULL, + node->ss.ss_currentRelation, festate->filename, - false, + festate->is_program, + NULL, NIL, festate->options); } @@ -736,11 +767,22 @@ fileAnalyzeForeignTable(Relation relation, BlockNumber *totalpages) { char *filename; + bool is_program; List *options; struct stat stat_buf; /* Fetch options of foreign table */ - fileGetOptions(RelationGetRelid(relation), &filename, &options); + fileGetOptions(RelationGetRelid(relation), &filename, &is_program, &options); + + /* + * If this is a program instead of a file, just return false to skip + * analyzing the table. We could run the program and collect stats on + * whatever it currently returns, but it seems likely that in such cases + * the output would be too volatile for the stats to be useful. Maybe + * there should be an option to enable doing this? + */ + if (is_program) + return false; /* * Get size of the file. (XXX if we fail here, would it be better to just @@ -767,8 +809,8 @@ fileAnalyzeForeignTable(Relation relation, /* * fileIsForeignScanParallelSafe - * Reading a file in a parallel worker should work just the same as - * reading it in the leader, so mark scans safe. + * Reading a file, or external program, in a parallel worker should work + * just the same as reading it in the leader, so mark scans safe. */ static bool fileIsForeignScanParallelSafe(PlannerInfo *root, RelOptInfo *rel, @@ -914,9 +956,10 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel, /* * Get size of the file. It might not be there at plan time, though, in - * which case we have to use a default estimate. + * which case we have to use a default estimate. We also have to fall + * back to the default if using a program as the input. */ - if (stat(fdw_private->filename, &stat_buf) < 0) + if (fdw_private->is_program || stat(fdw_private->filename, &stat_buf) < 0) stat_buf.st_size = 10 * BLCKSZ; /* @@ -998,6 +1041,11 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel, * that I/O costs are equivalent to a regular table file of the same size. * However, we take per-tuple CPU costs as 10x of a seqscan, to account * for the cost of parsing records. + * + * In the case of a program source, this calculation is even more divorced + * from reality, but we have no good alternative; and it's not clear that + * the numbers we produce here matter much anyway, since there's only one + * access path for the rel. */ run_cost += seq_page_cost * pages; @@ -1034,6 +1082,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, bool *nulls; bool found; char *filename; + bool is_program; List *options; CopyState cstate; ErrorContextCallback errcallback; @@ -1048,12 +1097,13 @@ file_acquire_sample_rows(Relation onerel, int elevel, nulls = (bool *) palloc(tupDesc->natts * sizeof(bool)); /* Fetch options of foreign table */ - fileGetOptions(RelationGetRelid(onerel), &filename, &options); + fileGetOptions(RelationGetRelid(onerel), &filename, &is_program, &options); /* * Create CopyState from FDW options. */ - cstate = BeginCopyFrom(onerel, filename, false, NIL, options); + cstate = BeginCopyFrom(NULL, onerel, filename, is_program, NULL, NIL, + options); /* * Use per-tuple memory context to prevent leak of memory used to read @@ -1061,9 +1111,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, */ tupcontext = AllocSetContextCreate(CurrentMemoryContext, "file_fdw temporary context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); /* Prepare for sampling rows */ reservoir_init_selection_state(&rstate, targrows); diff --git a/contrib/file_fdw/output/file_fdw.source b/contrib/file_fdw/output/file_fdw.source index 6fa54409b9..01e2690a82 100644 --- a/contrib/file_fdw/output/file_fdw.source +++ b/contrib/file_fdw/output/file_fdw.source @@ -76,7 +76,7 @@ CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (format 'csv', null ' '); -- ERROR ERROR: COPY null representation cannot use newline or carriage return CREATE FOREIGN TABLE tbl () SERVER file_server; -- ERROR -ERROR: filename is required for file_fdw foreign tables +ERROR: either filename or program is required for file_fdw foreign tables CREATE FOREIGN TABLE agg_text ( a int2 CHECK (a >= 0), b float4 @@ -132,7 +132,7 @@ ERROR: invalid option "force_not_null" HINT: There are no valid options in this context. CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (force_not_null '*'); -- ERROR ERROR: invalid option "force_not_null" -HINT: Valid options in this context are: filename, format, header, delimiter, quote, escape, null, encoding +HINT: Valid options in this context are: filename, program, format, header, delimiter, quote, escape, null, encoding -- force_null is not allowed to be specified at any foreign object level: ALTER FOREIGN DATA WRAPPER file_fdw OPTIONS (ADD force_null '*'); -- ERROR ERROR: invalid option "force_null" @@ -145,7 +145,7 @@ ERROR: invalid option "force_null" HINT: There are no valid options in this context. CREATE FOREIGN TABLE tbl () SERVER file_server OPTIONS (force_null '*'); -- ERROR ERROR: invalid option "force_null" -HINT: Valid options in this context are: filename, format, header, delimiter, quote, escape, null, encoding +HINT: Valid options in this context are: filename, program, format, header, delimiter, quote, escape, null, encoding -- basic query tests SELECT * FROM agg_text WHERE b > 10.0 ORDER BY a; a | b diff --git a/contrib/fuzzystrmatch/dmetaphone.c b/contrib/fuzzystrmatch/dmetaphone.c index 147c8501ee..4e89983afd 100644 --- a/contrib/fuzzystrmatch/dmetaphone.c +++ b/contrib/fuzzystrmatch/dmetaphone.c @@ -93,9 +93,6 @@ The remaining code is authored by Andrew Dunstan <amdunstan@ncshp.org> and ***********************************************************************/ - - - /* include these first, according to the docs */ #ifndef DMETAPHONE_MAIN @@ -105,14 +102,19 @@ The remaining code is authored by Andrew Dunstan <amdunstan@ncshp.org> and /* turn off assertions for embedded function */ #define NDEBUG -#endif +#else /* DMETAPHONE_MAIN */ + +/* we need these if we didn't get them from postgres.h */ #include <stdio.h> -#include <ctype.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> + +#endif /* DMETAPHONE_MAIN */ + #include <assert.h> +#include <ctype.h> /* prototype for the main function we got from the perl module */ static void DoubleMetaphone(char *, char **); @@ -137,7 +139,7 @@ dmetaphone(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) PG_RETURN_NULL(); #endif - arg = PG_GETARG_TEXT_P(0); + arg = PG_GETARG_TEXT_PP(0); aptr = text_to_cstring(arg); DoubleMetaphone(aptr, codes); @@ -166,7 +168,7 @@ dmetaphone_alt(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) PG_RETURN_NULL(); #endif - arg = PG_GETARG_TEXT_P(0); + arg = PG_GETARG_TEXT_PP(0); aptr = text_to_cstring(arg); DoubleMetaphone(aptr, codes); diff --git a/contrib/fuzzystrmatch/fuzzystrmatch.c b/contrib/fuzzystrmatch/fuzzystrmatch.c index cbac1f2381..f1bb7bca73 100644 --- a/contrib/fuzzystrmatch/fuzzystrmatch.c +++ b/contrib/fuzzystrmatch/fuzzystrmatch.c @@ -6,7 +6,7 @@ * Joe Conway <mail@joeconway.com> * * contrib/fuzzystrmatch/fuzzystrmatch.c - * Copyright (c) 2001-2016, PostgreSQL Global Development Group + * Copyright (c) 2001-2017, PostgreSQL Global Development Group * ALL RIGHTS RESERVED; * * metaphone() @@ -42,6 +42,7 @@ #include "mb/pg_wchar.h" #include "utils/builtins.h" +#include "utils/varlena.h" PG_MODULE_MAGIC; @@ -735,7 +736,7 @@ soundex(PG_FUNCTION_ARGS) char outstr[SOUNDEX_LEN + 1]; char *arg; - arg = text_to_cstring(PG_GETARG_TEXT_P(0)); + arg = text_to_cstring(PG_GETARG_TEXT_PP(0)); _soundex(arg, outstr); @@ -801,8 +802,8 @@ difference(PG_FUNCTION_ARGS) int i, result; - _soundex(text_to_cstring(PG_GETARG_TEXT_P(0)), sndx1); - _soundex(text_to_cstring(PG_GETARG_TEXT_P(1)), sndx2); + _soundex(text_to_cstring(PG_GETARG_TEXT_PP(0)), sndx1); + _soundex(text_to_cstring(PG_GETARG_TEXT_PP(1)), sndx2); result = 0; for (i = 0; i < SOUNDEX_LEN; i++) diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out index 5cba644d1c..b43da92815 100644 --- a/contrib/hstore/expected/hstore.out +++ b/contrib/hstore/expected/hstore.out @@ -1,4 +1,12 @@ CREATE EXTENSION hstore; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + set escape_string_warning=off; --hstore; select ''::hstore; diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c index 3a408a2070..191fc48c7a 100644 --- a/contrib/hstore/hstore_io.c +++ b/contrib/hstore/hstore_io.c @@ -611,19 +611,22 @@ hstore_from_arrays(PG_FUNCTION_ARGS) if (!value_nulls || value_nulls[i]) { - pairs[i].key = VARDATA_ANY(key_datums[i]); + pairs[i].key = VARDATA(key_datums[i]); pairs[i].val = NULL; - pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); + pairs[i].keylen = + hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ); pairs[i].vallen = 4; pairs[i].isnull = true; pairs[i].needfree = false; } else { - pairs[i].key = VARDATA_ANY(key_datums[i]); - pairs[i].val = VARDATA_ANY(value_datums[i]); - pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); - pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i])); + pairs[i].key = VARDATA(key_datums[i]); + pairs[i].val = VARDATA(value_datums[i]); + pairs[i].keylen = + hstoreCheckKeyLen(VARSIZE(key_datums[i]) - VARHDRSZ); + pairs[i].vallen = + hstoreCheckValLen(VARSIZE(value_datums[i]) - VARHDRSZ); pairs[i].isnull = false; pairs[i].needfree = false; } @@ -704,19 +707,22 @@ hstore_from_array(PG_FUNCTION_ARGS) if (in_nulls[i * 2 + 1]) { - pairs[i].key = VARDATA_ANY(in_datums[i * 2]); + pairs[i].key = VARDATA(in_datums[i * 2]); pairs[i].val = NULL; - pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); + pairs[i].keylen = + hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ); pairs[i].vallen = 4; pairs[i].isnull = true; pairs[i].needfree = false; } else { - pairs[i].key = VARDATA_ANY(in_datums[i * 2]); - pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]); - pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); - pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1])); + pairs[i].key = VARDATA(in_datums[i * 2]); + pairs[i].val = VARDATA(in_datums[i * 2 + 1]); + pairs[i].keylen = + hstoreCheckKeyLen(VARSIZE(in_datums[i * 2]) - VARHDRSZ); + pairs[i].vallen = + hstoreCheckValLen(VARSIZE(in_datums[i * 2 + 1]) - VARHDRSZ); pairs[i].isnull = false; pairs[i].needfree = false; } diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql index 8d88fb332b..57f9494325 100644 --- a/contrib/hstore/sql/hstore.sql +++ b/contrib/hstore/sql/hstore.sql @@ -1,5 +1,10 @@ CREATE EXTENSION hstore; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + set escape_string_warning=off; --hstore; diff --git a/contrib/hstore_plperl/Makefile b/contrib/hstore_plperl/Makefile index b3b8654bc8..34e1e137f7 100644 --- a/contrib/hstore_plperl/Makefile +++ b/contrib/hstore_plperl/Makefile @@ -23,20 +23,15 @@ include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif -# In configurations that forbid undefined symbols in libraries, link with each -# dependency. This does preclude pgxs builds. -ifeq ($(PORTNAME), aix) -rpathdir = $(pkglibdir):$(perl_archlibexp)/CORE -SHLIB_LINK += ../hstore/libhstore.exp $(perl_embed_ldflags) -endif +# We must link libperl explicitly ifeq ($(PORTNAME), win32) # these settings are the same as for plperl override CPPFLAGS += -DPLPERL_HAVE_UID_GID -Wno-comment -SHLIB_LINK += ../hstore/libhstore.a $(sort $(wildcard ../../src/pl/plperl/libperl*.a)) -endif - -ifeq ($(PORTNAME), cygwin) -SHLIB_LINK += -L../hstore -l hstore $(perl_embed_ldflags) +# ... see silliness in plperl Makefile ... +SHLIB_LINK += $(sort $(wildcard ../../src/pl/plperl/libperl*.a)) +else +rpathdir = $(perl_archlibexp)/CORE +SHLIB_LINK += $(perl_embed_ldflags) endif # As with plperl we need to make sure that the CORE directory is included diff --git a/contrib/hstore_plperl/expected/create_transform.out b/contrib/hstore_plperl/expected/create_transform.out index c588d33ab8..02dc62af0d 100644 --- a/contrib/hstore_plperl/expected/create_transform.out +++ b/contrib/hstore_plperl/expected/create_transform.out @@ -18,9 +18,9 @@ ERROR: type "foo" does not exist CREATE TRANSFORM FOR hstore LANGUAGE foo (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail ERROR: language "foo" does not exist CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_out(hstore), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail -ERROR: return data type of FROM SQL function must be "internal" +ERROR: return data type of FROM SQL function must be internal CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION internal_in(cstring), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail -ERROR: first argument of transform function must be type "internal" +ERROR: first argument of transform function must be type internal CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail ERROR: transform for type hstore language "plperl" already exists diff --git a/contrib/hstore_plperl/expected/hstore_plperlu.out b/contrib/hstore_plperl/expected/hstore_plperlu.out index b09fb78af9..d719d29f72 100644 --- a/contrib/hstore_plperl/expected/hstore_plperlu.out +++ b/contrib/hstore_plperl/expected/hstore_plperlu.out @@ -20,15 +20,12 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0])); return scalar(keys %{$_[0]}); $$; SELECT test1('aa=>bb, cc=>NULL'::hstore); -INFO: $VAR1 = { - 'aa' => 'bb', - 'cc' => undef - }; - +INFO: $VAR1 = {'aa' => 'bb','cc' => undef}; test1 ------- 2 @@ -39,12 +36,12 @@ LANGUAGE plperlu AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0])); return scalar(keys %{$_[0]}); $$; SELECT test1none('aa=>bb, cc=>NULL'::hstore); INFO: $VAR1 = '"aa"=>"bb", "cc"=>NULL'; - test1none ----------- 0 @@ -56,15 +53,12 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0])); return scalar(keys %{$_[0]}); $$; SELECT test1list('aa=>bb, cc=>NULL'::hstore); -INFO: $VAR1 = { - 'aa' => 'bb', - 'cc' => undef - }; - +INFO: $VAR1 = {'aa' => 'bb','cc' => undef}; test1list ----------- 2 @@ -77,18 +71,12 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0]->[0], $_[0]->[1])); return scalar(keys %{$_[0]}); $$; SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); -INFO: $VAR1 = { - 'aa' => 'bb', - 'cc' => undef - }; -$VAR2 = { - 'dd' => 'ee' - }; - +INFO: $VAR1 = {'aa' => 'bb','cc' => undef};$VAR2 = {'dd' => 'ee'}; test1arr ---------- 2 @@ -101,6 +89,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; $rv = spi_exec_query(q{SELECT 'aa=>bb, cc=>NULL'::hstore AS col1}); elog(INFO, Dumper($rv->{rows}[0]->{col1})); @@ -111,13 +100,8 @@ $rv = spi_exec_prepared($plan, {}, $val); elog(INFO, Dumper($rv->{rows}[0]->{col1})); $$; SELECT test3(); -INFO: $VAR1 = { - 'aa' => 'bb', - 'cc' => undef - }; - +INFO: $VAR1 = {'aa' => 'bb','cc' => undef}; INFO: $VAR1 = '"a"=>"1", "b"=>"boo", "c"=>NULL'; - test3 ------- @@ -138,6 +122,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_TD->{new})); if ($_TD->{new}{a} == 1) { $_TD->{new}{b} = {a => 1, b => 'boo', c => undef}; @@ -147,14 +132,7 @@ return "MODIFY"; $$; CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); UPDATE test1 SET a = a; -INFO: $VAR1 = { - 'a' => '1', - 'b' => { - 'aa' => 'bb', - 'cc' => undef - } - }; - +INFO: $VAR1 = {'a' => '1','b' => {'aa' => 'bb','cc' => undef}}; SELECT * FROM test1; a | b ---+--------------------------------- diff --git a/contrib/hstore_plperl/hstore_plperl--1.0.sql b/contrib/hstore_plperl/hstore_plperl--1.0.sql index 9a64fcb18b..af743c8733 100644 --- a/contrib/hstore_plperl/hstore_plperl--1.0.sql +++ b/contrib/hstore_plperl/hstore_plperl--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION hstore_plperl" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plperl'; -SELECT NULL::hstore; - - CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME'; diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c index d40a792730..480212f341 100644 --- a/contrib/hstore_plperl/hstore_plperl.c +++ b/contrib/hstore_plperl/hstore_plperl.c @@ -1,5 +1,7 @@ #include "postgres.h" + #undef _ + #include "fmgr.h" #include "plperl.h" #include "plperl_helpers.h" @@ -7,6 +9,58 @@ PG_MODULE_MAGIC; +extern void _PG_init(void); + +/* Linkage to functions in hstore module */ +typedef HStore *(*hstoreUpgrade_t) (Datum orig); +static hstoreUpgrade_t hstoreUpgrade_p; +typedef int (*hstoreUniquePairs_t) (Pairs *a, int32 l, int32 *buflen); +static hstoreUniquePairs_t hstoreUniquePairs_p; +typedef HStore *(*hstorePairs_t) (Pairs *pairs, int32 pcount, int32 buflen); +static hstorePairs_t hstorePairs_p; +typedef size_t (*hstoreCheckKeyLen_t) (size_t len); +static hstoreCheckKeyLen_t hstoreCheckKeyLen_p; +typedef size_t (*hstoreCheckValLen_t) (size_t len); +static hstoreCheckValLen_t hstoreCheckValLen_p; + + +/* + * Module initialize function: fetch function pointers for cross-module calls. + */ +void +_PG_init(void) +{ + /* Asserts verify that typedefs above match original declarations */ + AssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t); + hstoreUpgrade_p = (hstoreUpgrade_t) + load_external_function("$libdir/hstore", "hstoreUpgrade", + true, NULL); + AssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t); + hstoreUniquePairs_p = (hstoreUniquePairs_t) + load_external_function("$libdir/hstore", "hstoreUniquePairs", + true, NULL); + AssertVariableIsOfType(&hstorePairs, hstorePairs_t); + hstorePairs_p = (hstorePairs_t) + load_external_function("$libdir/hstore", "hstorePairs", + true, NULL); + AssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t); + hstoreCheckKeyLen_p = (hstoreCheckKeyLen_t) + load_external_function("$libdir/hstore", "hstoreCheckKeyLen", + true, NULL); + AssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t); + hstoreCheckValLen_p = (hstoreCheckValLen_t) + load_external_function("$libdir/hstore", "hstoreCheckValLen", + true, NULL); +} + + +/* These defines must be after the module init function */ +#define hstoreUpgrade hstoreUpgrade_p +#define hstoreUniquePairs hstoreUniquePairs_p +#define hstorePairs hstorePairs_p +#define hstoreCheckKeyLen hstoreCheckKeyLen_p +#define hstoreCheckValLen hstoreCheckValLen_p + PG_FUNCTION_INFO_V1(hstore_to_plperl); diff --git a/contrib/hstore_plperl/hstore_plperlu--1.0.sql b/contrib/hstore_plperl/hstore_plperlu--1.0.sql index f355284907..7c3bc86eba 100644 --- a/contrib/hstore_plperl/hstore_plperlu--1.0.sql +++ b/contrib/hstore_plperl/hstore_plperlu--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION hstore_plperlu" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plperl'; -SELECT NULL::hstore; - - CREATE FUNCTION hstore_to_plperlu(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME', 'hstore_to_plperl'; diff --git a/contrib/hstore_plperl/sql/hstore_plperlu.sql b/contrib/hstore_plperl/sql/hstore_plperlu.sql index 8d8508cf29..c714b35322 100644 --- a/contrib/hstore_plperl/sql/hstore_plperlu.sql +++ b/contrib/hstore_plperl/sql/hstore_plperlu.sql @@ -15,6 +15,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0])); return scalar(keys %{$_[0]}); $$; @@ -26,6 +27,7 @@ LANGUAGE plperlu AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0])); return scalar(keys %{$_[0]}); $$; @@ -38,6 +40,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0])); return scalar(keys %{$_[0]}); $$; @@ -52,6 +55,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_[0]->[0], $_[0]->[1])); return scalar(keys %{$_[0]}); $$; @@ -66,6 +70,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; $rv = spi_exec_query(q{SELECT 'aa=>bb, cc=>NULL'::hstore AS col1}); elog(INFO, Dumper($rv->{rows}[0]->{col1})); @@ -90,6 +95,7 @@ TRANSFORM FOR TYPE hstore AS $$ use Data::Dumper; $Data::Dumper::Sortkeys = 1; +$Data::Dumper::Indent = 0; elog(INFO, Dumper($_TD->{new})); if ($_TD->{new}{a} == 1) { $_TD->{new}{b} = {a => 1, b => 'boo', c => undef}; diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile index c4dad6f111..7ff787a22e 100644 --- a/contrib/hstore_plpython/Makefile +++ b/contrib/hstore_plpython/Makefile @@ -4,7 +4,7 @@ MODULE_big = hstore_plpython$(python_majorversion) OBJS = hstore_plpython.o $(WIN32RES) PGFILEDESC = "hstore_plpython - hstore transform for plpython" -PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/hstore +PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/hstore -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"' EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql @@ -23,19 +23,13 @@ include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif -# In configurations that forbid undefined symbols in libraries, link with each -# dependency. This does preclude pgxs builds. -ifeq ($(PORTNAME), aix) -rpathdir = $(pkglibdir):$(python_libdir) -SHLIB_LINK += ../hstore/libhstore.exp $(python_libspec) $(python_additional_libs) $(sort $(wildcard ../../src/pl/plpython/libplpython*.exp)) -endif +# We must link libpython explicitly ifeq ($(PORTNAME), win32) -SHLIB_LINK += ../hstore/libhstore.a $(sort $(wildcard ../../src/pl/plpython/libpython*.a)) $(sort $(wildcard ../../src/pl/plpython/libplpython*.a)) -endif - -ifeq ($(PORTNAME), cygwin) -SHLIB_LINK += -L../hstore -lhstore -L../../src/pl/plpython \ - -lplpython$(python_majorversion) $(python_libspec) +# ... see silliness in plpython Makefile ... +SHLIB_LINK += $(sort $(wildcard ../../src/pl/plpython/libpython*.a)) +else +rpathdir = $(python_libdir) +SHLIB_LINK += $(python_libspec) $(python_additional_libs) endif REGRESS_OPTS += --load-extension=hstore diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out index b0025c04a8..df49cd5f37 100644 --- a/contrib/hstore_plpython/expected/hstore_plpython.out +++ b/contrib/hstore_plpython/expected/hstore_plpython.out @@ -6,9 +6,7 @@ LANGUAGE plpythonu TRANSFORM FOR TYPE hstore AS $$ assert isinstance(val, dict) -i = list(val.items()) -i.sort() -plpy.info(i) +plpy.info(sorted(val.items())) return len(val) $$; SELECT test1('aa=>bb, cc=>NULL'::hstore); @@ -24,9 +22,7 @@ LANGUAGE plpython2u TRANSFORM FOR TYPE hstore AS $$ assert isinstance(val, dict) -i = list(val.items()) -i.sort() -plpy.info(i) +plpy.info(sorted(val.items())) return len(val) $$; SELECT test1n('aa=>bb, cc=>NULL'::hstore); diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c index 6f2751a8df..b184324ebf 100644 --- a/contrib/hstore_plpython/hstore_plpython.c +++ b/contrib/hstore_plpython/hstore_plpython.c @@ -1,4 +1,5 @@ #include "postgres.h" + #include "fmgr.h" #include "plpython.h" #include "plpy_typeio.h" @@ -6,6 +7,78 @@ PG_MODULE_MAGIC; +extern void _PG_init(void); + +/* Linkage to functions in plpython module */ +typedef char *(*PLyObject_AsString_t) (PyObject *plrv); +static PLyObject_AsString_t PLyObject_AsString_p; +#if PY_MAJOR_VERSION >= 3 +typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size); +static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p; +#endif + +/* Linkage to functions in hstore module */ +typedef HStore *(*hstoreUpgrade_t) (Datum orig); +static hstoreUpgrade_t hstoreUpgrade_p; +typedef int (*hstoreUniquePairs_t) (Pairs *a, int32 l, int32 *buflen); +static hstoreUniquePairs_t hstoreUniquePairs_p; +typedef HStore *(*hstorePairs_t) (Pairs *pairs, int32 pcount, int32 buflen); +static hstorePairs_t hstorePairs_p; +typedef size_t (*hstoreCheckKeyLen_t) (size_t len); +static hstoreCheckKeyLen_t hstoreCheckKeyLen_p; +typedef size_t (*hstoreCheckValLen_t) (size_t len); +static hstoreCheckValLen_t hstoreCheckValLen_p; + + +/* + * Module initialize function: fetch function pointers for cross-module calls. + */ +void +_PG_init(void) +{ + /* Asserts verify that typedefs above match original declarations */ + AssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t); + PLyObject_AsString_p = (PLyObject_AsString_t) + load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString", + true, NULL); +#if PY_MAJOR_VERSION >= 3 + AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t); + PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t) + load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize", + true, NULL); +#endif + AssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t); + hstoreUpgrade_p = (hstoreUpgrade_t) + load_external_function("$libdir/hstore", "hstoreUpgrade", + true, NULL); + AssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t); + hstoreUniquePairs_p = (hstoreUniquePairs_t) + load_external_function("$libdir/hstore", "hstoreUniquePairs", + true, NULL); + AssertVariableIsOfType(&hstorePairs, hstorePairs_t); + hstorePairs_p = (hstorePairs_t) + load_external_function("$libdir/hstore", "hstorePairs", + true, NULL); + AssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t); + hstoreCheckKeyLen_p = (hstoreCheckKeyLen_t) + load_external_function("$libdir/hstore", "hstoreCheckKeyLen", + true, NULL); + AssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t); + hstoreCheckValLen_p = (hstoreCheckValLen_t) + load_external_function("$libdir/hstore", "hstoreCheckValLen", + true, NULL); +} + + +/* These defines must be after the module init function */ +#define PLyObject_AsString PLyObject_AsString_p +#define PLyUnicode_FromStringAndSize PLyUnicode_FromStringAndSize_p +#define hstoreUpgrade hstoreUpgrade_p +#define hstoreUniquePairs hstoreUniquePairs_p +#define hstorePairs hstorePairs_p +#define hstoreCheckKeyLen hstoreCheckKeyLen_p +#define hstoreCheckValLen hstoreCheckValLen_p + PG_FUNCTION_INFO_V1(hstore_to_plpython); diff --git a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql index e3aea6399e..800765f3f0 100644 --- a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql +++ b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION hstore_plpython2u" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plpython2'; -SELECT NULL::hstore; - - CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME', 'hstore_to_plpython'; diff --git a/contrib/hstore_plpython/hstore_plpython3u--1.0.sql b/contrib/hstore_plpython/hstore_plpython3u--1.0.sql index a964a49059..0b410ab183 100644 --- a/contrib/hstore_plpython/hstore_plpython3u--1.0.sql +++ b/contrib/hstore_plpython/hstore_plpython3u--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION hstore_plpython3u" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plpython3'; -SELECT NULL::hstore; - - CREATE FUNCTION hstore_to_plpython3(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME', 'hstore_to_plpython'; diff --git a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql index d79bdc96d9..52832912ab 100644 --- a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql +++ b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION hstore_plpythonu" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plpython2'; -- change to plpython3 if that ever becomes the default -SELECT NULL::hstore; - - CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME'; diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql index d55bedaf50..911bbd67fe 100644 --- a/contrib/hstore_plpython/sql/hstore_plpython.sql +++ b/contrib/hstore_plpython/sql/hstore_plpython.sql @@ -7,9 +7,7 @@ LANGUAGE plpythonu TRANSFORM FOR TYPE hstore AS $$ assert isinstance(val, dict) -i = list(val.items()) -i.sort() -plpy.info(i) +plpy.info(sorted(val.items())) return len(val) $$; @@ -22,9 +20,7 @@ LANGUAGE plpython2u TRANSFORM FOR TYPE hstore AS $$ assert isinstance(val, dict) -i = list(val.items()) -i.sort() -plpy.info(i) +plpy.info(sorted(val.items())) return len(val) $$; diff --git a/contrib/intarray/_int_op.c b/contrib/intarray/_int_op.c index 537174175b..c30d3c8269 100644 --- a/contrib/intarray/_int_op.c +++ b/contrib/intarray/_int_op.c @@ -200,9 +200,9 @@ Datum sort(PG_FUNCTION_ARGS) { ArrayType *a = PG_GETARG_ARRAYTYPE_P_COPY(0); - text *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_P(1) : NULL; - int32 dc = (dirstr) ? VARSIZE(dirstr) - VARHDRSZ : 0; - char *d = (dirstr) ? VARDATA(dirstr) : NULL; + text *dirstr = (fcinfo->nargs == 2) ? PG_GETARG_TEXT_PP(1) : NULL; + int32 dc = (dirstr) ? VARSIZE_ANY_EXHDR(dirstr) : 0; + char *d = (dirstr) ? VARDATA_ANY(dirstr) : NULL; int dir = -1; CHECKARRVALID(a); diff --git a/contrib/intarray/_int_selfuncs.c b/contrib/intarray/_int_selfuncs.c index 3dad7eeb7d..3d92025ba5 100644 --- a/contrib/intarray/_int_selfuncs.c +++ b/contrib/intarray/_int_selfuncs.c @@ -3,7 +3,7 @@ * _int_selfuncs.c * Functions for selectivity estimation of intarray operators * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -19,6 +19,7 @@ #include "catalog/pg_operator.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" +#include "utils/builtins.h" #include "utils/selfuncs.h" #include "utils/syscache.h" #include "utils/lsyscache.h" @@ -32,14 +33,6 @@ PG_FUNCTION_INFO_V1(_int_contains_joinsel); PG_FUNCTION_INFO_V1(_int_contained_joinsel); PG_FUNCTION_INFO_V1(_int_matchsel); -Datum _int_overlap_sel(PG_FUNCTION_ARGS); -Datum _int_contains_sel(PG_FUNCTION_ARGS); -Datum _int_contained_sel(PG_FUNCTION_ARGS); -Datum _int_overlap_joinsel(PG_FUNCTION_ARGS); -Datum _int_contains_joinsel(PG_FUNCTION_ARGS); -Datum _int_contained_joinsel(PG_FUNCTION_ARGS); -Datum _int_matchsel(PG_FUNCTION_ARGS); - static Selectivity int_query_opr_selec(ITEM *item, Datum *values, float4 *freqs, int nmncelems, float4 minfreq); @@ -143,11 +136,7 @@ _int_matchsel(PG_FUNCTION_ARGS) int nmcelems = 0; float4 minfreq = 0.0; float4 nullfrac = 0.0; - Form_pg_statistic stats; - Datum *values = NULL; - int nvalues = 0; - float4 *numbers = NULL; - int nnumbers = 0; + AttStatsSlot sslot; /* * If expression is not "variable @@ something" or "something @@ variable" @@ -200,6 +189,8 @@ _int_matchsel(PG_FUNCTION_ARGS) */ if (HeapTupleIsValid(vardata.statsTuple)) { + Form_pg_statistic stats; + stats = (Form_pg_statistic) GETSTRUCT(vardata.statsTuple); nullfrac = stats->stanullfrac; @@ -207,29 +198,30 @@ _int_matchsel(PG_FUNCTION_ARGS) * For an int4 array, the default array type analyze function will * collect a Most Common Elements list, which is an array of int4s. */ - if (get_attstatsslot(vardata.statsTuple, - INT4OID, -1, + if (get_attstatsslot(&sslot, vardata.statsTuple, STATISTIC_KIND_MCELEM, InvalidOid, - NULL, - &values, &nvalues, - &numbers, &nnumbers)) + ATTSTATSSLOT_VALUES | ATTSTATSSLOT_NUMBERS)) { + Assert(sslot.valuetype == INT4OID); + /* * There should be three more Numbers than Values, because the * last three (for intarray) cells are taken for minimal, maximal * and nulls frequency. Punt if not. */ - if (nnumbers == nvalues + 3) + if (sslot.nnumbers == sslot.nvalues + 3) { /* Grab the lowest frequency. */ - minfreq = numbers[nnumbers - (nnumbers - nvalues)]; + minfreq = sslot.numbers[sslot.nnumbers - (sslot.nnumbers - sslot.nvalues)]; - mcelems = values; - mcefreqs = numbers; - nmcelems = nvalues; + mcelems = sslot.values; + mcefreqs = sslot.numbers; + nmcelems = sslot.nvalues; } } } + else + memset(&sslot, 0, sizeof(sslot)); /* Process the logical expression in the query, using the stats */ selec = int_query_opr_selec(GETQUERY(query) + query->size - 1, @@ -238,7 +230,7 @@ _int_matchsel(PG_FUNCTION_ARGS) /* MCE stats count only non-null rows, so adjust for null rows. */ selec *= (1.0 - nullfrac); - free_attstatsslot(INT4OID, values, nvalues, numbers, nnumbers); + free_attstatsslot(&sslot); ReleaseVariableStats(vardata); CLAMP_PROBABILITY(selec); diff --git a/contrib/intarray/bench/bench.pl b/contrib/intarray/bench/bench.pl index 8746291114..92035d6c06 100755 --- a/contrib/intarray/bench/bench.pl +++ b/contrib/intarray/bench/bench.pl @@ -92,7 +92,9 @@ if ($opt{v}) if ($opt{e}) { - $dbi->do("explain $sql"); + my @plan = + map { "$_->[0]\n" } @{ $dbi->selectall_arrayref("explain $sql") }; + print @plan; } my $t0 = [gettimeofday]; diff --git a/contrib/intarray/bench/create_test.pl b/contrib/intarray/bench/create_test.pl index 1323b31e4d..f3262df05b 100755 --- a/contrib/intarray/bench/create_test.pl +++ b/contrib/intarray/bench/create_test.pl @@ -15,8 +15,8 @@ create table message_section_map ( EOT -open(MSG, ">message.tmp") || die; -open(MAP, ">message_section_map.tmp") || die; +open(my $msg, '>', "message.tmp") || die; +open(my $map, '>', "message_section_map.tmp") || die; srand(1); @@ -42,16 +42,16 @@ foreach my $i (1 .. 200000) } if ($#sect < 0 || rand() < 0.1) { - print MSG "$i\t\\N\n"; + print $msg "$i\t\\N\n"; } else { - print MSG "$i\t{" . join(',', @sect) . "}\n"; - map { print MAP "$i\t$_\n" } @sect; + print $msg "$i\t{" . join(',', @sect) . "}\n"; + map { print $map "$i\t$_\n" } @sect; } } -close MAP; -close MSG; +close $map; +close $msg; copytable('message'); copytable('message_section_map'); @@ -79,8 +79,8 @@ sub copytable my $t = shift; print "COPY $t from stdin;\n"; - open(FFF, "$t.tmp") || die; - while (<FFF>) { print; } - close FFF; + open(my $fff, '<', "$t.tmp") || die; + while (<$fff>) { print; } + close $fff; print "\\.\n"; } diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out index 962e5c6a4b..0a5dd463ac 100644 --- a/contrib/intarray/expected/_int.out +++ b/contrib/intarray/expected/_int.out @@ -1,4 +1,12 @@ CREATE EXTENSION intarray; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + SELECT intset(1234); intset -------- diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql index f6fe2de55c..44e1a729b4 100644 --- a/contrib/intarray/sql/_int.sql +++ b/contrib/intarray/sql/_int.sql @@ -1,5 +1,10 @@ CREATE EXTENSION intarray; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + SELECT intset(1234); SELECT icount('{1234234,234234}'); SELECT sort('{1234234,-30,234234}'); diff --git a/contrib/isn/ISSN.h b/contrib/isn/ISSN.h index 082efcff7c..585f0e2674 100644 --- a/contrib/isn/ISSN.h +++ b/contrib/isn/ISSN.h @@ -23,7 +23,7 @@ * Product 9 + 21 + 7 + 3 + 1 + 12 + 4 + 24 + 7 + 15 + 0 + 0 = 103 * 103 / 10 = 10 remainder 3 * Check digit 10 - 3 = 7 - * => 977-1144875-00-7 ?? <- suplemental number (number of the week, month, etc.) + * => 977-1144875-00-7 ?? <- supplemental number (number of the week, month, etc.) * ^^ 00 for non-daily publications (01=Monday, 02=Tuesday, ...) * * The hyphenation is always in after the four digits of the ISSN code. diff --git a/contrib/isn/expected/isn.out b/contrib/isn/expected/isn.out index 140bc86656..ef9d3a61e7 100644 --- a/contrib/isn/expected/isn.out +++ b/contrib/isn/expected/isn.out @@ -2,6 +2,50 @@ -- Test ISN extension -- CREATE EXTENSION isn; +-- Check whether any of our opclasses fail amvalidate +-- ... they will, because of missing cross-type operators +SELECT amname, opcname +FROM (SELECT amname, opcname, opc.oid + FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod + WHERE opc.oid >= 16384 + ORDER BY 1, 2 OFFSET 0) ss +WHERE NOT amvalidate(oid); +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: btree operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) +INFO: hash operator family "isn_ops" is missing cross-type operator(s) + amname | opcname +--------+------------ + btree | ean13_ops + btree | isbn13_ops + btree | isbn_ops + btree | ismn13_ops + btree | ismn_ops + btree | issn13_ops + btree | issn_ops + btree | upc_ops + hash | ean13_ops + hash | isbn13_ops + hash | isbn_ops + hash | ismn13_ops + hash | ismn_ops + hash | issn13_ops + hash | issn_ops + hash | upc_ops +(16 rows) + -- -- test valid conversions -- diff --git a/contrib/isn/isn.c b/contrib/isn/isn.c index c622a4ef07..c3c10e14bc 100644 --- a/contrib/isn/isn.c +++ b/contrib/isn/isn.c @@ -4,7 +4,7 @@ * PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC) * * Author: German Mendez Bravo (Kronuz) - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/isn/isn.c @@ -160,7 +160,7 @@ dehyphenate(char *bufO, char *bufI) * into bufO using the given hyphenation range TABLE. * Assumes the input string to be used is of only digits. * - * Returns the number of characters acctually hyphenated. + * Returns the number of characters actually hyphenated. */ static unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2]) @@ -748,7 +748,7 @@ string2ean(const char *str, bool errorOK, ean13 *result, } else if (*aux2 == '!' && *(aux2 + 1) == '\0') { - /* the invalid check digit sufix was found, set it */ + /* the invalid check digit suffix was found, set it */ if (!magic) valid = false; magic = true; diff --git a/contrib/isn/isn.h b/contrib/isn/isn.h index d8291c2b06..09dc7c6575 100644 --- a/contrib/isn/isn.h +++ b/contrib/isn/isn.h @@ -4,7 +4,7 @@ * PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC) * * Author: German Mendez Bravo (Kronuz) - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/isn/isn.h @@ -30,25 +30,6 @@ typedef uint64 ean13; #define PG_GETARG_EAN13(n) PG_GETARG_INT64(n) #define PG_RETURN_EAN13(x) PG_RETURN_INT64(x) -extern Datum isn_out(PG_FUNCTION_ARGS); -extern Datum ean13_out(PG_FUNCTION_ARGS); -extern Datum ean13_in(PG_FUNCTION_ARGS); -extern Datum isbn_in(PG_FUNCTION_ARGS); -extern Datum ismn_in(PG_FUNCTION_ARGS); -extern Datum issn_in(PG_FUNCTION_ARGS); -extern Datum upc_in(PG_FUNCTION_ARGS); - -extern Datum isbn_cast_from_ean13(PG_FUNCTION_ARGS); -extern Datum ismn_cast_from_ean13(PG_FUNCTION_ARGS); -extern Datum issn_cast_from_ean13(PG_FUNCTION_ARGS); -extern Datum upc_cast_from_ean13(PG_FUNCTION_ARGS); - -extern Datum is_valid(PG_FUNCTION_ARGS); -extern Datum make_valid(PG_FUNCTION_ARGS); - -extern Datum accept_weak_input(PG_FUNCTION_ARGS); -extern Datum weak_input_status(PG_FUNCTION_ARGS); - extern void initialize(void); #endif /* ISN_H */ diff --git a/contrib/isn/sql/isn.sql b/contrib/isn/sql/isn.sql index 5ef6d8aa3b..71577d5590 100644 --- a/contrib/isn/sql/isn.sql +++ b/contrib/isn/sql/isn.sql @@ -4,6 +4,15 @@ CREATE EXTENSION isn; +-- Check whether any of our opclasses fail amvalidate +-- ... they will, because of missing cross-type operators +SELECT amname, opcname +FROM (SELECT amname, opcname, opc.oid + FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod + WHERE opc.oid >= 16384 + ORDER BY 1, 2 OFFSET 0) ss +WHERE NOT amvalidate(oid); + -- -- test valid conversions -- diff --git a/contrib/lo/lo.c b/contrib/lo/lo.c index 953659305f..6bd2430931 100644 --- a/contrib/lo/lo.c +++ b/contrib/lo/lo.c @@ -9,13 +9,11 @@ #include "commands/trigger.h" #include "executor/spi.h" -#include "libpq/be-fsstubs.h" +#include "utils/builtins.h" #include "utils/rel.h" PG_MODULE_MAGIC; -#define atooid(x) ((Oid) strtoul((x), NULL, 10)) - /* * This is the trigger that protects us from orphaned large objects @@ -82,7 +80,7 @@ lo_manage(PG_FUNCTION_ARGS) char *newv = SPI_getvalue(newtuple, tupdesc, attnum); if (orig != NULL && (newv == NULL || strcmp(orig, newv) != 0)) - DirectFunctionCall1(lo_unlink, + DirectFunctionCall1(be_lo_unlink, ObjectIdGetDatum(atooid(orig))); if (newv) @@ -102,7 +100,7 @@ lo_manage(PG_FUNCTION_ARGS) if (orig != NULL) { - DirectFunctionCall1(lo_unlink, + DirectFunctionCall1(be_lo_unlink, ObjectIdGetDatum(atooid(orig))); pfree(orig); diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c index 403dae0d7d..447e4b2960 100644 --- a/contrib/ltree/crc32.c +++ b/contrib/ltree/crc32.c @@ -9,10 +9,6 @@ #include "postgres.h" -#include <sys/types.h> -#include <stdio.h> -#include <sys/types.h> - #ifdef LOWER_NODE #include <ctype.h> #define TOLOWER(x) tolower((unsigned char) (x)) diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 17690406b6..197718e950 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -1,4 +1,12 @@ CREATE EXTENSION ltree; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + SELECT ''::ltree; ltree ------- @@ -1105,7 +1113,7 @@ SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e,*.df}'; t (1 row) ---exractors +--extractors SELECT ('{3456,1.2.3.34}'::ltree[] ?@> '1.2.3.4') is null; ?column? ---------- diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c index befda1344d..9ca1994249 100644 --- a/contrib/ltree/ltxtquery_io.c +++ b/contrib/ltree/ltxtquery_io.c @@ -96,7 +96,7 @@ gettoken_query(QPRS_STATE *state, int32 *val, int32 *lenval, char **strval, uint if (*flag) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("modificators syntax error"))); + errmsg("modifiers syntax error"))); *lenval += charlen; } else if (charlen == 1 && t_iseq(state->buf, '%')) @@ -197,7 +197,7 @@ pushval_asis(QPRS_STATE *state, int type, char *strval, int lenval, uint16 flag) #define STACKDEPTH 32 /* - * make polish notaion of query + * make polish notation of query */ static int32 makepol(QPRS_STATE *state) diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 46cfa41a41..e9f74909a6 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -1,5 +1,10 @@ CREATE EXTENSION ltree; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + SELECT ''::ltree; SELECT '1'::ltree; SELECT '1.2'::ltree; @@ -204,7 +209,7 @@ SELECT 'a.b.c.d.e'::ltree ? '{A.b.c.d.e, a.*}'; SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e}'; SELECT '{a.b.c.d.e,B.df}'::ltree[] ? '{A.b.c.d.e,*.df}'; ---exractors +--extractors SELECT ('{3456,1.2.3.34}'::ltree[] ?@> '1.2.3.4') is null; SELECT '{3456,1.2.3}'::ltree[] ?@> '1.2.3.4'; SELECT '{3456,1.2.3.4}'::ltree[] ?<@ '1.2.3'; diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile index 08186f19a1..bc7502b8c3 100644 --- a/contrib/ltree_plpython/Makefile +++ b/contrib/ltree_plpython/Makefile @@ -4,7 +4,7 @@ MODULE_big = ltree_plpython$(python_majorversion) OBJS = ltree_plpython.o $(WIN32RES) PGFILEDESC = "ltree_plpython - ltree transform for plpython" -PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/ltree +PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/ltree -DPLPYTHON_LIBNAME='"plpython$(python_majorversion)"' EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql @@ -23,19 +23,13 @@ include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mk endif -# In configurations that forbid undefined symbols in libraries, link with each -# dependency. This does preclude pgxs builds. -ifeq ($(PORTNAME), aix) -rpathdir = $(pkglibdir):$(python_libdir) -SHLIB_LINK += $(python_libspec) $(python_additional_libs) $(sort $(wildcard ../../src/pl/plpython/libplpython*.exp)) -endif +# We must link libpython explicitly ifeq ($(PORTNAME), win32) -SHLIB_LINK += $(sort $(wildcard ../../src/pl/plpython/libpython*.a)) $(sort $(wildcard ../../src/pl/plpython/libplpython*.a)) -endif - -ifeq ($(PORTNAME), cygwin) -SHLIB_LINK += -L../ltree -lltree -L../../src/pl/plpython \ - -lplpython$(python_majorversion) $(python_libspec) +# ... see silliness in plpython Makefile ... +SHLIB_LINK += $(sort $(wildcard ../../src/pl/plpython/libpython*.a)) +else +rpathdir = $(python_libdir) +SHLIB_LINK += $(python_libspec) $(python_additional_libs) endif REGRESS_OPTS += --load-extension=ltree diff --git a/contrib/ltree_plpython/ltree_plpython.c b/contrib/ltree_plpython/ltree_plpython.c index 26b7b3c275..bdd462a91b 100644 --- a/contrib/ltree_plpython/ltree_plpython.c +++ b/contrib/ltree_plpython/ltree_plpython.c @@ -1,10 +1,39 @@ #include "postgres.h" + #include "fmgr.h" #include "plpython.h" #include "ltree.h" PG_MODULE_MAGIC; +extern void _PG_init(void); + +/* Linkage to functions in plpython module */ +#if PY_MAJOR_VERSION >= 3 +typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size); +static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p; +#endif + + +/* + * Module initialize function: fetch function pointers for cross-module calls. + */ +void +_PG_init(void) +{ + /* Asserts verify that typedefs above match original declarations */ +#if PY_MAJOR_VERSION >= 3 + AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t); + PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t) + load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize", + true, NULL); +#endif +} + + +/* These defines must be after the module init function */ +#define PLyUnicode_FromStringAndSize PLyUnicode_FromStringAndSize_p + PG_FUNCTION_INFO_V1(ltree_to_plpython); diff --git a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql index 62531371bf..5c4a703701 100644 --- a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql +++ b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION ltree_plpython2u" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plpython2'; -SELECT NULL::ltree; - - CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME', 'ltree_to_plpython'; diff --git a/contrib/ltree_plpython/ltree_plpython3u--1.0.sql b/contrib/ltree_plpython/ltree_plpython3u--1.0.sql index 3f21d1b721..09ada3c7e8 100644 --- a/contrib/ltree_plpython/ltree_plpython3u--1.0.sql +++ b/contrib/ltree_plpython/ltree_plpython3u--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION ltree_plpython3u" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plpython3'; -SELECT NULL::ltree; - - CREATE FUNCTION ltree_to_plpython3(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME', 'ltree_to_plpython'; diff --git a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql index e8deadc62d..ee93edf28b 100644 --- a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql +++ b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql @@ -3,11 +3,6 @@ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION ltree_plpythonu" to load this file. \quit --- make sure the prerequisite libraries are loaded -LOAD 'plpython2'; -- change to plpython3 if that ever becomes the default -SELECT NULL::ltree; - - CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal LANGUAGE C STRICT IMMUTABLE AS 'MODULE_PATHNAME'; diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c index e5eeec21c1..aab71aed2f 100644 --- a/contrib/oid2name/oid2name.c +++ b/contrib/oid2name/oid2name.c @@ -9,6 +9,8 @@ */ #include "postgres_fe.h" +#include "catalog/pg_class.h" + #include "libpq-fe.h" #include "pg_getopt.h" @@ -261,7 +263,8 @@ PGconn * sql_conn(struct options * my_opts) { PGconn *conn; - char *password = NULL; + bool have_password = false; + char password[100]; bool new_pass; /* @@ -282,7 +285,7 @@ sql_conn(struct options * my_opts) keywords[2] = "user"; values[2] = my_opts->username; keywords[3] = "password"; - values[3] = password; + values[3] = have_password ? password : NULL; keywords[4] = "dbname"; values[4] = my_opts->dbname; keywords[5] = "fallback_application_name"; @@ -302,17 +305,15 @@ sql_conn(struct options * my_opts) if (PQstatus(conn) == CONNECTION_BAD && PQconnectionNeedsPassword(conn) && - password == NULL) + !have_password) { PQfinish(conn); - password = simple_prompt("Password: ", 100, false); + simple_prompt("Password: ", password, sizeof(password), false); + have_password = true; new_pass = true; } } while (new_pass); - if (password) - free(password); - /* check to see that the backend connection was successfully made */ if (PQstatus(conn) == CONNECTION_BAD) { @@ -434,11 +435,12 @@ sql_exec_dumpalltables(PGconn *conn, struct options * opts) snprintf(todo, sizeof(todo), "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s " - "FROM pg_class c " + "FROM pg_catalog.pg_class c " " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database()," " pg_catalog.pg_tablespace t " - "WHERE relkind IN ('r', 'm'%s%s) AND " + "WHERE relkind IN (" CppAsString2(RELKIND_RELATION) "," + CppAsString2(RELKIND_MATVIEW) "%s%s) AND " " %s" " t.oid = CASE" " WHEN reltablespace <> 0 THEN reltablespace" @@ -446,8 +448,8 @@ sql_exec_dumpalltables(PGconn *conn, struct options * opts) " END " "ORDER BY relname", opts->extended ? addfields : "", - opts->indexes ? ", 'i', 'S'" : "", - opts->systables ? ", 't'" : "", + opts->indexes ? "," CppAsString2(RELKIND_INDEX) "," CppAsString2(RELKIND_SEQUENCE) : "", + opts->systables ? "," CppAsString2(RELKIND_TOASTVALUE) : "", opts->systables ? "" : "n.nspname NOT IN ('pg_catalog', 'information_schema') AND n.nspname !~ '^pg_toast' AND"); sql_exec(conn, todo, opts->quiet); @@ -504,16 +506,20 @@ sql_exec_searchtables(PGconn *conn, struct options * opts) /* now build the query */ todo = psprintf( "SELECT pg_catalog.pg_relation_filenode(c.oid) as \"Filenode\", relname as \"Table Name\" %s\n" - "FROM pg_catalog.pg_class c \n" - " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \n" + "FROM pg_catalog.pg_class c\n" + " LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\n" " LEFT JOIN pg_catalog.pg_database d ON d.datname = pg_catalog.current_database(),\n" - " pg_catalog.pg_tablespace t \n" - "WHERE relkind IN ('r', 'm', 'i', 'S', 't') AND \n" + " pg_catalog.pg_tablespace t\n" + "WHERE relkind IN (" CppAsString2(RELKIND_RELATION) "," + CppAsString2(RELKIND_MATVIEW) "," + CppAsString2(RELKIND_INDEX) "," + CppAsString2(RELKIND_SEQUENCE) "," + CppAsString2(RELKIND_TOASTVALUE) ") AND\n" " t.oid = CASE\n" " WHEN reltablespace <> 0 THEN reltablespace\n" " ELSE dattablespace\n" - " END AND \n" - " (%s) \n" + " END AND\n" + " (%s)\n" "ORDER BY relname\n", opts->extended ? addfields : "", qualifiers); diff --git a/contrib/pageinspect/.gitignore b/contrib/pageinspect/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/contrib/pageinspect/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index a98237ecbd..0a3cbeeb10 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -2,15 +2,17 @@ MODULE_big = pageinspect OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o \ - brinfuncs.o ginfuncs.o $(WIN32RES) + brinfuncs.o ginfuncs.o hashfuncs.o $(WIN32RES) EXTENSION = pageinspect -DATA = pageinspect--1.5.sql pageinspect--1.4--1.5.sql \ - pageinspect--1.3--1.4.sql pageinspect--1.2--1.3.sql \ - pageinspect--1.1--1.2.sql pageinspect--1.0--1.1.sql \ - pageinspect--unpackaged--1.0.sql +DATA = pageinspect--1.5.sql pageinspect--1.5--1.6.sql \ + pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \ + pageinspect--1.2--1.3.sql pageinspect--1.1--1.2.sql \ + pageinspect--1.0--1.1.sql pageinspect--unpackaged--1.0.sql PGFILEDESC = "pageinspect - functions to inspect contents of database pages" +REGRESS = page btree brin gin hash + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c index aa6bd0bc97..d52807dcdd 100644 --- a/contrib/pageinspect/brinfuncs.c +++ b/contrib/pageinspect/brinfuncs.c @@ -2,13 +2,15 @@ * brinfuncs.c * Functions to investigate BRIN indexes * - * Copyright (c) 2014-2016, PostgreSQL Global Development Group + * Copyright (c) 2014-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pageinspect/brinfuncs.c */ #include "postgres.h" +#include "pageinspect.h" + #include "access/htup_details.h" #include "access/brin.h" #include "access/brin_internal.h" @@ -224,7 +226,8 @@ brin_page_items(PG_FUNCTION_ARGS) if (ItemIdIsUsed(itemId)) { dtup = brin_deform_tuple(bdesc, - (BrinTuple *) PageGetItem(page, itemId)); + (BrinTuple *) PageGetItem(page, itemId), + NULL); attno = 1; unusedItem = false; } diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c index 4983bbbbaf..02440eca47 100644 --- a/contrib/pageinspect/btreefuncs.c +++ b/contrib/pageinspect/btreefuncs.c @@ -27,6 +27,8 @@ #include "postgres.h" +#include "pageinspect.h" + #include "access/nbtree.h" #include "catalog/namespace.h" #include "catalog/pg_am.h" @@ -34,10 +36,12 @@ #include "miscadmin.h" #include "utils/builtins.h" #include "utils/rel.h" +#include "utils/varlena.h" PG_FUNCTION_INFO_V1(bt_metap); PG_FUNCTION_INFO_V1(bt_page_items); +PG_FUNCTION_INFO_V1(bt_page_items_bytea); PG_FUNCTION_INFO_V1(bt_page_stats); #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) @@ -156,7 +160,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat) Datum bt_page_stats(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); uint32 blkno = PG_GETARG_UINT32(1); Buffer buffer; Relation rel; @@ -232,14 +236,6 @@ bt_page_stats(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } -/*------------------------------------------------------- - * bt_page_items() - * - * Get IndexTupleData set in a btree page - * - * Usage: SELECT * FROM bt_page_items('t1_pkey', 1); - *------------------------------------------------------- - */ /* * cross-call data structure for SRF @@ -250,14 +246,72 @@ struct user_args OffsetNumber offset; }; +/*------------------------------------------------------- + * bt_page_print_tuples() + * + * Form a tuple describing index tuple at a given offset + * ------------------------------------------------------ + */ +static Datum +bt_page_print_tuples(FuncCallContext *fctx, Page page, OffsetNumber offset) +{ + char *values[6]; + HeapTuple tuple; + ItemId id; + IndexTuple itup; + int j; + int off; + int dlen; + char *dump; + char *ptr; + + id = PageGetItemId(page, offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(page, id); + + j = 0; + values[j++] = psprintf("%d", offset); + values[j++] = psprintf("(%u,%u)", + ItemPointerGetBlockNumberNoCheck(&itup->t_tid), + ItemPointerGetOffsetNumberNoCheck(&itup->t_tid)); + values[j++] = psprintf("%d", (int) IndexTupleSize(itup)); + values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f'); + values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); + + ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); + dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); + dump = palloc0(dlen * 3 + 1); + values[j] = dump; + for (off = 0; off < dlen; off++) + { + if (off > 0) + *dump++ = ' '; + sprintf(dump, "%02x", *(ptr + off) & 0xff); + dump += 2; + } + + tuple = BuildTupleFromCStrings(fctx->attinmeta, values); + + return HeapTupleGetDatum(tuple); +} + +/*------------------------------------------------------- + * bt_page_items() + * + * Get IndexTupleData set in a btree page + * + * Usage: SELECT * FROM bt_page_items('t1_pkey', 1); + *------------------------------------------------------- + */ Datum bt_page_items(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); uint32 blkno = PG_GETARG_UINT32(1); Datum result; - char *values[6]; - HeapTuple tuple; FuncCallContext *fctx; MemoryContext mctx; struct user_args *uargs; @@ -342,47 +396,8 @@ bt_page_items(PG_FUNCTION_ARGS) if (fctx->call_cntr < fctx->max_calls) { - ItemId id; - IndexTuple itup; - int j; - int off; - int dlen; - char *dump; - char *ptr; - - id = PageGetItemId(uargs->page, uargs->offset); - - if (!ItemIdIsValid(id)) - elog(ERROR, "invalid ItemId"); - - itup = (IndexTuple) PageGetItem(uargs->page, id); - - j = 0; - values[j++] = psprintf("%d", uargs->offset); - values[j++] = psprintf("(%u,%u)", - BlockIdGetBlockNumber(&(itup->t_tid.ip_blkid)), - itup->t_tid.ip_posid); - values[j++] = psprintf("%d", (int) IndexTupleSize(itup)); - values[j++] = psprintf("%c", IndexTupleHasNulls(itup) ? 't' : 'f'); - values[j++] = psprintf("%c", IndexTupleHasVarwidths(itup) ? 't' : 'f'); - - ptr = (char *) itup + IndexInfoFindDataOffset(itup->t_info); - dlen = IndexTupleSize(itup) - IndexInfoFindDataOffset(itup->t_info); - dump = palloc0(dlen * 3 + 1); - values[j] = dump; - for (off = 0; off < dlen; off++) - { - if (off > 0) - *dump++ = ' '; - sprintf(dump, "%02x", *(ptr + off) & 0xff); - dump += 2; - } - - tuple = BuildTupleFromCStrings(fctx->attinmeta, values); - result = HeapTupleGetDatum(tuple); - - uargs->offset = uargs->offset + 1; - + result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); + uargs->offset++; SRF_RETURN_NEXT(fctx, result); } else @@ -393,6 +408,90 @@ bt_page_items(PG_FUNCTION_ARGS) } } +/*------------------------------------------------------- + * bt_page_items_bytea() + * + * Get IndexTupleData set in a btree page + * + * Usage: SELECT * FROM bt_page_items(get_raw_page('t1_pkey', 1)); + *------------------------------------------------------- + */ + +Datum +bt_page_items_bytea(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Datum result; + FuncCallContext *fctx; + struct user_args *uargs; + int raw_page_size; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use pageinspect functions")))); + + if (SRF_IS_FIRSTCALL()) + { + BTPageOpaque opaque; + MemoryContext mctx; + TupleDesc tupleDesc; + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + + if (raw_page_size < SizeOfPageHeaderData) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page too small (%d bytes)", raw_page_size))); + + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + uargs = palloc(sizeof(struct user_args)); + + uargs->page = VARDATA(raw_page); + + uargs->offset = FirstOffsetNumber; + + opaque = (BTPageOpaque) PageGetSpecialPointer(uargs->page); + + if (P_ISMETA(opaque)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("block is a meta page"))); + + if (P_ISDELETED(opaque)) + elog(NOTICE, "page is deleted"); + + fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); + + fctx->user_fctx = uargs; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + uargs = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + result = bt_page_print_tuples(fctx, uargs->page, uargs->offset); + uargs->offset++; + SRF_RETURN_NEXT(fctx, result); + } + else + { + pfree(uargs); + SRF_RETURN_DONE(fctx); + } +} + /* ------------------------------------------------ * bt_metap() @@ -405,7 +504,7 @@ bt_page_items(PG_FUNCTION_ARGS) Datum bt_metap(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); Datum result; Relation rel; RangeVar *relrv; diff --git a/contrib/pageinspect/expected/brin.out b/contrib/pageinspect/expected/brin.out new file mode 100644 index 0000000000..71eb190380 --- /dev/null +++ b/contrib/pageinspect/expected/brin.out @@ -0,0 +1,51 @@ +CREATE TABLE test1 (a int, b text); +INSERT INTO test1 VALUES (1, 'one'); +CREATE INDEX test1_a_idx ON test1 USING brin (a); +SELECT brin_page_type(get_raw_page('test1_a_idx', 0)); + brin_page_type +---------------- + meta +(1 row) + +SELECT brin_page_type(get_raw_page('test1_a_idx', 1)); + brin_page_type +---------------- + revmap +(1 row) + +SELECT brin_page_type(get_raw_page('test1_a_idx', 2)); + brin_page_type +---------------- + regular +(1 row) + +SELECT * FROM brin_metapage_info(get_raw_page('test1_a_idx', 0)); + magic | version | pagesperrange | lastrevmappage +------------+---------+---------------+---------------- + 0xA8109CFA | 1 | 128 | 1 +(1 row) + +SELECT * FROM brin_metapage_info(get_raw_page('test1_a_idx', 1)); +ERROR: page is not a BRIN page of type "metapage" +DETAIL: Expected special type 0000f091, got 0000f092. +SELECT * FROM brin_revmap_data(get_raw_page('test1_a_idx', 0)) LIMIT 5; +ERROR: page is not a BRIN page of type "revmap" +DETAIL: Expected special type 0000f092, got 0000f091. +SELECT * FROM brin_revmap_data(get_raw_page('test1_a_idx', 1)) LIMIT 5; + pages +------- + (2,1) + (0,0) + (0,0) + (0,0) + (0,0) +(5 rows) + +SELECT * FROM brin_page_items(get_raw_page('test1_a_idx', 2), 'test1_a_idx') + ORDER BY blknum, attnum LIMIT 5; + itemoffset | blknum | attnum | allnulls | hasnulls | placeholder | value +------------+--------+--------+----------+----------+-------------+---------- + 1 | 0 | 1 | f | f | f | {1 .. 1} +(1 row) + +DROP TABLE test1; diff --git a/contrib/pageinspect/expected/btree.out b/contrib/pageinspect/expected/btree.out new file mode 100644 index 0000000000..67b103add3 --- /dev/null +++ b/contrib/pageinspect/expected/btree.out @@ -0,0 +1,58 @@ +CREATE TABLE test1 (a int8, b text); +INSERT INTO test1 VALUES (72057594037927937, 'text'); +CREATE INDEX test1_a_idx ON test1 USING btree (a); +\x +SELECT * FROM bt_metap('test1_a_idx'); +-[ RECORD 1 ]----- +magic | 340322 +version | 2 +root | 1 +level | 0 +fastroot | 1 +fastlevel | 0 + +SELECT * FROM bt_page_stats('test1_a_idx', 0); +ERROR: block 0 is a meta page +SELECT * FROM bt_page_stats('test1_a_idx', 1); +-[ RECORD 1 ]-+----- +blkno | 1 +type | l +live_items | 1 +dead_items | 0 +avg_item_size | 16 +page_size | 8192 +free_size | 8128 +btpo_prev | 0 +btpo_next | 0 +btpo | 0 +btpo_flags | 3 + +SELECT * FROM bt_page_stats('test1_a_idx', 2); +ERROR: block number out of range +SELECT * FROM bt_page_items('test1_a_idx', 0); +ERROR: block 0 is a meta page +SELECT * FROM bt_page_items('test1_a_idx', 1); +-[ RECORD 1 ]----------------------- +itemoffset | 1 +ctid | (0,1) +itemlen | 16 +nulls | f +vars | f +data | 01 00 00 00 00 00 00 01 + +SELECT * FROM bt_page_items('test1_a_idx', 2); +ERROR: block number out of range +SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0)); +ERROR: block is a meta page +SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1)); +-[ RECORD 1 ]----------------------- +itemoffset | 1 +ctid | (0,1) +itemlen | 16 +nulls | f +vars | f +data | 01 00 00 00 00 00 00 01 + +SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2)); +ERROR: block number 2 is out of range for relation "test1_a_idx" +DROP TABLE test1; diff --git a/contrib/pageinspect/expected/gin.out b/contrib/pageinspect/expected/gin.out new file mode 100644 index 0000000000..82f63b23b1 --- /dev/null +++ b/contrib/pageinspect/expected/gin.out @@ -0,0 +1,37 @@ +CREATE TABLE test1 (x int, y int[]); +INSERT INTO test1 VALUES (1, ARRAY[11, 111]); +CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off); +\x +SELECT * FROM gin_metapage_info(get_raw_page('test1_y_idx', 0)); +-[ RECORD 1 ]----+----------- +pending_head | 4294967295 +pending_tail | 4294967295 +tail_free_size | 0 +n_pending_pages | 0 +n_pending_tuples | 0 +n_total_pages | 2 +n_entry_pages | 1 +n_data_pages | 0 +n_entries | 2 +version | 2 + +SELECT * FROM gin_metapage_info(get_raw_page('test1_y_idx', 1)); +ERROR: input page is not a GIN metapage +DETAIL: Flags 0002, expected 0008 +SELECT * FROM gin_page_opaque_info(get_raw_page('test1_y_idx', 1)); +-[ RECORD 1 ]--------- +rightlink | 4294967295 +maxoff | 0 +flags | {leaf} + +SELECT * FROM gin_leafpage_items(get_raw_page('test1_y_idx', 1)); +ERROR: input page is not a compressed GIN data leaf page +DETAIL: Flags 0002, expected 0083 +INSERT INTO test1 SELECT x, ARRAY[1,10] FROM generate_series(2,10000) x; +SELECT COUNT(*) > 0 +FROM gin_leafpage_items(get_raw_page('test1_y_idx', + (pg_relation_size('test1_y_idx') / + current_setting('block_size')::bigint)::int - 1)); +-[ RECORD 1 ] +?column? | t + diff --git a/contrib/pageinspect/expected/hash.out b/contrib/pageinspect/expected/hash.out new file mode 100644 index 0000000000..39374158b1 --- /dev/null +++ b/contrib/pageinspect/expected/hash.out @@ -0,0 +1,162 @@ +CREATE TABLE test_hash (a int, b text); +INSERT INTO test_hash VALUES (1, 'one'); +CREATE INDEX test_hash_a_idx ON test_hash USING hash (a); +\x +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0)); +-[ RECORD 1 ]--+--------- +hash_page_type | metapage + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 1)); +-[ RECORD 1 ]--+------- +hash_page_type | bucket + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 2)); +-[ RECORD 1 ]--+------- +hash_page_type | bucket + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 3)); +-[ RECORD 1 ]--+------- +hash_page_type | bucket + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 4)); +-[ RECORD 1 ]--+------- +hash_page_type | bucket + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 5)); +-[ RECORD 1 ]--+------- +hash_page_type | bitmap + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 6)); +ERROR: block number 6 is out of range for relation "test_hash_a_idx" +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 0); +ERROR: invalid overflow block number 0 +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 1); +ERROR: invalid overflow block number 1 +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 2); +ERROR: invalid overflow block number 2 +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3); +ERROR: invalid overflow block number 3 +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4); +ERROR: invalid overflow block number 4 +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5); +ERROR: invalid overflow block number 5 +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 0)); +-[ RECORD 1 ]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +magic | 105121344 +version | 3 +ntuples | 1 +bsize | 8152 +bmsize | 4096 +bmshift | 15 +maxbucket | 3 +highmask | 7 +lowmask | 3 +ovflpoint | 2 +firstfree | 0 +nmaps | 1 +procid | 450 +spares | {0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} +mapp | {5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 1)); +ERROR: page is not a hash meta page +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 2)); +ERROR: page is not a hash meta page +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 3)); +ERROR: page is not a hash meta page +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 4)); +ERROR: page is not a hash meta page +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 5)); +ERROR: page is not a hash meta page +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 0)); +ERROR: page is not a hash bucket or overflow page +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 1)); +-[ RECORD 1 ]---+----------- +live_items | 0 +dead_items | 0 +page_size | 8192 +hasho_prevblkno | 3 +hasho_nextblkno | 4294967295 +hasho_bucket | 0 +hasho_flag | 2 +hasho_page_id | 65408 + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 2)); +-[ RECORD 1 ]---+----------- +live_items | 0 +dead_items | 0 +page_size | 8192 +hasho_prevblkno | 3 +hasho_nextblkno | 4294967295 +hasho_bucket | 1 +hasho_flag | 2 +hasho_page_id | 65408 + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 3)); +-[ RECORD 1 ]---+----------- +live_items | 1 +dead_items | 0 +page_size | 8192 +hasho_prevblkno | 3 +hasho_nextblkno | 4294967295 +hasho_bucket | 2 +hasho_flag | 2 +hasho_page_id | 65408 + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 4)); +-[ RECORD 1 ]---+----------- +live_items | 0 +dead_items | 0 +page_size | 8192 +hasho_prevblkno | 3 +hasho_nextblkno | 4294967295 +hasho_bucket | 3 +hasho_flag | 2 +hasho_page_id | 65408 + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 5)); +ERROR: page is not a hash bucket or overflow page +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 0)); +ERROR: page is not a hash bucket or overflow page +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 1)); +(0 rows) + +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 2)); +(0 rows) + +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 3)); +-[ RECORD 1 ]---------- +itemoffset | 1 +ctid | (0,1) +data | 2389907270 + +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 4)); +(0 rows) + +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 5)); +ERROR: page is not a hash bucket or overflow page +DROP TABLE test_hash; diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out new file mode 100644 index 0000000000..8e15947a81 --- /dev/null +++ b/contrib/pageinspect/expected/page.out @@ -0,0 +1,94 @@ +CREATE EXTENSION pageinspect; +CREATE TABLE test1 (a int, b int); +INSERT INTO test1 VALUES (16777217, 131584); +VACUUM test1; -- set up FSM +-- The page contents can vary, so just test that it can be read +-- successfully, but don't keep the output. +SELECT octet_length(get_raw_page('test1', 'main', 0)) AS main_0; + main_0 +-------- + 8192 +(1 row) + +SELECT octet_length(get_raw_page('test1', 'main', 1)) AS main_1; +ERROR: block number 1 is out of range for relation "test1" +SELECT octet_length(get_raw_page('test1', 'fsm', 0)) AS fsm_0; + fsm_0 +------- + 8192 +(1 row) + +SELECT octet_length(get_raw_page('test1', 'fsm', 1)) AS fsm_1; + fsm_1 +------- + 8192 +(1 row) + +SELECT octet_length(get_raw_page('test1', 'vm', 0)) AS vm_0; + vm_0 +------ + 8192 +(1 row) + +SELECT octet_length(get_raw_page('test1', 'vm', 1)) AS vm_1; +ERROR: block number 1 is out of range for relation "test1" +SELECT octet_length(get_raw_page('xxx', 'main', 0)); +ERROR: relation "xxx" does not exist +SELECT octet_length(get_raw_page('test1', 'xxx', 0)); +ERROR: invalid fork name +HINT: Valid fork names are "main", "fsm", "vm", and "init". +SELECT get_raw_page('test1', 0) = get_raw_page('test1', 'main', 0); + ?column? +---------- + t +(1 row) + +SELECT pagesize, version FROM page_header(get_raw_page('test1', 0)); + pagesize | version +----------+--------- + 8192 | 4 +(1 row) + +SELECT page_checksum(get_raw_page('test1', 0), 0) IS NOT NULL AS silly_checksum_test; + silly_checksum_test +--------------------- + t +(1 row) + +SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bits) + FROM heap_page_items(get_raw_page('test1', 0)); + tuple_data_split +------------------------------- + {"\\x01000001","\\x00020200"} +(1 row) + +SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0)); + fsm_page_contents +------------------- + 0: 254 + + 1: 254 + + 3: 254 + + 7: 254 + + 15: 254 + + 31: 254 + + 63: 254 + + 127: 254 + + 255: 254 + + 511: 254 + + 1023: 254 + + 2047: 254 + + 4095: 254 + + fp_next_slot: 0 + + +(1 row) + +DROP TABLE test1; +-- check that using any of these functions with a partitioned table would fail +create table test_partitioned (a int) partition by range (a); +select get_raw_page('test_partitioned', 0); -- error about partitioned table +ERROR: cannot get raw page from partitioned table "test_partitioned" +-- a regular table which is a member of a partition set should work though +create table test_part1 partition of test_partitioned for values from ( 1 ) to (100); +select get_raw_page('test_part1', 0); -- get farther and error about empty table +ERROR: block number 0 is out of range for relation "test_part1" +drop table test_partitioned; diff --git a/contrib/pageinspect/fsmfuncs.c b/contrib/pageinspect/fsmfuncs.c index c94f5f8445..615dab8b13 100644 --- a/contrib/pageinspect/fsmfuncs.c +++ b/contrib/pageinspect/fsmfuncs.c @@ -9,7 +9,7 @@ * there's hardly any use case for using these without superuser-rights * anyway. * - * Copyright (c) 2007-2016, PostgreSQL Global Development Group + * Copyright (c) 2007-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pageinspect/fsmfuncs.c @@ -19,6 +19,8 @@ #include "postgres.h" +#include "pageinspect.h" + #include "funcapi.h" #include "lib/stringinfo.h" #include "miscadmin.h" diff --git a/contrib/pageinspect/ginfuncs.c b/contrib/pageinspect/ginfuncs.c index a2f119b02e..993fc2d9ae 100644 --- a/contrib/pageinspect/ginfuncs.c +++ b/contrib/pageinspect/ginfuncs.c @@ -2,13 +2,15 @@ * ginfuncs.c * Functions to investigate the content of GIN indexes * - * Copyright (c) 2014-2016, PostgreSQL Global Development Group + * Copyright (c) 2014-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pageinspect/ginfuncs.c */ #include "postgres.h" +#include "pageinspect.h" + #include "access/gin.h" #include "access/gin_private.h" #include "access/htup_details.h" @@ -28,11 +30,11 @@ PG_FUNCTION_INFO_V1(gin_metapage_info); PG_FUNCTION_INFO_V1(gin_page_opaque_info); PG_FUNCTION_INFO_V1(gin_leafpage_items); + Datum gin_metapage_info(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); - int raw_page_size; TupleDesc tupdesc; Page page; GinPageOpaque opaq; @@ -46,12 +48,7 @@ gin_metapage_info(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); - raw_page_size = VARSIZE(raw_page) - VARHDRSZ; - if (raw_page_size < BLCKSZ) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input page too small (%d bytes)", raw_page_size))); - page = VARDATA(raw_page); + page = get_page_from_raw(raw_page); opaq = (GinPageOpaque) PageGetSpecialPointer(page); if (opaq->flags != GIN_META) @@ -94,13 +91,12 @@ Datum gin_page_opaque_info(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); - int raw_page_size; TupleDesc tupdesc; Page page; GinPageOpaque opaq; HeapTuple resultTuple; Datum values[3]; - bool nulls[10]; + bool nulls[3]; Datum flags[16]; int nflags = 0; uint16 flagbits; @@ -110,12 +106,7 @@ gin_page_opaque_info(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); - raw_page_size = VARSIZE(raw_page) - VARHDRSZ; - if (raw_page_size < BLCKSZ) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input page too small (%d bytes)", raw_page_size))); - page = VARDATA(raw_page); + page = get_page_from_raw(raw_page); opaq = (GinPageOpaque) PageGetSpecialPointer(page); @@ -152,9 +143,9 @@ gin_page_opaque_info(PG_FUNCTION_ARGS) memset(nulls, 0, sizeof(nulls)); values[0] = Int64GetDatum(opaq->rightlink); - values[1] = Int64GetDatum(opaq->maxoff); - values[2] = PointerGetDatum( - construct_array(flags, nflags, TEXTOID, -1, false, 'i')); + values[1] = Int32GetDatum(opaq->maxoff); + values[2] = PointerGetDatum(construct_array(flags, nflags, + TEXTOID, -1, false, 'i')); /* Build and return the result tuple. */ resultTuple = heap_form_tuple(tupdesc, values, nulls); @@ -173,7 +164,6 @@ Datum gin_leafpage_items(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); - int raw_page_size; FuncCallContext *fctx; gin_leafpage_items_state *inter_call_data; @@ -182,8 +172,6 @@ gin_leafpage_items(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to use raw page functions")))); - raw_page_size = VARSIZE(raw_page) - VARHDRSZ; - if (SRF_IS_FIRSTCALL()) { TupleDesc tupdesc; @@ -191,11 +179,10 @@ gin_leafpage_items(PG_FUNCTION_ARGS) Page page; GinPageOpaque opaq; - if (raw_page_size < BLCKSZ) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("input page too small (%d bytes)", raw_page_size))); - page = VARDATA(raw_page); + fctx = SRF_FIRSTCALL_INIT(); + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + page = get_page_from_raw(raw_page); if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData))) ereport(ERROR, @@ -214,9 +201,6 @@ gin_leafpage_items(PG_FUNCTION_ARGS) opaq->flags, (GIN_DATA | GIN_LEAF | GIN_COMPRESSED)))); - fctx = SRF_FIRSTCALL_INIT(); - mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); - inter_call_data = palloc(sizeof(gin_leafpage_items_state)); /* Build a tuple descriptor for our result type */ diff --git a/contrib/pageinspect/hashfuncs.c b/contrib/pageinspect/hashfuncs.c new file mode 100644 index 0000000000..228a147c9e --- /dev/null +++ b/contrib/pageinspect/hashfuncs.c @@ -0,0 +1,570 @@ +/* + * hashfuncs.c + * Functions to investigate the content of HASH indexes + * + * Copyright (c) 2017, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/hashfuncs.c + */ + +#include "postgres.h" + +#include "pageinspect.h" + +#include "access/hash.h" +#include "access/htup_details.h" +#include "catalog/pg_type.h" +#include "catalog/pg_am.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "utils/builtins.h" + +PG_FUNCTION_INFO_V1(hash_page_type); +PG_FUNCTION_INFO_V1(hash_page_stats); +PG_FUNCTION_INFO_V1(hash_page_items); +PG_FUNCTION_INFO_V1(hash_bitmap_info); +PG_FUNCTION_INFO_V1(hash_metapage_info); + +#define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID) + +/* ------------------------------------------------ + * structure for single hash page statistics + * ------------------------------------------------ + */ +typedef struct HashPageStat +{ + int live_items; + int dead_items; + int page_size; + int free_size; + + /* opaque data */ + BlockNumber hasho_prevblkno; + BlockNumber hasho_nextblkno; + Bucket hasho_bucket; + uint16 hasho_flag; + uint16 hasho_page_id; +} HashPageStat; + + +/* + * Verify that the given bytea contains a HASH page, or die in the attempt. + * A pointer to a palloc'd, properly aligned copy of the page is returned. + */ +static Page +verify_hash_page(bytea *raw_page, int flags) +{ + Page page = get_page_from_raw(raw_page); + int pagetype = LH_UNUSED_PAGE; + + /* Treat new pages as unused. */ + if (!PageIsNew(page)) + { + HashPageOpaque pageopaque; + + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(HashPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("index table contains corrupted page"))); + + pageopaque = (HashPageOpaque) PageGetSpecialPointer(page); + if (pageopaque->hasho_page_id != HASHO_PAGE_ID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("page is not a hash page"), + errdetail("Expected %08x, got %08x.", + HASHO_PAGE_ID, pageopaque->hasho_page_id))); + + pagetype = pageopaque->hasho_flag & LH_PAGE_TYPE; + } + + /* Check that page type is sane. */ + if (pagetype != LH_OVERFLOW_PAGE && pagetype != LH_BUCKET_PAGE && + pagetype != LH_BITMAP_PAGE && pagetype != LH_META_PAGE && + pagetype != LH_UNUSED_PAGE) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid hash page type %08x", pagetype))); + + /* If requested, verify page type. */ + if (flags != 0 && (pagetype & flags) == 0) + { + switch (flags) + { + case LH_META_PAGE: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("page is not a hash meta page"))); + case LH_BUCKET_PAGE | LH_OVERFLOW_PAGE: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("page is not a hash bucket or overflow page"))); + case LH_OVERFLOW_PAGE: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("page is not a hash overflow page"))); + default: + elog(ERROR, + "hash page of type %08x not in mask %08x", + pagetype, flags); + } + } + + /* + * If it is the metapage, also verify magic number and version. + */ + if (pagetype == LH_META_PAGE) + { + HashMetaPage metap = HashPageGetMeta(page); + + if (metap->hashm_magic != HASH_MAGIC) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalid magic number for metadata"), + errdetail("Expected 0x%08x, got 0x%08x.", + HASH_MAGIC, metap->hashm_magic))); + + if (metap->hashm_version != HASH_VERSION) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("invalid version for metadata"), + errdetail("Expected %d, got %d", + HASH_VERSION, metap->hashm_version))); + } + + return page; +} + +/* ------------------------------------------------- + * GetHashPageStatistics() + * + * Collect statistics of single hash page + * ------------------------------------------------- + */ +static void +GetHashPageStatistics(Page page, HashPageStat *stat) +{ + OffsetNumber maxoff = PageGetMaxOffsetNumber(page); + HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page); + int off; + + stat->dead_items = stat->live_items = 0; + stat->page_size = PageGetPageSize(page); + + /* hash page opaque data */ + stat->hasho_prevblkno = opaque->hasho_prevblkno; + stat->hasho_nextblkno = opaque->hasho_nextblkno; + stat->hasho_bucket = opaque->hasho_bucket; + stat->hasho_flag = opaque->hasho_flag; + stat->hasho_page_id = opaque->hasho_page_id; + + /* count live and dead tuples, and free space */ + for (off = FirstOffsetNumber; off <= maxoff; off++) + { + ItemId id = PageGetItemId(page, off); + + if (!ItemIdIsDead(id)) + stat->live_items++; + else + stat->dead_items++; + } + stat->free_size = PageGetFreeSpace(page); +} + +/* --------------------------------------------------- + * hash_page_type() + * + * Usage: SELECT hash_page_type(get_raw_page('con_hash_index', 1)); + * --------------------------------------------------- + */ +Datum +hash_page_type(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Page page; + HashPageOpaque opaque; + int pagetype; + const char *type; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + page = verify_hash_page(raw_page, 0); + + if (PageIsNew(page)) + type = "unused"; + else + { + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + + /* page type (flags) */ + pagetype = opaque->hasho_flag & LH_PAGE_TYPE; + if (pagetype == LH_META_PAGE) + type = "metapage"; + else if (pagetype == LH_OVERFLOW_PAGE) + type = "overflow"; + else if (pagetype == LH_BUCKET_PAGE) + type = "bucket"; + else if (pagetype == LH_BITMAP_PAGE) + type = "bitmap"; + else + type = "unused"; + } + + PG_RETURN_TEXT_P(cstring_to_text(type)); +} + +/* --------------------------------------------------- + * hash_page_stats() + * + * Usage: SELECT * FROM hash_page_stats(get_raw_page('con_hash_index', 1)); + * --------------------------------------------------- + */ +Datum +hash_page_stats(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Page page; + int j; + Datum values[9]; + bool nulls[9]; + HashPageStat stat; + HeapTuple tuple; + TupleDesc tupleDesc; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + page = verify_hash_page(raw_page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + + /* keep compiler quiet */ + stat.hasho_prevblkno = stat.hasho_nextblkno = InvalidBlockNumber; + stat.hasho_flag = stat.hasho_page_id = stat.free_size = 0; + + GetHashPageStatistics(page, &stat); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + tupleDesc = BlessTupleDesc(tupleDesc); + + MemSet(nulls, 0, sizeof(nulls)); + + j = 0; + values[j++] = Int32GetDatum(stat.live_items); + values[j++] = Int32GetDatum(stat.dead_items); + values[j++] = Int32GetDatum(stat.page_size); + values[j++] = Int32GetDatum(stat.free_size); + values[j++] = Int64GetDatum((int64) stat.hasho_prevblkno); + values[j++] = Int64GetDatum((int64) stat.hasho_nextblkno); + values[j++] = Int64GetDatum((int64) stat.hasho_bucket); + values[j++] = Int32GetDatum((int32) stat.hasho_flag); + values[j++] = Int32GetDatum((int32) stat.hasho_page_id); + + tuple = heap_form_tuple(tupleDesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} + +/* + * cross-call data structure for SRF + */ +struct user_args +{ + Page page; + OffsetNumber offset; +}; + +/*------------------------------------------------------- + * hash_page_items() + * + * Get IndexTupleData set in a hash page + * + * Usage: SELECT * FROM hash_page_items(get_raw_page('con_hash_index', 1)); + *------------------------------------------------------- + */ +Datum +hash_page_items(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Page page; + Datum result; + Datum values[3]; + bool nulls[3]; + uint32 hashkey; + HeapTuple tuple; + FuncCallContext *fctx; + MemoryContext mctx; + struct user_args *uargs; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + if (SRF_IS_FIRSTCALL()) + { + TupleDesc tupleDesc; + + fctx = SRF_FIRSTCALL_INIT(); + + page = verify_hash_page(raw_page, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE); + + mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + uargs = palloc(sizeof(struct user_args)); + + uargs->page = page; + + uargs->offset = FirstOffsetNumber; + + fctx->max_calls = PageGetMaxOffsetNumber(uargs->page); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + tupleDesc = BlessTupleDesc(tupleDesc); + + fctx->attinmeta = TupleDescGetAttInMetadata(tupleDesc); + + fctx->user_fctx = uargs; + + MemoryContextSwitchTo(mctx); + } + + fctx = SRF_PERCALL_SETUP(); + uargs = fctx->user_fctx; + + if (fctx->call_cntr < fctx->max_calls) + { + ItemId id; + IndexTuple itup; + int j; + + id = PageGetItemId(uargs->page, uargs->offset); + + if (!ItemIdIsValid(id)) + elog(ERROR, "invalid ItemId"); + + itup = (IndexTuple) PageGetItem(uargs->page, id); + + MemSet(nulls, 0, sizeof(nulls)); + + j = 0; + values[j++] = Int32GetDatum((int32) uargs->offset); + values[j++] = PointerGetDatum(&itup->t_tid); + + hashkey = _hash_get_indextuple_hashkey(itup); + values[j] = Int64GetDatum((int64) hashkey); + + tuple = heap_form_tuple(fctx->attinmeta->tupdesc, values, nulls); + result = HeapTupleGetDatum(tuple); + + uargs->offset = uargs->offset + 1; + + SRF_RETURN_NEXT(fctx, result); + } + else + { + pfree(uargs); + SRF_RETURN_DONE(fctx); + } +} + +/* ------------------------------------------------ + * hash_bitmap_info() + * + * Get bitmap information for a particular overflow page + * + * Usage: SELECT * FROM hash_bitmap_info('con_hash_index'::regclass, 5); + * ------------------------------------------------ + */ +Datum +hash_bitmap_info(PG_FUNCTION_ARGS) +{ + Oid indexRelid = PG_GETARG_OID(0); + uint64 ovflblkno = PG_GETARG_INT64(1); + HashMetaPage metap; + Buffer metabuf, + mapbuf; + BlockNumber bitmapblkno; + Page mappage; + bool bit = false; + TupleDesc tupleDesc; + Relation indexRel; + uint32 ovflbitno; + int32 bitmappage, + bitmapbit; + HeapTuple tuple; + int i, + j; + Datum values[3]; + bool nulls[3]; + uint32 *freep; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + indexRel = index_open(indexRelid, AccessShareLock); + + if (!IS_HASH(indexRel)) + elog(ERROR, "relation \"%s\" is not a hash index", + RelationGetRelationName(indexRel)); + + if (RELATION_IS_OTHER_TEMP(indexRel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + + if (ovflblkno >= RelationGetNumberOfBlocks(indexRel)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("block number " UINT64_FORMAT " is out of range for relation \"%s\"", + ovflblkno, RelationGetRelationName(indexRel)))); + + /* Read the metapage so we can determine which bitmap page to use */ + metabuf = _hash_getbuf(indexRel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); + metap = HashPageGetMeta(BufferGetPage(metabuf)); + + /* + * Reject attempt to read the bit for a metapage or bitmap page; this is + * only meaningful for overflow pages. + */ + if (ovflblkno == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid overflow block number %u", + (BlockNumber) ovflblkno))); + for (i = 0; i < metap->hashm_nmaps; i++) + if (metap->hashm_mapp[i] == ovflblkno) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid overflow block number %u", + (BlockNumber) ovflblkno))); + + /* + * Identify overflow bit number. This will error out for primary bucket + * pages, and we've already rejected the metapage and bitmap pages above. + */ + ovflbitno = _hash_ovflblkno_to_bitno(metap, (BlockNumber) ovflblkno); + + bitmappage = ovflbitno >> BMPG_SHIFT(metap); + bitmapbit = ovflbitno & BMPG_MASK(metap); + + if (bitmappage >= metap->hashm_nmaps) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid overflow block number %u", + (BlockNumber) ovflblkno))); + + bitmapblkno = metap->hashm_mapp[bitmappage]; + + _hash_relbuf(indexRel, metabuf); + + /* Check the status of bitmap bit for overflow page */ + mapbuf = _hash_getbuf(indexRel, bitmapblkno, HASH_READ, LH_BITMAP_PAGE); + mappage = BufferGetPage(mapbuf); + freep = HashPageGetBitmap(mappage); + + bit = ISSET(freep, bitmapbit) != 0; + + _hash_relbuf(indexRel, mapbuf); + index_close(indexRel, AccessShareLock); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + tupleDesc = BlessTupleDesc(tupleDesc); + + MemSet(nulls, 0, sizeof(nulls)); + + j = 0; + values[j++] = Int64GetDatum((int64) bitmapblkno); + values[j++] = Int32GetDatum(bitmapbit); + values[j++] = BoolGetDatum(bit); + + tuple = heap_form_tuple(tupleDesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} + +/* ------------------------------------------------ + * hash_metapage_info() + * + * Get the meta-page information for a hash index + * + * Usage: SELECT * FROM hash_metapage_info(get_raw_page('con_hash_index', 0)) + * ------------------------------------------------ + */ +Datum +hash_metapage_info(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + Page page; + HashMetaPageData *metad; + TupleDesc tupleDesc; + HeapTuple tuple; + int i, + j; + Datum values[16]; + bool nulls[16]; + Datum spares[HASH_MAX_SPLITPOINTS]; + Datum mapp[HASH_MAX_BITMAPS]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + page = verify_hash_page(raw_page, LH_META_PAGE); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + tupleDesc = BlessTupleDesc(tupleDesc); + + metad = HashPageGetMeta(page); + + MemSet(nulls, 0, sizeof(nulls)); + + j = 0; + values[j++] = Int64GetDatum((int64) metad->hashm_magic); + values[j++] = Int64GetDatum((int64) metad->hashm_version); + values[j++] = Float8GetDatum(metad->hashm_ntuples); + values[j++] = Int32GetDatum((int32) metad->hashm_ffactor); + values[j++] = Int32GetDatum((int32) metad->hashm_bsize); + values[j++] = Int32GetDatum((int32) metad->hashm_bmsize); + values[j++] = Int32GetDatum((int32) metad->hashm_bmshift); + values[j++] = Int64GetDatum((int64) metad->hashm_maxbucket); + values[j++] = Int64GetDatum((int64) metad->hashm_highmask); + values[j++] = Int64GetDatum((int64) metad->hashm_lowmask); + values[j++] = Int64GetDatum((int64) metad->hashm_ovflpoint); + values[j++] = Int64GetDatum((int64) metad->hashm_firstfree); + values[j++] = Int64GetDatum((int64) metad->hashm_nmaps); + values[j++] = ObjectIdGetDatum((Oid) metad->hashm_procid); + + for (i = 0; i < HASH_MAX_SPLITPOINTS; i++) + spares[i] = Int64GetDatum((int64) metad->hashm_spares[i]); + values[j++] = PointerGetDatum(construct_array(spares, + HASH_MAX_SPLITPOINTS, + INT8OID, + 8, FLOAT8PASSBYVAL, 'd')); + + for (i = 0; i < HASH_MAX_BITMAPS; i++) + mapp[i] = Int64GetDatum((int64) metad->hashm_mapp[i]); + values[j++] = PointerGetDatum(construct_array(mapp, + HASH_MAX_BITMAPS, + INT8OID, + 8, FLOAT8PASSBYVAL, 'd')); + + tuple = heap_form_tuple(tupleDesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c index 904eaef2da..1448effb50 100644 --- a/contrib/pageinspect/heapfuncs.c +++ b/contrib/pageinspect/heapfuncs.c @@ -15,7 +15,7 @@ * there's hardly any use case for using these without superuser-rights * anyway. * - * Copyright (c) 2007-2016, PostgreSQL Global Development Group + * Copyright (c) 2007-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pageinspect/heapfuncs.c @@ -25,6 +25,8 @@ #include "postgres.h" +#include "pageinspect.h" + #include "access/htup_details.h" #include "funcapi.h" #include "catalog/pg_type.h" @@ -279,7 +281,7 @@ heap_page_items(PG_FUNCTION_ARGS) * * Split raw tuple data taken directly from a page into an array of bytea * elements. This routine does a lookup on NULL values and creates array - * elements accordindly. This is a reimplementation of nocachegetattr() + * elements accordingly. This is a reimplementation of nocachegetattr() * in heaptuple.c simplified for educational purposes. */ static Datum diff --git a/contrib/pageinspect/pageinspect--1.5--1.6.sql b/contrib/pageinspect/pageinspect--1.5--1.6.sql new file mode 100644 index 0000000000..c4356288db --- /dev/null +++ b/contrib/pageinspect/pageinspect--1.5--1.6.sql @@ -0,0 +1,99 @@ +/* contrib/pageinspect/pageinspect--1.5--1.6.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.6'" to load this file. \quit + +-- +-- HASH functions +-- + +-- +-- hash_page_type() +-- +CREATE FUNCTION hash_page_type(IN page bytea) +RETURNS text +AS 'MODULE_PATHNAME', 'hash_page_type' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- hash_page_stats() +-- +CREATE FUNCTION hash_page_stats(IN page bytea, + OUT live_items int4, + OUT dead_items int4, + OUT page_size int4, + OUT free_size int4, + OUT hasho_prevblkno int8, + OUT hasho_nextblkno int8, + OUT hasho_bucket int8, + OUT hasho_flag int4, + OUT hasho_page_id int4) +AS 'MODULE_PATHNAME', 'hash_page_stats' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- hash_page_items() +-- +CREATE FUNCTION hash_page_items(IN page bytea, + OUT itemoffset int4, + OUT ctid tid, + OUT data int8) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'hash_page_items' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- hash_bitmap_info() +-- +CREATE FUNCTION hash_bitmap_info(IN index_oid regclass, IN blkno int8, + OUT bitmapblkno int8, + OUT bitmapbit int4, + OUT bitstatus bool) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'hash_bitmap_info' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- hash_metapage_info() +-- +CREATE FUNCTION hash_metapage_info(IN page bytea, + OUT magic int8, + OUT version int8, + OUT ntuples double precision, + OUT ffactor int4, + OUT bsize int4, + OUT bmsize int4, + OUT bmshift int4, + OUT maxbucket int8, + OUT highmask int8, + OUT lowmask int8, + OUT ovflpoint int8, + OUT firstfree int8, + OUT nmaps int8, + OUT procid oid, + OUT spares int8[], + OUT mapp int8[]) +AS 'MODULE_PATHNAME', 'hash_metapage_info' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- page_checksum() +-- +CREATE FUNCTION page_checksum(IN page bytea, IN blkno int4) +RETURNS smallint +AS 'MODULE_PATHNAME', 'page_checksum' +LANGUAGE C STRICT PARALLEL SAFE; + +-- +-- bt_page_items_bytea() +-- +CREATE FUNCTION bt_page_items(IN page bytea, + OUT itemoffset smallint, + OUT ctid tid, + OUT itemlen smallint, + OUT nulls bool, + OUT vars bool, + OUT data text) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'bt_page_items_bytea' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control index 23c8eff9cd..1a61c9f5ad 100644 --- a/contrib/pageinspect/pageinspect.control +++ b/contrib/pageinspect/pageinspect.control @@ -1,5 +1,5 @@ # pageinspect extension comment = 'inspect the contents of database pages at a low level' -default_version = '1.5' +default_version = '1.6' module_pathname = '$libdir/pageinspect' relocatable = true diff --git a/contrib/pageinspect/pageinspect.h b/contrib/pageinspect/pageinspect.h new file mode 100644 index 0000000000..55ca68fab3 --- /dev/null +++ b/contrib/pageinspect/pageinspect.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * pageinspect.h + * Common functions for pageinspect. + * + * Copyright (c) 2017, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/pageinspect/pageinspect.h + * + *------------------------------------------------------------------------- + */ +#ifndef _PAGEINSPECT_H_ +#define _PAGEINSPECT_H_ + +#include "storage/bufpage.h" + +/* in rawpage.c */ +extern Page get_page_from_raw(bytea *raw_page); + +#endif /* _PAGEINSPECT_H_ */ diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c index 71d0c8d2ca..f273dfa7cb 100644 --- a/contrib/pageinspect/rawpage.c +++ b/contrib/pageinspect/rawpage.c @@ -5,7 +5,7 @@ * * Access-method specific inspection functions are in separate files. * - * Copyright (c) 2007-2016, PostgreSQL Global Development Group + * Copyright (c) 2007-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pageinspect/rawpage.c @@ -15,6 +15,8 @@ #include "postgres.h" +#include "pageinspect.h" + #include "access/htup_details.h" #include "catalog/catalog.h" #include "catalog/namespace.h" @@ -22,9 +24,11 @@ #include "funcapi.h" #include "miscadmin.h" #include "storage/bufmgr.h" +#include "storage/checksum.h" #include "utils/builtins.h" #include "utils/pg_lsn.h" #include "utils/rel.h" +#include "utils/varlena.h" PG_MODULE_MAGIC; @@ -42,7 +46,7 @@ PG_FUNCTION_INFO_V1(get_raw_page); Datum get_raw_page(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); uint32 blkno = PG_GETARG_UINT32(1); bytea *raw_page; @@ -71,8 +75,8 @@ PG_FUNCTION_INFO_V1(get_raw_page_fork); Datum get_raw_page_fork(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); - text *forkname = PG_GETARG_TEXT_P(1); + text *relname = PG_GETARG_TEXT_PP(0); + text *forkname = PG_GETARG_TEXT_PP(1); uint32 blkno = PG_GETARG_UINT32(2); bytea *raw_page; ForkNumber forknum; @@ -120,6 +124,11 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot get raw page from foreign table \"%s\"", RelationGetRelationName(rel)))); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from partitioned table \"%s\"", + RelationGetRelationName(rel)))); /* * Reject attempts to read non-local temporary relations; we would be @@ -157,6 +166,42 @@ get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno) return raw_page; } + +/* + * get_page_from_raw + * + * Get a palloc'd, maxalign'ed page image from the result of get_raw_page() + * + * On machines with MAXALIGN = 8, the payload of a bytea is not maxaligned, + * since it will start 4 bytes into a palloc'd value. On alignment-picky + * machines, this will cause failures in accesses to 8-byte-wide values + * within the page. We don't need to worry if accessing only 4-byte or + * smaller fields, but when examining a struct that contains 8-byte fields, + * use this function for safety. + */ +Page +get_page_from_raw(bytea *raw_page) +{ + Page page; + int raw_page_size; + + raw_page_size = VARSIZE_ANY_EXHDR(raw_page); + + if (raw_page_size != BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid page size"), + errdetail("Expected %d bytes, got %d.", + BLCKSZ, raw_page_size))); + + page = palloc(raw_page_size); + + memcpy(page, VARDATA_ANY(raw_page), raw_page_size); + + return page; +} + + /* * page_header * @@ -236,3 +281,39 @@ page_header(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } + +/* + * page_checksum + * + * Compute checksum of a raw page + */ + +PG_FUNCTION_INFO_V1(page_checksum); + +Datum +page_checksum(PG_FUNCTION_ARGS) +{ + bytea *raw_page = PG_GETARG_BYTEA_P(0); + uint32 blkno = PG_GETARG_INT32(1); + int raw_page_size; + PageHeader page; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use raw page functions")))); + + raw_page_size = VARSIZE(raw_page) - VARHDRSZ; + + /* + * Check that the supplied page is of the right size. + */ + if (raw_page_size != BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("incorrect size of input page (%d bytes)", raw_page_size))); + + page = (PageHeader) VARDATA(raw_page); + + PG_RETURN_INT16(pg_checksum_page((char *) page, blkno)); +} diff --git a/contrib/pageinspect/sql/brin.sql b/contrib/pageinspect/sql/brin.sql new file mode 100644 index 0000000000..735bc3b673 --- /dev/null +++ b/contrib/pageinspect/sql/brin.sql @@ -0,0 +1,18 @@ +CREATE TABLE test1 (a int, b text); +INSERT INTO test1 VALUES (1, 'one'); +CREATE INDEX test1_a_idx ON test1 USING brin (a); + +SELECT brin_page_type(get_raw_page('test1_a_idx', 0)); +SELECT brin_page_type(get_raw_page('test1_a_idx', 1)); +SELECT brin_page_type(get_raw_page('test1_a_idx', 2)); + +SELECT * FROM brin_metapage_info(get_raw_page('test1_a_idx', 0)); +SELECT * FROM brin_metapage_info(get_raw_page('test1_a_idx', 1)); + +SELECT * FROM brin_revmap_data(get_raw_page('test1_a_idx', 0)) LIMIT 5; +SELECT * FROM brin_revmap_data(get_raw_page('test1_a_idx', 1)) LIMIT 5; + +SELECT * FROM brin_page_items(get_raw_page('test1_a_idx', 2), 'test1_a_idx') + ORDER BY blknum, attnum LIMIT 5; + +DROP TABLE test1; diff --git a/contrib/pageinspect/sql/btree.sql b/contrib/pageinspect/sql/btree.sql new file mode 100644 index 0000000000..8eac64c7b3 --- /dev/null +++ b/contrib/pageinspect/sql/btree.sql @@ -0,0 +1,21 @@ +CREATE TABLE test1 (a int8, b text); +INSERT INTO test1 VALUES (72057594037927937, 'text'); +CREATE INDEX test1_a_idx ON test1 USING btree (a); + +\x + +SELECT * FROM bt_metap('test1_a_idx'); + +SELECT * FROM bt_page_stats('test1_a_idx', 0); +SELECT * FROM bt_page_stats('test1_a_idx', 1); +SELECT * FROM bt_page_stats('test1_a_idx', 2); + +SELECT * FROM bt_page_items('test1_a_idx', 0); +SELECT * FROM bt_page_items('test1_a_idx', 1); +SELECT * FROM bt_page_items('test1_a_idx', 2); + +SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 0)); +SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 1)); +SELECT * FROM bt_page_items(get_raw_page('test1_a_idx', 2)); + +DROP TABLE test1; diff --git a/contrib/pageinspect/sql/gin.sql b/contrib/pageinspect/sql/gin.sql new file mode 100644 index 0000000000..d516ed3cbd --- /dev/null +++ b/contrib/pageinspect/sql/gin.sql @@ -0,0 +1,19 @@ +CREATE TABLE test1 (x int, y int[]); +INSERT INTO test1 VALUES (1, ARRAY[11, 111]); +CREATE INDEX test1_y_idx ON test1 USING gin (y) WITH (fastupdate = off); + +\x + +SELECT * FROM gin_metapage_info(get_raw_page('test1_y_idx', 0)); +SELECT * FROM gin_metapage_info(get_raw_page('test1_y_idx', 1)); + +SELECT * FROM gin_page_opaque_info(get_raw_page('test1_y_idx', 1)); + +SELECT * FROM gin_leafpage_items(get_raw_page('test1_y_idx', 1)); + +INSERT INTO test1 SELECT x, ARRAY[1,10] FROM generate_series(2,10000) x; + +SELECT COUNT(*) > 0 +FROM gin_leafpage_items(get_raw_page('test1_y_idx', + (pg_relation_size('test1_y_idx') / + current_setting('block_size')::bigint)::int - 1)); diff --git a/contrib/pageinspect/sql/hash.sql b/contrib/pageinspect/sql/hash.sql new file mode 100644 index 0000000000..87ee549a7b --- /dev/null +++ b/contrib/pageinspect/sql/hash.sql @@ -0,0 +1,80 @@ +CREATE TABLE test_hash (a int, b text); +INSERT INTO test_hash VALUES (1, 'one'); +CREATE INDEX test_hash_a_idx ON test_hash USING hash (a); + +\x + +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0)); +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 1)); +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 2)); +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 3)); +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 4)); +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 5)); +SELECT hash_page_type(get_raw_page('test_hash_a_idx', 6)); + + +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 0); +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 1); +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 2); +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3); +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4); +SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5); + + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 0)); + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 1)); + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 2)); + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 3)); + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 4)); + +SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask, +lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM +hash_metapage_info(get_raw_page('test_hash_a_idx', 5)); + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 0)); + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 1)); + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 2)); + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 3)); + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 4)); + +SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno, +hasho_bucket, hasho_flag, hasho_page_id FROM +hash_page_stats(get_raw_page('test_hash_a_idx', 5)); + +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 0)); +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 1)); +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 2)); +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 3)); +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 4)); +SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 5)); + + +DROP TABLE test_hash; diff --git a/contrib/pageinspect/sql/page.sql b/contrib/pageinspect/sql/page.sql new file mode 100644 index 0000000000..493ca9b211 --- /dev/null +++ b/contrib/pageinspect/sql/page.sql @@ -0,0 +1,43 @@ +CREATE EXTENSION pageinspect; + +CREATE TABLE test1 (a int, b int); +INSERT INTO test1 VALUES (16777217, 131584); + +VACUUM test1; -- set up FSM + +-- The page contents can vary, so just test that it can be read +-- successfully, but don't keep the output. + +SELECT octet_length(get_raw_page('test1', 'main', 0)) AS main_0; +SELECT octet_length(get_raw_page('test1', 'main', 1)) AS main_1; + +SELECT octet_length(get_raw_page('test1', 'fsm', 0)) AS fsm_0; +SELECT octet_length(get_raw_page('test1', 'fsm', 1)) AS fsm_1; + +SELECT octet_length(get_raw_page('test1', 'vm', 0)) AS vm_0; +SELECT octet_length(get_raw_page('test1', 'vm', 1)) AS vm_1; + +SELECT octet_length(get_raw_page('xxx', 'main', 0)); +SELECT octet_length(get_raw_page('test1', 'xxx', 0)); + +SELECT get_raw_page('test1', 0) = get_raw_page('test1', 'main', 0); + +SELECT pagesize, version FROM page_header(get_raw_page('test1', 0)); + +SELECT page_checksum(get_raw_page('test1', 0), 0) IS NOT NULL AS silly_checksum_test; + +SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bits) + FROM heap_page_items(get_raw_page('test1', 0)); + +SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0)); + +DROP TABLE test1; + +-- check that using any of these functions with a partitioned table would fail +create table test_partitioned (a int) partition by range (a); +select get_raw_page('test_partitioned', 0); -- error about partitioned table + +-- a regular table which is a member of a partition set should work though +create table test_part1 partition of test_partitioned for values from ( 1 ) to (100); +select get_raw_page('test_part1', 0); -- get farther and error about empty table +drop table test_partitioned; diff --git a/contrib/passwordcheck/passwordcheck.c b/contrib/passwordcheck/passwordcheck.c index b4c1ce005b..59f73a1e6b 100644 --- a/contrib/passwordcheck/passwordcheck.c +++ b/contrib/passwordcheck/passwordcheck.c @@ -3,7 +3,7 @@ * passwordcheck.c * * - * Copyright (c) 2009-2016, PostgreSQL Global Development Group + * Copyright (c) 2009-2017, PostgreSQL Global Development Group * * Author: Laurenz Albe <laurenz.albe@wien.gv.at> * @@ -21,8 +21,8 @@ #endif #include "commands/user.h" +#include "libpq/crypt.h" #include "fmgr.h" -#include "libpq/md5.h" PG_MODULE_MAGIC; @@ -39,8 +39,8 @@ extern void _PG_init(void); * * username: name of role being created or changed * password: new password (possibly already encrypted) - * password_type: PASSWORD_TYPE_PLAINTEXT or PASSWORD_TYPE_MD5 (there - * could be other encryption schemes in future) + * password_type: PASSWORD_TYPE_* code, to indicate if the password is + * in plaintext or encrypted form. * validuntil_time: password expiration time, as a timestamptz Datum * validuntil_null: true if password expiration time is NULL * @@ -50,87 +50,77 @@ extern void _PG_init(void); */ static void check_password(const char *username, - const char *password, - int password_type, + const char *shadow_pass, + PasswordType password_type, Datum validuntil_time, bool validuntil_null) { - int namelen = strlen(username); - int pwdlen = strlen(password); - char encrypted[MD5_PASSWD_LEN + 1]; - int i; - bool pwd_has_letter, - pwd_has_nonletter; - - switch (password_type) + if (password_type != PASSWORD_TYPE_PLAINTEXT) { - case PASSWORD_TYPE_MD5: - - /* - * Unfortunately we cannot perform exhaustive checks on encrypted - * passwords - we are restricted to guessing. (Alternatively, we - * could insist on the password being presented non-encrypted, but - * that has its own security disadvantages.) - * - * We only check for username = password. - */ - if (!pg_md5_encrypt(username, username, namelen, encrypted)) - elog(ERROR, "password encryption failed"); - if (strcmp(password, encrypted) == 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("password must not contain user name"))); - break; - - case PASSWORD_TYPE_PLAINTEXT: - + /* + * Unfortunately we cannot perform exhaustive checks on encrypted + * passwords - we are restricted to guessing. (Alternatively, we could + * insist on the password being presented non-encrypted, but that has + * its own security disadvantages.) + * + * We only check for username = password. + */ + char *logdetail; + + if (plain_crypt_verify(username, shadow_pass, username, &logdetail) == STATUS_OK) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password must not contain user name"))); + } + else + { + /* + * For unencrypted passwords we can perform better checks + */ + const char *password = shadow_pass; + int pwdlen = strlen(password); + int i; + bool pwd_has_letter, + pwd_has_nonletter; + + /* enforce minimum length */ + if (pwdlen < MIN_PWD_LENGTH) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password is too short"))); + + /* check if the password contains the username */ + if (strstr(password, username)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password must not contain user name"))); + + /* check if the password contains both letters and non-letters */ + pwd_has_letter = false; + pwd_has_nonletter = false; + for (i = 0; i < pwdlen; i++) + { /* - * For unencrypted passwords we can perform better checks + * isalpha() does not work for multibyte encodings but let's + * consider non-ASCII characters non-letters */ - - /* enforce minimum length */ - if (pwdlen < MIN_PWD_LENGTH) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("password is too short"))); - - /* check if the password contains the username */ - if (strstr(password, username)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("password must not contain user name"))); - - /* check if the password contains both letters and non-letters */ - pwd_has_letter = false; - pwd_has_nonletter = false; - for (i = 0; i < pwdlen; i++) - { - /* - * isalpha() does not work for multibyte encodings but let's - * consider non-ASCII characters non-letters - */ - if (isalpha((unsigned char) password[i])) - pwd_has_letter = true; - else - pwd_has_nonletter = true; - } - if (!pwd_has_letter || !pwd_has_nonletter) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("password must contain both letters and nonletters"))); + if (isalpha((unsigned char) password[i])) + pwd_has_letter = true; + else + pwd_has_nonletter = true; + } + if (!pwd_has_letter || !pwd_has_nonletter) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password must contain both letters and nonletters"))); #ifdef USE_CRACKLIB - /* call cracklib to check password */ - if (FascistCheck(password, CRACKLIB_DICTPATH)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("password is easily cracked"))); + /* call cracklib to check password */ + if (FascistCheck(password, CRACKLIB_DICTPATH)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("password is easily cracked"))); #endif - break; - - default: - elog(ERROR, "unrecognized password type: %d", password_type); - break; } /* all checks passed, password is ok */ diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile index 497dbeb229..18f7a87452 100644 --- a/contrib/pg_buffercache/Makefile +++ b/contrib/pg_buffercache/Makefile @@ -4,8 +4,9 @@ MODULE_big = pg_buffercache OBJS = pg_buffercache_pages.o $(WIN32RES) EXTENSION = pg_buffercache -DATA = pg_buffercache--1.2.sql pg_buffercache--1.1--1.2.sql \ - pg_buffercache--1.0--1.1.sql pg_buffercache--unpackaged--1.0.sql +DATA = pg_buffercache--1.2.sql pg_buffercache--1.2--1.3.sql \ + pg_buffercache--1.1--1.2.sql pg_buffercache--1.0--1.1.sql \ + pg_buffercache--unpackaged--1.0.sql PGFILEDESC = "pg_buffercache - monitoring of shared buffer cache in real-time" ifdef USE_PGXS diff --git a/contrib/pg_buffercache/pg_buffercache--1.2--1.3.sql b/contrib/pg_buffercache/pg_buffercache--1.2--1.3.sql new file mode 100644 index 0000000000..b37ef0112e --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache--1.2--1.3.sql @@ -0,0 +1,7 @@ +/* contrib/pg_buffercache/pg_buffercache--1.2--1.3.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_buffercache UPDATE TO '1.3'" to load this file. \quit + +GRANT EXECUTE ON FUNCTION pg_buffercache_pages() TO pg_monitor; +GRANT SELECT ON pg_buffercache TO pg_monitor; diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control index a4d664f3fa..8c060ae9ab 100644 --- a/contrib/pg_buffercache/pg_buffercache.control +++ b/contrib/pg_buffercache/pg_buffercache.control @@ -1,5 +1,5 @@ # pg_buffercache extension comment = 'examine the shared buffer cache' -default_version = '1.2' +default_version = '1.3' module_pathname = '$libdir/pg_buffercache' relocatable = true diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c index 17b4b6fa6f..8bebf2384d 100644 --- a/contrib/pg_buffercache/pg_buffercache_pages.c +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -124,7 +124,9 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) fctx->tupdesc = BlessTupleDesc(tupledesc); /* Allocate NBuffers worth of BufferCachePagesRec records. */ - fctx->record = (BufferCachePagesRec *) palloc(sizeof(BufferCachePagesRec) * NBuffers); + fctx->record = (BufferCachePagesRec *) + MemoryContextAllocHuge(CurrentMemoryContext, + sizeof(BufferCachePagesRec) * NBuffers); /* Set max calls and remember the user function context. */ funcctx->max_calls = NBuffers; @@ -134,17 +136,12 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) MemoryContextSwitchTo(oldcontext); /* - * To get a consistent picture of the buffer state, we must lock all - * partitions of the buffer map. Needless to say, this is horrible - * for concurrency. Must grab locks in increasing order to avoid - * possible deadlocks. - */ - for (i = 0; i < NUM_BUFFER_PARTITIONS; i++) - LWLockAcquire(BufMappingPartitionLockByIndex(i), LW_SHARED); - - /* * Scan through all the buffers, saving the relevant fields in the * fctx->record structure. + * + * We don't hold the partition locks, so we don't get a consistent + * snapshot across all buffers, but we do grab the buffer header + * locks, so the information of each buffer is self-consistent. */ for (i = 0; i < NBuffers; i++) { @@ -177,16 +174,6 @@ pg_buffercache_pages(PG_FUNCTION_ARGS) UnlockBufHdr(bufHdr, buf_state); } - - /* - * And release locks. We do this in reverse order for two reasons: - * (1) Anyone else who needs more than one of the locks will be trying - * to lock them in increasing order; we don't want to release the - * other process until it can get all the locks it needs. (2) This - * avoids O(N^2) behavior inside LWLockRelease. - */ - for (i = NUM_BUFFER_PARTITIONS; --i >= 0;) - LWLockRelease(BufMappingPartitionLockByIndex(i)); } funcctx = SRF_PERCALL_SETUP(); diff --git a/contrib/pg_freespacemap/Makefile b/contrib/pg_freespacemap/Makefile index 7bc0e9555d..0a2f000ec6 100644 --- a/contrib/pg_freespacemap/Makefile +++ b/contrib/pg_freespacemap/Makefile @@ -4,8 +4,8 @@ MODULE_big = pg_freespacemap OBJS = pg_freespacemap.o $(WIN32RES) EXTENSION = pg_freespacemap -DATA = pg_freespacemap--1.1.sql pg_freespacemap--1.0--1.1.sql \ - pg_freespacemap--unpackaged--1.0.sql +DATA = pg_freespacemap--1.1.sql pg_freespacemap--1.1--1.2.sql \ + pg_freespacemap--1.0--1.1.sql pg_freespacemap--unpackaged--1.0.sql PGFILEDESC = "pg_freespacemap - monitoring of free space map" ifdef USE_PGXS diff --git a/contrib/pg_freespacemap/pg_freespacemap--1.1--1.2.sql b/contrib/pg_freespacemap/pg_freespacemap--1.1--1.2.sql new file mode 100644 index 0000000000..f558defadd --- /dev/null +++ b/contrib/pg_freespacemap/pg_freespacemap--1.1--1.2.sql @@ -0,0 +1,7 @@ +/* contrib/pg_freespacemap/pg_freespacemap--1.1--1.2.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_freespacemap UPDATE TO '1.2'" to load this file. \quit + +GRANT EXECUTE ON FUNCTION pg_freespace(regclass, bigint) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_freespace(regclass) TO pg_stat_scan_tables; diff --git a/contrib/pg_freespacemap/pg_freespacemap.control b/contrib/pg_freespacemap/pg_freespacemap.control index 764db30d18..ac8fc5050a 100644 --- a/contrib/pg_freespacemap/pg_freespacemap.control +++ b/contrib/pg_freespacemap/pg_freespacemap.control @@ -1,5 +1,5 @@ # pg_freespacemap extension comment = 'examine the free space map (FSM)' -default_version = '1.1' +default_version = '1.2' module_pathname = '$libdir/pg_freespacemap' relocatable = true diff --git a/contrib/pg_prewarm/pg_prewarm.c b/contrib/pg_prewarm/pg_prewarm.c index c3a518cfc2..78d71ab078 100644 --- a/contrib/pg_prewarm/pg_prewarm.c +++ b/contrib/pg_prewarm/pg_prewarm.c @@ -3,7 +3,7 @@ * pg_prewarm.c * prewarming utilities * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pg_prewarm/pg_prewarm.c @@ -79,7 +79,7 @@ pg_prewarm(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("prewarm type cannot be null")))); - type = PG_GETARG_TEXT_P(1); + type = PG_GETARG_TEXT_PP(1); ttype = text_to_cstring(type); if (strcmp(ttype, "prefetch") == 0) ptype = PREWARM_PREFETCH; @@ -99,7 +99,7 @@ pg_prewarm(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errmsg("relation fork cannot be null")))); - forkName = PG_GETARG_TEXT_P(2); + forkName = PG_GETARG_TEXT_PP(2); forkString = text_to_cstring(forkName); forkNumber = forkname_to_number(forkString); diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c index 5eac2b1e49..c37eaa395d 100644 --- a/contrib/pg_standby/pg_standby.c +++ b/contrib/pg_standby/pg_standby.c @@ -57,7 +57,7 @@ char *xlogFilePath; /* where we are going to restore to */ char *nextWALFileName; /* the file we need to get from archive */ char *restartWALFileName; /* the file from which we can restart restore */ char *priorWALFileName; /* the file we need to get from archive */ -char WALFilePath[MAXPGPATH]; /* the file path including archive */ +char WALFilePath[MAXPGPATH * 2]; /* the file path including archive */ char restoreCommand[MAXPGPATH]; /* run this to restore */ char exclusiveCleanupFileName[MAXFNAMELEN]; /* the file we need to * get from archive */ @@ -259,9 +259,9 @@ CustomizableCleanupPriorWALFiles(void) strcmp(xlde->d_name + 8, exclusiveCleanupFileName + 8) < 0) { #ifdef WIN32 - snprintf(WALFilePath, MAXPGPATH, "%s\\%s", archiveLocation, xlde->d_name); + snprintf(WALFilePath, sizeof(WALFilePath), "%s\\%s", archiveLocation, xlde->d_name); #else - snprintf(WALFilePath, MAXPGPATH, "%s/%s", archiveLocation, xlde->d_name); + snprintf(WALFilePath, sizeof(WALFilePath), "%s/%s", archiveLocation, xlde->d_name); #endif if (debug) @@ -632,7 +632,7 @@ main(int argc, char **argv) } break; case 't': /* Trigger file */ - triggerPath = strdup(optarg); + triggerPath = pg_strdup(optarg); break; case 'w': /* Max wait time */ maxwaittime = atoi(optarg); @@ -779,7 +779,7 @@ main(int argc, char **argv) { /* * Once we have restored this file successfully we can remove some - * prior WAL files. If this restore fails we musn't remove any + * prior WAL files. If this restore fails we mustn't remove any * file because some of them will be requested again immediately * after the failed restore, or when we restart recovery. */ diff --git a/contrib/pg_stat_statements/.gitignore b/contrib/pg_stat_statements/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/contrib/pg_stat_statements/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile index ddcdb10b92..39b368b70e 100644 --- a/contrib/pg_stat_statements/Makefile +++ b/contrib/pg_stat_statements/Makefile @@ -4,13 +4,20 @@ MODULE_big = pg_stat_statements OBJS = pg_stat_statements.o $(WIN32RES) EXTENSION = pg_stat_statements -DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.3--1.4.sql \ - pg_stat_statements--1.2--1.3.sql pg_stat_statements--1.1--1.2.sql \ - pg_stat_statements--1.0--1.1.sql pg_stat_statements--unpackaged--1.0.sql +DATA = pg_stat_statements--1.4.sql pg_stat_statements--1.4--1.5.sql \ + pg_stat_statements--1.3--1.4.sql pg_stat_statements--1.2--1.3.sql \ + pg_stat_statements--1.1--1.2.sql pg_stat_statements--1.0--1.1.sql \ + pg_stat_statements--unpackaged--1.0.sql PGFILEDESC = "pg_stat_statements - execution statistics of SQL statements" LDFLAGS_SL += $(filter -lm, $(LIBS)) +REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_stat_statements/pg_stat_statements.conf +REGRESS = pg_stat_statements +# Disabled because these tests require "shared_preload_libraries=pg_stat_statements", +# which typical installcheck users do not have (e.g. buildfarm clients). +NO_INSTALLCHECK = 1 + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/pg_stat_statements/expected/pg_stat_statements.out b/contrib/pg_stat_statements/expected/pg_stat_statements.out new file mode 100644 index 0000000000..5318c3550c --- /dev/null +++ b/contrib/pg_stat_statements/expected/pg_stat_statements.out @@ -0,0 +1,398 @@ +CREATE EXTENSION pg_stat_statements; +-- +-- simple and compound statements +-- +SET pg_stat_statements.track_utility = FALSE; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +SELECT 1 AS "int"; + int +----- + 1 +(1 row) + +SELECT 'hello' + -- multiline + AS "text"; + text +------- + hello +(1 row) + +SELECT 'world' AS "text"; + text +------- + world +(1 row) + +-- transaction +BEGIN; +SELECT 1 AS "int"; + int +----- + 1 +(1 row) + +SELECT 'hello' AS "text"; + text +------- + hello +(1 row) + +COMMIT; +-- compound transaction +BEGIN \; +SELECT 2.0 AS "float" \; +SELECT 'world' AS "text" \; +COMMIT; +-- compound with empty statements and spurious leading spacing +\;\; SELECT 3 + 3 \;\;\; SELECT ' ' || ' !' \;\; SELECT 1 + 4 \;; + ?column? +---------- + 5 +(1 row) + +-- non ;-terminated statements +SELECT 1 + 1 + 1 AS "add" \gset +SELECT :add + 1 + 1 AS "add" \; +SELECT :add + 1 + 1 AS "add" \gset +-- set operator +SELECT 1 AS i UNION SELECT 2 ORDER BY i; + i +--- + 1 + 2 +(2 rows) + +-- ? operator +select '{"a":1, "b":2}'::jsonb ? 'b'; + ?column? +---------- + t +(1 row) + +-- cte +WITH t(f) AS ( + VALUES (1.0), (2.0) +) + SELECT f FROM t ORDER BY f; + f +----- + 1.0 + 2.0 +(2 rows) + +-- prepared statement with parameter +PREPARE pgss_test (int) AS SELECT $1, 'test' LIMIT 1; +EXECUTE pgss_test(1); + ?column? | ?column? +----------+---------- + 1 | test +(1 row) + +DEALLOCATE pgss_test; +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls | rows +---------------------------------------------------+-------+------ + PREPARE pgss_test (int) AS SELECT $1, $2 LIMIT $3 | 1 | 1 + SELECT $1 +| 4 | 4 + +| | + AS "text" | | + SELECT $1 + $2 | 2 | 2 + SELECT $1 + $2 + $3 AS "add" | 3 | 3 + SELECT $1 AS "float" | 1 | 1 + SELECT $1 AS "int" | 2 | 2 + SELECT $1 AS i UNION SELECT $2 ORDER BY i | 1 | 2 + SELECT $1 || $2 | 1 | 1 + SELECT pg_stat_statements_reset() | 1 | 1 + WITH t(f) AS ( +| 1 | 2 + VALUES ($1), ($2) +| | + ) +| | + SELECT f FROM t ORDER BY f | | + select $1::jsonb ? $2 | 1 | 1 +(11 rows) + +-- +-- CRUD: INSERT SELECT UPDATE DELETE on test table +-- +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- utility "create table" should not be shown +CREATE TEMP TABLE test (a int, b char(20)); +INSERT INTO test VALUES(generate_series(1, 10), 'aaa'); +UPDATE test SET b = 'bbb' WHERE a > 7; +DELETE FROM test WHERE a > 9; +-- explicit transaction +BEGIN; +UPDATE test SET b = '111' WHERE a = 1 ; +COMMIT; +BEGIN \; +UPDATE test SET b = '222' WHERE a = 2 \; +COMMIT ; +UPDATE test SET b = '333' WHERE a = 3 \; +UPDATE test SET b = '444' WHERE a = 4 ; +BEGIN \; +UPDATE test SET b = '555' WHERE a = 5 \; +UPDATE test SET b = '666' WHERE a = 6 \; +COMMIT ; +-- many INSERT values +INSERT INTO test (a, b) VALUES (1, 'a'), (2, 'b'), (3, 'c'); +-- SELECT with constants +SELECT * FROM test WHERE a > 5 ORDER BY a ; + a | b +---+---------------------- + 6 | 666 + 7 | aaa + 8 | bbb + 9 | bbb +(4 rows) + +SELECT * + FROM test + WHERE a > 9 + ORDER BY a ; + a | b +---+--- +(0 rows) + +-- SELECT without constants +SELECT * FROM test ORDER BY a; + a | b +---+---------------------- + 1 | a + 1 | 111 + 2 | b + 2 | 222 + 3 | c + 3 | 333 + 4 | 444 + 5 | 555 + 6 | 666 + 7 | aaa + 8 | bbb + 9 | bbb +(12 rows) + +-- SELECT with IN clause +SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5); + a | b +---+---------------------- + 1 | 111 + 2 | 222 + 3 | 333 + 4 | 444 + 5 | 555 + 1 | a + 2 | b + 3 | c +(8 rows) + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls | rows +-------------------------------------------------------------+-------+------ + DELETE FROM test WHERE a > $1 | 1 | 1 + INSERT INTO test (a, b) VALUES ($1, $2), ($3, $4), ($5, $6) | 1 | 3 + INSERT INTO test VALUES(generate_series($1, $2), $3) | 1 | 10 + SELECT * FROM test ORDER BY a | 1 | 12 + SELECT * FROM test WHERE a > $1 ORDER BY a | 2 | 4 + SELECT * FROM test WHERE a IN ($1, $2, $3, $4, $5) | 1 | 8 + SELECT pg_stat_statements_reset() | 1 | 1 + UPDATE test SET b = $1 WHERE a = $2 | 6 | 6 + UPDATE test SET b = $1 WHERE a > $2 | 1 | 3 +(9 rows) + +-- +-- pg_stat_statements.track = none +-- +SET pg_stat_statements.track = 'none'; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +SELECT 1 AS "one"; + one +----- + 1 +(1 row) + +SELECT 1 + 1 AS "two"; + two +----- + 2 +(1 row) + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls | rows +-------+-------+------ +(0 rows) + +-- +-- pg_stat_statements.track = top +-- +SET pg_stat_statements.track = 'top'; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; +$$; +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(3); + plus_two +---------- + 5 +(1 row) + +SELECT PLUS_TWO(7); + plus_two +---------- + 9 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(8); + plus_one +---------- + 9 +(1 row) + +SELECT PLUS_ONE(10); + plus_one +---------- + 11 +(1 row) + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls | rows +-----------------------------------+-------+------ + SELECT $1::TEXT | 1 | 1 + SELECT PLUS_ONE($1) | 2 | 2 + SELECT PLUS_TWO($1) | 2 | 2 + SELECT pg_stat_statements_reset() | 1 | 1 +(4 rows) + +-- +-- pg_stat_statements.track = all +-- +SET pg_stat_statements.track = 'all'; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; +SELECT PLUS_TWO(-1); + plus_two +---------- + 1 +(1 row) + +SELECT PLUS_TWO(2); + plus_two +---------- + 4 +(1 row) + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; +SELECT PLUS_ONE(3); + plus_one +---------- + 4 +(1 row) + +SELECT PLUS_ONE(1); + plus_one +---------- + 2 +(1 row) + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls | rows +-----------------------------------+-------+------ + SELECT (i + $2 + $3)::INTEGER | 2 | 2 + SELECT (i + $2)::INTEGER LIMIT $3 | 2 | 2 + SELECT PLUS_ONE($1) | 2 | 2 + SELECT PLUS_TWO($1) | 2 | 2 + SELECT pg_stat_statements_reset() | 1 | 1 +(5 rows) + +-- +-- utility commands +-- +SET pg_stat_statements.track_utility = TRUE; +SELECT pg_stat_statements_reset(); + pg_stat_statements_reset +-------------------------- + +(1 row) + +SELECT 1; + ?column? +---------- + 1 +(1 row) + +CREATE INDEX test_b ON test(b); +DROP TABLE test \; +DROP TABLE IF EXISTS test \; +DROP FUNCTION PLUS_ONE(INTEGER); +NOTICE: table "test" does not exist, skipping +DROP TABLE IF EXISTS test \; +DROP TABLE IF EXISTS test \; +DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER); +NOTICE: table "test" does not exist, skipping +NOTICE: table "test" does not exist, skipping +NOTICE: function plus_one(pg_catalog.int4) does not exist, skipping +DROP FUNCTION PLUS_TWO(INTEGER); +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + query | calls | rows +-------------------------------------------+-------+------ + CREATE INDEX test_b ON test(b) | 1 | 0 + DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER) | 1 | 0 + DROP FUNCTION PLUS_ONE(INTEGER) | 1 | 0 + DROP FUNCTION PLUS_TWO(INTEGER) | 1 | 0 + DROP TABLE IF EXISTS test | 3 | 0 + DROP TABLE test | 1 | 0 + SELECT $1 | 1 | 1 + SELECT pg_stat_statements_reset() | 1 | 1 +(8 rows) + +DROP EXTENSION pg_stat_statements; diff --git a/contrib/pg_stat_statements/pg_stat_statements--1.4--1.5.sql b/contrib/pg_stat_statements/pg_stat_statements--1.4--1.5.sql new file mode 100644 index 0000000000..9c76122a2b --- /dev/null +++ b/contrib/pg_stat_statements/pg_stat_statements--1.4--1.5.sql @@ -0,0 +1,6 @@ +/* contrib/pg_stat_statements/pg_stat_statements--1.4--1.5.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_stat_statements UPDATE TO '1.5'" to load this file. \quit + +GRANT EXECUTE ON FUNCTION pg_stat_statements_reset() TO pg_read_all_stats; diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index cc56d80bb5..3c35604b5d 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -27,10 +27,10 @@ * to blame query costs on the proper queryId. * * To facilitate presenting entries to users, we create "representative" query - * strings in which constants are replaced with '?' characters, to make it - * clearer what a normalized entry can represent. To save on shared memory, - * and to avoid having to truncate oversized query strings, we store these - * strings in a temporary external query-texts file. Offsets into this + * strings in which constants are replaced with parameter symbols ($n), to + * make it clearer what a normalized entry can represent. To save on shared + * memory, and to avoid having to truncate oversized query strings, we store + * these strings in a temporary external query-texts file. Offsets into this * file are kept in shared memory. * * Note about locking issues: to create or delete an entry in the shared @@ -48,7 +48,7 @@ * in the file to be read or written while holding only shared lock. * * - * Copyright (c) 2008-2016, PostgreSQL Global Development Group + * Copyright (c) 2008-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pg_stat_statements/pg_stat_statements.c @@ -62,6 +62,7 @@ #include <unistd.h> #include "access/hash.h" +#include "catalog/pg_authid.h" #include "executor/instrument.h" #include "funcapi.h" #include "mb/pg_wchar.h" @@ -69,6 +70,7 @@ #include "parser/analyze.h" #include "parser/parsetree.h" #include "parser/scanner.h" +#include "parser/scansup.h" #include "pgstat.h" #include "storage/fd.h" #include "storage/ipc.h" @@ -138,7 +140,7 @@ typedef struct Counters { int64 calls; /* # of times executed */ double total_time; /* total execution time, in msec */ - double min_time; /* minimim execution time in msec */ + double min_time; /* minimum execution time in msec */ double max_time; /* maximum execution time in msec */ double mean_time; /* mean execution time in msec */ double sum_var_time; /* sum of variances in execution time in msec */ @@ -218,6 +220,9 @@ typedef struct pgssJumbleState /* Current number of valid entries in clocations array */ int clocations_count; + + /* highest Param id we've seen, in order to start normalization correctly */ + int highest_extern_param_id; } pgssJumbleState; /*---- Local variables ----*/ @@ -289,11 +294,12 @@ static void pgss_post_parse_analyze(ParseState *pstate, Query *query); static void pgss_ExecutorStart(QueryDesc *queryDesc, int eflags); static void pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, - uint64 count); + uint64 count, bool execute_once); static void pgss_ExecutorFinish(QueryDesc *queryDesc); static void pgss_ExecutorEnd(QueryDesc *queryDesc); -static void pgss_ProcessUtility(Node *parsetree, const char *queryString, +static void pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, + QueryEnvironment *queryEnv, DestReceiver *dest, #ifdef PGXC bool sentToRemote, @@ -301,8 +307,9 @@ static void pgss_ProcessUtility(Node *parsetree, const char *queryString, char *completionTag); static uint32 pgss_hash_fn(const void *key, Size keysize); static int pgss_match_fn(const void *key1, const void *key2, Size keysize); -static uint32 pgss_hash_string(const char *str); +static uint32 pgss_hash_string(const char *str, int len); static void pgss_store(const char *query, uint32 queryId, + int query_location, int query_len, double total_time, uint64 rows, const BufferUsage *bufusage, pgssJumbleState *jstate); @@ -328,8 +335,9 @@ static void JumbleRangeTable(pgssJumbleState *jstate, List *rtable); static void JumbleExpr(pgssJumbleState *jstate, Node *node); static void RecordConstLocation(pgssJumbleState *jstate, int location); static char *generate_normalized_query(pgssJumbleState *jstate, const char *query, - int *query_len_p, int encoding); -static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query); + int query_loc, int *query_len_p, int encoding); +static void fill_in_constant_lengths(pgssJumbleState *jstate, const char *query, + int query_loc); static int comp_location(const void *a, const void *b); @@ -804,6 +812,7 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query) jstate.clocations = (pgssLocationLen *) palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen)); jstate.clocations_count = 0; + jstate.highest_extern_param_id = 0; /* Compute query ID and mark the Query node with it */ JumbleQuery(&jstate, query); @@ -826,6 +835,8 @@ pgss_post_parse_analyze(ParseState *pstate, Query *query) if (jstate.clocations_count > 0) pgss_store(pstate->p_sourcetext, query->queryId, + query->stmt_location, + query->stmt_len, 0, 0, NULL, @@ -870,15 +881,16 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags) * ExecutorRun hook: all we need do is track nesting depth */ static void -pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count) +pgss_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, uint64 count, + bool execute_once) { nested_level++; PG_TRY(); { if (prev_ExecutorRun) - prev_ExecutorRun(queryDesc, direction, count); + prev_ExecutorRun(queryDesc, direction, count, execute_once); else - standard_ExecutorRun(queryDesc, direction, count); + standard_ExecutorRun(queryDesc, direction, count, execute_once); nested_level--; } PG_CATCH(); @@ -930,6 +942,8 @@ pgss_ExecutorEnd(QueryDesc *queryDesc) pgss_store(queryDesc->sourceText, queryId, + queryDesc->plannedstmt->stmt_location, + queryDesc->plannedstmt->stmt_len, queryDesc->totaltime->total * 1000.0, /* convert to msec */ queryDesc->estate->es_processed, &queryDesc->totaltime->bufusage, @@ -946,14 +960,17 @@ pgss_ExecutorEnd(QueryDesc *queryDesc) * ProcessUtility hook */ static void -pgss_ProcessUtility(Node *parsetree, const char *queryString, +pgss_ProcessUtility(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, + QueryEnvironment *queryEnv, DestReceiver *dest, #ifdef PGXC bool sentToRemote, #endif /* PGXC */ char *completionTag) { + Node *parsetree = pstmt->utilityStmt; + /* * If it's an EXECUTE statement, we don't track it and don't increment the * nesting level. This allows the cycles to be charged to the underlying @@ -978,7 +995,6 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString, uint64 rows; BufferUsage bufusage_start, bufusage; - uint32 queryId; bufusage_start = pgBufferUsage; INSTR_TIME_SET_CURRENT(start); @@ -987,20 +1003,16 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString, PG_TRY(); { if (prev_ProcessUtility) - prev_ProcessUtility(parsetree, queryString, - context, params, + prev_ProcessUtility(pstmt, queryString, + context, params, queryEnv, dest, -#ifdef PGXC sentToRemote, -#endif /* PGXC */ completionTag); else - standard_ProcessUtility(parsetree, queryString, - context, params, + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, dest, -#ifdef PGXC sentToRemote, -#endif /* PGXC */ completionTag); nested_level--; } @@ -1047,11 +1059,10 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString, bufusage.blk_write_time = pgBufferUsage.blk_write_time; INSTR_TIME_SUBTRACT(bufusage.blk_write_time, bufusage_start.blk_write_time); - /* For utility statements, we just hash the query string directly */ - queryId = pgss_hash_string(queryString); - pgss_store(queryString, - queryId, + 0, /* signal that it's a utility stmt */ + pstmt->stmt_location, + pstmt->stmt_len, INSTR_TIME_GET_MILLISEC(duration), rows, &bufusage, @@ -1060,20 +1071,16 @@ pgss_ProcessUtility(Node *parsetree, const char *queryString, else { if (prev_ProcessUtility) - prev_ProcessUtility(parsetree, queryString, - context, params, + prev_ProcessUtility(pstmt, queryString, + context, params, queryEnv, dest, -#ifdef PGXC sentToRemote, -#endif /* PGXC */ completionTag); else - standard_ProcessUtility(parsetree, queryString, - context, params, + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, dest, -#ifdef PGXC sentToRemote, -#endif /* PGXC */ completionTag); } } @@ -1114,20 +1121,24 @@ pgss_match_fn(const void *key1, const void *key2, Size keysize) * utility statements. */ static uint32 -pgss_hash_string(const char *str) +pgss_hash_string(const char *str, int len) { - return hash_any((const unsigned char *) str, strlen(str)); + return hash_any((const unsigned char *) str, len); } /* * Store some statistics for a statement. * + * If queryId is 0 then this is a utility statement and we should compute + * a suitable queryId internally. + * * If jstate is not NULL then we're trying to create an entry for which * we have no statistics as yet; we just want to record the normalized * query string. total_time, rows, bufusage are ignored in this case. */ static void pgss_store(const char *query, uint32 queryId, + int query_location, int query_len, double total_time, uint64 rows, const BufferUsage *bufusage, pgssJumbleState *jstate) @@ -1136,7 +1147,6 @@ pgss_store(const char *query, uint32 queryId, pgssEntry *entry; char *norm_query = NULL; int encoding = GetDatabaseEncoding(); - int query_len; Assert(query != NULL); @@ -1144,7 +1154,43 @@ pgss_store(const char *query, uint32 queryId, if (!pgss || !pgss_hash) return; - query_len = strlen(query); + /* + * Confine our attention to the relevant part of the string, if the query + * is a portion of a multi-statement source string. + * + * First apply starting offset, unless it's -1 (unknown). + */ + if (query_location >= 0) + { + Assert(query_location <= strlen(query)); + query += query_location; + /* Length of 0 (or -1) means "rest of string" */ + if (query_len <= 0) + query_len = strlen(query); + else + Assert(query_len <= strlen(query)); + } + else + { + /* If query location is unknown, distrust query_len as well */ + query_location = 0; + query_len = strlen(query); + } + + /* + * Discard leading and trailing whitespace, too. Use scanner_isspace() + * not libc's isspace(), because we want to match the lexer's behavior. + */ + while (query_len > 0 && scanner_isspace(query[0])) + query++, query_location++, query_len--; + while (query_len > 0 && scanner_isspace(query[query_len - 1])) + query_len--; + + /* + * For utility statements, we just hash the query string to get an ID. + */ + if (queryId == 0) + queryId = pgss_hash_string(query, query_len); /* Set up key for hashtable search */ key.userid = GetUserId(); @@ -1175,6 +1221,7 @@ pgss_store(const char *query, uint32 queryId, { LWLockRelease(pgss->lock); norm_query = generate_normalized_query(jstate, query, + query_location, &query_len, encoding); LWLockAcquire(pgss->lock, LW_SHARED); @@ -1363,7 +1410,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo, MemoryContext per_query_ctx; MemoryContext oldcontext; Oid userid = GetUserId(); - bool is_superuser = superuser(); + bool is_allowed_role = false; char *qbuffer = NULL; Size qbuffer_size = 0; Size extent = 0; @@ -1371,6 +1418,9 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo, HASH_SEQ_STATUS hash_seq; pgssEntry *entry; + /* Superusers or members of pg_read_all_stats members are allowed */ + is_allowed_role = is_member_of_role(GetUserId(), DEFAULT_ROLE_READ_ALL_STATS); + /* hash table must exist already */ if (!pgss || !pgss_hash) ereport(ERROR, @@ -1513,7 +1563,7 @@ pg_stat_statements_internal(FunctionCallInfo fcinfo, values[i++] = ObjectIdGetDatum(entry->key.userid); values[i++] = ObjectIdGetDatum(entry->key.dbid); - if (is_superuser || entry->key.userid == userid) + if (is_allowed_role || entry->key.userid == userid) { if (api_version >= PGSS_V1_2) values[i++] = Int64GetDatumFast(queryid); @@ -1794,11 +1844,8 @@ entry_dealloc(void) } /* - * Given a null-terminated string, allocate a new entry in the external query - * text file and store the string there. - * - * Although we could compute the string length via strlen(), callers already - * have it handy, so we require them to pass it too. + * Given a query string (not necessarily null-terminated), allocate a new + * entry in the external query text file and store the string there. * * If successful, returns true, and stores the new entry's offset in the file * into *query_offset. Also, if gc_count isn't NULL, *gc_count is set to the @@ -1846,7 +1893,9 @@ qtext_store(const char *query, int query_len, if (lseek(fd, off, SEEK_SET) != off) goto error; - if (write(fd, query, query_len + 1) != query_len + 1) + if (write(fd, query, query_len) != query_len) + goto error; + if (write(fd, "\0", 1) != 1) goto error; CloseTransientFile(fd); @@ -2360,9 +2409,8 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable) foreach(lc, rtable) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); + RangeTblEntry *rte = lfirst_node(RangeTblEntry, lc); - Assert(IsA(rte, RangeTblEntry)); APP_JUMB(rte->rtekind); switch (rte->rtekind) { @@ -2379,6 +2427,9 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable) case RTE_FUNCTION: JumbleExpr(jstate, (Node *) rte->functions); break; + case RTE_TABLEFUNC: + JumbleExpr(jstate, (Node *) rte->tablefunc); + break; case RTE_VALUES: JumbleExpr(jstate, (Node *) rte->values_lists); break; @@ -2391,6 +2442,9 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable) APP_JUMB_STRING(rte->ctename); APP_JUMB(rte->ctelevelsup); break; + case RTE_NAMEDTUPLESTORE: + APP_JUMB_STRING(rte->enrname); + break; default: elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); break; @@ -2457,6 +2511,10 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) APP_JUMB(p->paramkind); APP_JUMB(p->paramid); APP_JUMB(p->paramtype); + /* Also, track the highest external Param id */ + if (p->paramkind == PARAM_EXTERN && + p->paramid > jstate->highest_extern_param_id) + jstate->highest_extern_param_id = p->paramid; } break; case T_Aggref: @@ -2548,7 +2606,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) APP_JUMB(sublink->subLinkType); APP_JUMB(sublink->subLinkId); JumbleExpr(jstate, (Node *) sublink->testexpr); - JumbleQuery(jstate, (Query *) sublink->subselect); + JumbleQuery(jstate, castNode(Query, sublink->subselect)); } break; case T_FieldSelect: @@ -2614,9 +2672,8 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) JumbleExpr(jstate, (Node *) caseexpr->arg); foreach(temp, caseexpr->args) { - CaseWhen *when = (CaseWhen *) lfirst(temp); + CaseWhen *when = lfirst_node(CaseWhen, temp); - Assert(IsA(when, CaseWhen)); JumbleExpr(jstate, (Node *) when->expr); JumbleExpr(jstate, (Node *) when->result); } @@ -2656,6 +2713,15 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) JumbleExpr(jstate, (Node *) mmexpr->args); } break; + case T_SQLValueFunction: + { + SQLValueFunction *svf = (SQLValueFunction *) node; + + APP_JUMB(svf->op); + /* type is fully determined by op */ + APP_JUMB(svf->typmod); + } + break; case T_XmlExpr: { XmlExpr *xexpr = (XmlExpr *) node; @@ -2819,7 +2885,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) /* we store the string name because RTE_CTE RTEs need it */ APP_JUMB_STRING(cte->ctename); - JumbleQuery(jstate, (Query *) cte->ctequery); + JumbleQuery(jstate, castNode(Query, cte->ctequery)); } break; case T_SetOperationStmt: @@ -2839,6 +2905,15 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) JumbleExpr(jstate, rtfunc->funcexpr); } break; + case T_TableFunc: + { + TableFunc *tablefunc = (TableFunc *) node; + + JumbleExpr(jstate, tablefunc->docexpr); + JumbleExpr(jstate, tablefunc->rowexpr); + JumbleExpr(jstate, (Node *) tablefunc->colexprs); + } + break; case T_TableSampleClause: { TableSampleClause *tsc = (TableSampleClause *) node; @@ -2890,18 +2965,25 @@ RecordConstLocation(pgssJumbleState *jstate, int location) * just which "equivalent" query is used to create the hashtable entry. * We assume this is OK. * + * If query_loc > 0, then "query" has been advanced by that much compared to + * the original string start, so we need to translate the provided locations + * to compensate. (This lets us avoid re-scanning statements before the one + * of interest, so it's worth doing.) + * * *query_len_p contains the input string length, and is updated with - * the result string length (which cannot be longer) on exit. + * the result string length on exit. The resulting string might be longer + * or shorter depending on what happens with replacement of constants. * * Returns a palloc'd string. */ static char * generate_normalized_query(pgssJumbleState *jstate, const char *query, - int *query_len_p, int encoding) + int query_loc, int *query_len_p, int encoding) { char *norm_query; int query_len = *query_len_p; int i, + norm_query_buflen, /* Space allowed for norm_query */ len_to_wrt, /* Length (in bytes) to write */ quer_loc = 0, /* Source query byte location */ n_quer_loc = 0, /* Normalized query byte location */ @@ -2912,10 +2994,19 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query, * Get constants' lengths (core system only gives us locations). Note * this also ensures the items are sorted by location. */ - fill_in_constant_lengths(jstate, query); + fill_in_constant_lengths(jstate, query, query_loc); + + /* + * Allow for $n symbols to be longer than the constants they replace. + * Constants must take at least one byte in text form, while a $n symbol + * certainly isn't more than 11 bytes, even if n reaches INT_MAX. We + * could refine that limit based on the max value of n for the current + * query, but it hardly seems worth any extra effort to do so. + */ + norm_query_buflen = query_len + jstate->clocations_count * 10; /* Allocate result buffer */ - norm_query = palloc(query_len + 1); + norm_query = palloc(norm_query_buflen + 1); for (i = 0; i < jstate->clocations_count; i++) { @@ -2923,6 +3014,9 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query, tok_len; /* Length (in bytes) of that tok */ off = jstate->clocations[i].location; + /* Adjust recorded location if we're dealing with partial string */ + off -= query_loc; + tok_len = jstate->clocations[i].length; if (tok_len < 0) @@ -2936,8 +3030,9 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query, memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); n_quer_loc += len_to_wrt; - /* And insert a '?' in place of the constant token */ - norm_query[n_quer_loc++] = '?'; + /* And insert a param symbol in place of the constant token */ + n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d", + i + 1 + jstate->highest_extern_param_id); quer_loc = off + tok_len; last_off = off; @@ -2954,7 +3049,7 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query, memcpy(norm_query + n_quer_loc, query + quer_loc, len_to_wrt); n_quer_loc += len_to_wrt; - Assert(n_quer_loc <= query_len); + Assert(n_quer_loc <= norm_query_buflen); norm_query[n_quer_loc] = '\0'; *query_len_p = n_quer_loc; @@ -2979,12 +3074,18 @@ generate_normalized_query(pgssJumbleState *jstate, const char *query, * marked as '-1', so that they are later ignored. (Actually, we assume the * lengths were initialized as -1 to start with, and don't change them here.) * + * If query_loc > 0, then "query" has been advanced by that much compared to + * the original string start, so we need to translate the provided locations + * to compensate. (This lets us avoid re-scanning statements before the one + * of interest, so it's worth doing.) + * * N.B. There is an assumption that a '-' character at a Const location begins * a negative numeric constant. This precludes there ever being another * reason for a constant to start with a '-'. */ static void -fill_in_constant_lengths(pgssJumbleState *jstate, const char *query) +fill_in_constant_lengths(pgssJumbleState *jstate, const char *query, + int query_loc) { pgssLocationLen *locs; core_yyscan_t yyscanner; @@ -3018,6 +3119,9 @@ fill_in_constant_lengths(pgssJumbleState *jstate, const char *query) int loc = locs[i].location; int tok; + /* Adjust recorded location if we're dealing with partial string */ + loc -= query_loc; + Assert(loc >= 0); if (loc <= last_loc) diff --git a/contrib/pg_stat_statements/pg_stat_statements.conf b/contrib/pg_stat_statements/pg_stat_statements.conf new file mode 100644 index 0000000000..13346e2807 --- /dev/null +++ b/contrib/pg_stat_statements/pg_stat_statements.conf @@ -0,0 +1 @@ +shared_preload_libraries = 'pg_stat_statements' diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control index 24038f56b1..193fcdfafa 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.control +++ b/contrib/pg_stat_statements/pg_stat_statements.control @@ -1,5 +1,5 @@ # pg_stat_statements extension comment = 'track execution statistics of all SQL statements executed' -default_version = '1.4' +default_version = '1.5' module_pathname = '$libdir/pg_stat_statements' relocatable = true diff --git a/contrib/pg_stat_statements/sql/pg_stat_statements.sql b/contrib/pg_stat_statements/sql/pg_stat_statements.sql new file mode 100644 index 0000000000..a8361fd1bf --- /dev/null +++ b/contrib/pg_stat_statements/sql/pg_stat_statements.sql @@ -0,0 +1,198 @@ +CREATE EXTENSION pg_stat_statements; + +-- +-- simple and compound statements +-- +SET pg_stat_statements.track_utility = FALSE; +SELECT pg_stat_statements_reset(); + +SELECT 1 AS "int"; + +SELECT 'hello' + -- multiline + AS "text"; + +SELECT 'world' AS "text"; + +-- transaction +BEGIN; +SELECT 1 AS "int"; +SELECT 'hello' AS "text"; +COMMIT; + +-- compound transaction +BEGIN \; +SELECT 2.0 AS "float" \; +SELECT 'world' AS "text" \; +COMMIT; + +-- compound with empty statements and spurious leading spacing +\;\; SELECT 3 + 3 \;\;\; SELECT ' ' || ' !' \;\; SELECT 1 + 4 \;; + +-- non ;-terminated statements +SELECT 1 + 1 + 1 AS "add" \gset +SELECT :add + 1 + 1 AS "add" \; +SELECT :add + 1 + 1 AS "add" \gset + +-- set operator +SELECT 1 AS i UNION SELECT 2 ORDER BY i; + +-- ? operator +select '{"a":1, "b":2}'::jsonb ? 'b'; + +-- cte +WITH t(f) AS ( + VALUES (1.0), (2.0) +) + SELECT f FROM t ORDER BY f; + +-- prepared statement with parameter +PREPARE pgss_test (int) AS SELECT $1, 'test' LIMIT 1; +EXECUTE pgss_test(1); +DEALLOCATE pgss_test; + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + +-- +-- CRUD: INSERT SELECT UPDATE DELETE on test table +-- +SELECT pg_stat_statements_reset(); + +-- utility "create table" should not be shown +CREATE TEMP TABLE test (a int, b char(20)); + +INSERT INTO test VALUES(generate_series(1, 10), 'aaa'); +UPDATE test SET b = 'bbb' WHERE a > 7; +DELETE FROM test WHERE a > 9; + +-- explicit transaction +BEGIN; +UPDATE test SET b = '111' WHERE a = 1 ; +COMMIT; + +BEGIN \; +UPDATE test SET b = '222' WHERE a = 2 \; +COMMIT ; + +UPDATE test SET b = '333' WHERE a = 3 \; +UPDATE test SET b = '444' WHERE a = 4 ; + +BEGIN \; +UPDATE test SET b = '555' WHERE a = 5 \; +UPDATE test SET b = '666' WHERE a = 6 \; +COMMIT ; + +-- many INSERT values +INSERT INTO test (a, b) VALUES (1, 'a'), (2, 'b'), (3, 'c'); + +-- SELECT with constants +SELECT * FROM test WHERE a > 5 ORDER BY a ; + +SELECT * + FROM test + WHERE a > 9 + ORDER BY a ; + +-- SELECT without constants +SELECT * FROM test ORDER BY a; + +-- SELECT with IN clause +SELECT * FROM test WHERE a IN (1, 2, 3, 4, 5); + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + +-- +-- pg_stat_statements.track = none +-- +SET pg_stat_statements.track = 'none'; +SELECT pg_stat_statements_reset(); + +SELECT 1 AS "one"; +SELECT 1 + 1 AS "two"; + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + +-- +-- pg_stat_statements.track = top +-- +SET pg_stat_statements.track = 'top'; +SELECT pg_stat_statements_reset(); + +DO LANGUAGE plpgsql $$ +BEGIN + -- this is a SELECT + PERFORM 'hello world'::TEXT; +END; +$$; + +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; + +SELECT PLUS_TWO(3); +SELECT PLUS_TWO(7); + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; + +SELECT PLUS_ONE(8); +SELECT PLUS_ONE(10); + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + +-- +-- pg_stat_statements.track = all +-- +SET pg_stat_statements.track = 'all'; +SELECT pg_stat_statements_reset(); + +-- we drop and recreate the functions to avoid any caching funnies +DROP FUNCTION PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); + +-- PL/pgSQL function +CREATE FUNCTION PLUS_TWO(i INTEGER) RETURNS INTEGER AS $$ +DECLARE + r INTEGER; +BEGIN + SELECT (i + 1 + 1.0)::INTEGER INTO r; + RETURN r; +END; $$ LANGUAGE plpgsql; + +SELECT PLUS_TWO(-1); +SELECT PLUS_TWO(2); + +-- SQL function --- use LIMIT to keep it from being inlined +CREATE FUNCTION PLUS_ONE(i INTEGER) RETURNS INTEGER AS +$$ SELECT (i + 1.0)::INTEGER LIMIT 1 $$ LANGUAGE SQL; + +SELECT PLUS_ONE(3); +SELECT PLUS_ONE(1); + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + +-- +-- utility commands +-- +SET pg_stat_statements.track_utility = TRUE; +SELECT pg_stat_statements_reset(); + +SELECT 1; +CREATE INDEX test_b ON test(b); +DROP TABLE test \; +DROP TABLE IF EXISTS test \; +DROP FUNCTION PLUS_ONE(INTEGER); +DROP TABLE IF EXISTS test \; +DROP TABLE IF EXISTS test \; +DROP FUNCTION IF EXISTS PLUS_ONE(INTEGER); +DROP FUNCTION PLUS_TWO(INTEGER); + +SELECT query, calls, rows FROM pg_stat_statements ORDER BY query COLLATE "C"; + +DROP EXTENSION pg_stat_statements; diff --git a/contrib/pg_trgm/expected/pg_trgm.out b/contrib/pg_trgm/expected/pg_trgm.out index a9782b658a..81ccc594d4 100644 --- a/contrib/pg_trgm/expected/pg_trgm.out +++ b/contrib/pg_trgm/expected/pg_trgm.out @@ -1,4 +1,12 @@ CREATE EXTENSION pg_trgm; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + select show_trgm(''); show_trgm ----------- @@ -1194,6 +1202,12 @@ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988 0.5 | qwertyu0987 (2 rows) +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + create index trgm_idx on test_trgm using gist (t gist_trgm_ops); set enable_seqscan=off; select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; @@ -2340,6 +2354,12 @@ select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988 0.5 | qwertyu0987 (2 rows) +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + drop index trgm_idx; create index trgm_idx on test_trgm using gin (t gin_trgm_ops); set enable_seqscan=off; @@ -3469,10 +3489,17 @@ select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu198 qwertyu0988 | 0.333333 (1 row) +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; + count +------- + 1000 +(1 row) + create table test2(t text COLLATE "C"); insert into test2 values ('abcdef'); insert into test2 values ('quark'); insert into test2 values (' z foo bar'); +insert into test2 values ('/123/-45/'); create index test2_idx_gin on test2 using gin (t gin_trgm_ops); set enable_seqscan=off; explain (costs off) @@ -3582,7 +3609,8 @@ select * from test2 where t ~ '(abc)*$'; abcdef quark z foo bar -(3 rows) + /123/-45/ +(4 rows) select * from test2 where t ~* 'DEF'; t @@ -3668,6 +3696,18 @@ select * from test2 where t ~ ' z foo'; z foo bar (1 row) +select * from test2 where t ~ 'qua(?!foo)'; + t +------- + quark +(1 row) + +select * from test2 where t ~ '/\d+/-\d'; + t +----------- + /123/-45/ +(1 row) + drop index test2_idx_gin; create index test2_idx_gist on test2 using gist (t gist_trgm_ops); set enable_seqscan=off; @@ -3770,7 +3810,8 @@ select * from test2 where t ~ '(abc)*$'; abcdef quark z foo bar -(3 rows) + /123/-45/ +(4 rows) select * from test2 where t ~* 'DEF'; t @@ -3856,6 +3897,18 @@ select * from test2 where t ~ ' z foo'; z foo bar (1 row) +select * from test2 where t ~ 'qua(?!foo)'; + t +------- + quark +(1 row) + +select * from test2 where t ~ '/\d+/-\d'; + t +----------- + /123/-45/ +(1 row) + -- Check similarity threshold (bug #14202) CREATE TEMP TABLE restaurants (city text); INSERT INTO restaurants SELECT 'Warsaw' FROM generate_series(1, 10000); diff --git a/contrib/pg_trgm/sql/pg_trgm.sql b/contrib/pg_trgm/sql/pg_trgm.sql index 244946bb8d..fe8d0a7495 100644 --- a/contrib/pg_trgm/sql/pg_trgm.sql +++ b/contrib/pg_trgm/sql/pg_trgm.sql @@ -1,5 +1,10 @@ CREATE EXTENSION pg_trgm; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + select show_trgm(''); select show_trgm('(*&^$@%@'); select show_trgm('a b c'); @@ -21,6 +26,7 @@ select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu098 select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; create index trgm_idx on test_trgm using gist (t gist_trgm_ops); set enable_seqscan=off; @@ -31,6 +37,7 @@ select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu198 explain (costs off) select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; select t <-> 'q0987wertyu0988', t from test_trgm order by t <-> 'q0987wertyu0988' limit 2; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; drop index trgm_idx; create index trgm_idx on test_trgm using gin (t gin_trgm_ops); @@ -39,11 +46,13 @@ set enable_seqscan=off; select t,similarity(t,'qwertyu0988') as sml from test_trgm where t % 'qwertyu0988' order by sml desc, t; select t,similarity(t,'gwertyu0988') as sml from test_trgm where t % 'gwertyu0988' order by sml desc, t; select t,similarity(t,'gwertyu1988') as sml from test_trgm where t % 'gwertyu1988' order by sml desc, t; +select count(*) from test_trgm where t ~ '[qwerty]{2}-?[qwerty]{2}'; create table test2(t text COLLATE "C"); insert into test2 values ('abcdef'); insert into test2 values ('quark'); insert into test2 values (' z foo bar'); +insert into test2 values ('/123/-45/'); create index test2_idx_gin on test2 using gin (t gin_trgm_ops); set enable_seqscan=off; explain (costs off) @@ -78,7 +87,10 @@ select * from test2 where t ~ 'z foo bar'; select * from test2 where t ~ ' z foo bar'; select * from test2 where t ~ ' z foo bar'; select * from test2 where t ~ ' z foo'; +select * from test2 where t ~ 'qua(?!foo)'; +select * from test2 where t ~ '/\d+/-\d'; drop index test2_idx_gin; + create index test2_idx_gist on test2 using gist (t gist_trgm_ops); set enable_seqscan=off; explain (costs off) @@ -113,6 +125,8 @@ select * from test2 where t ~ 'z foo bar'; select * from test2 where t ~ ' z foo bar'; select * from test2 where t ~ ' z foo bar'; select * from test2 where t ~ ' z foo'; +select * from test2 where t ~ 'qua(?!foo)'; +select * from test2 where t ~ '/\d+/-\d'; -- Check similarity threshold (bug #14202) diff --git a/contrib/pg_trgm/trgm_gin.c b/contrib/pg_trgm/trgm_gin.c index ead33ef544..e4b3daea44 100644 --- a/contrib/pg_trgm/trgm_gin.c +++ b/contrib/pg_trgm/trgm_gin.c @@ -35,7 +35,7 @@ gin_extract_trgm(PG_FUNCTION_ARGS) Datum gin_extract_value_trgm(PG_FUNCTION_ARGS) { - text *val = (text *) PG_GETARG_TEXT_P(0); + text *val = (text *) PG_GETARG_TEXT_PP(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); Datum *entries = NULL; TRGM *trg; @@ -43,7 +43,7 @@ gin_extract_value_trgm(PG_FUNCTION_ARGS) *nentries = 0; - trg = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); + trg = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val)); trglen = ARRNELEM(trg); if (trglen > 0) @@ -70,7 +70,7 @@ gin_extract_value_trgm(PG_FUNCTION_ARGS) Datum gin_extract_query_trgm(PG_FUNCTION_ARGS) { - text *val = (text *) PG_GETARG_TEXT_P(0); + text *val = (text *) PG_GETARG_TEXT_PP(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); @@ -90,7 +90,7 @@ gin_extract_query_trgm(PG_FUNCTION_ARGS) { case SimilarityStrategyNumber: case WordSimilarityStrategyNumber: - trg = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); + trg = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val)); break; case ILikeStrategyNumber: #ifndef IGNORECASE @@ -103,7 +103,8 @@ gin_extract_query_trgm(PG_FUNCTION_ARGS) * For wildcard search we extract all the trigrams that every * potentially-matching string must include. */ - trg = generate_wildcard_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); + trg = generate_wildcard_trgm(VARDATA_ANY(val), + VARSIZE_ANY_EXHDR(val)); break; case RegExpICaseStrategyNumber: #ifndef IGNORECASE @@ -170,7 +171,7 @@ gin_trgm_consistent(PG_FUNCTION_ARGS) bool *check = (bool *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); - /* text *query = PG_GETARG_TEXT_P(2); */ + /* text *query = PG_GETARG_TEXT_PP(2); */ int32 nkeys = PG_GETARG_INT32(3); Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); bool *recheck = (bool *) PG_GETARG_POINTER(5); @@ -268,7 +269,7 @@ gin_trgm_triconsistent(PG_FUNCTION_ARGS) GinTernaryValue *check = (GinTernaryValue *) PG_GETARG_POINTER(0); StrategyNumber strategy = PG_GETARG_UINT16(1); - /* text *query = PG_GETARG_TEXT_P(2); */ + /* text *query = PG_GETARG_TEXT_PP(2); */ int32 nkeys = PG_GETARG_INT32(3); Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); GinTernaryValue res = GIN_MAYBE; diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c index f52867df32..ed02af875c 100644 --- a/contrib/pg_trgm/trgm_gist.c +++ b/contrib/pg_trgm/trgm_gist.c @@ -100,9 +100,9 @@ gtrgm_compress(PG_FUNCTION_ARGS) if (entry->leafkey) { /* trgm */ TRGM *res; - text *val = DatumGetTextP(entry->key); + text *val = DatumGetTextPP(entry->key); - res = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); + res = generate_trgm(VARDATA_ANY(val), VARSIZE_ANY_EXHDR(val)); retval = (GISTENTRY *) palloc(sizeof(GISTENTRY)); gistentryinit(*retval, PointerGetDatum(res), entry->rel, entry->page, @@ -142,7 +142,7 @@ gtrgm_decompress(PG_FUNCTION_ARGS) GISTENTRY *retval; text *key; - key = DatumGetTextP(entry->key); + key = DatumGetTextPP(entry->key); if (key != (text *) DatumGetPointer(entry->key)) { @@ -200,11 +200,12 @@ gtrgm_consistent(PG_FUNCTION_ARGS) * depends on strategy. * * The cached structure is a single palloc chunk containing the - * gtrgm_consistent_cache header, then the input query (starting at a - * MAXALIGN boundary), then the TRGM value (also starting at a MAXALIGN - * boundary). However we don't try to include the regex graph (if any) in - * that struct. (XXX currently, this approach can leak regex graphs - * across index rescans. Not clear if that's worth fixing.) + * gtrgm_consistent_cache header, then the input query (4-byte length + * word, uncompressed, starting at a MAXALIGN boundary), then the TRGM + * value (also starting at a MAXALIGN boundary). However we don't try to + * include the regex graph (if any) in that struct. (XXX currently, this + * approach can leak regex graphs across index rescans. Not clear if + * that's worth fixing.) */ cache = (gtrgm_consistent_cache *) fcinfo->flinfo->fn_extra; if (cache == NULL || diff --git a/contrib/pg_trgm/trgm_op.c b/contrib/pg_trgm/trgm_op.c index dd0f492cfa..e9a713113e 100644 --- a/contrib/pg_trgm/trgm_op.c +++ b/contrib/pg_trgm/trgm_op.c @@ -413,7 +413,7 @@ comp_ptrgm(const void *v1, const void *v2) * ulen1: count of unique trigrams of array "trg1". * len2: length of array "trg2" and array "trg2indexes". * len: length of the array "found". - * check_only: if true then only check existaince of similar search pattern in + * check_only: if true then only check existence of similar search pattern in * text. * * Returns word similarity. @@ -456,7 +456,7 @@ iterate_word_similarity(int *trg2indexes, lastpos[trgindex] = i; } - /* Adjust lower bound if this trigram is present in required substing */ + /* Adjust lower bound if this trigram is present in required substring */ if (found[trgindex]) { int prev_lower, @@ -547,7 +547,7 @@ iterate_word_similarity(int *trg2indexes, * * str1: search pattern string, of length slen1 bytes. * str2: text in which we are looking for a word, of length slen2 bytes. - * check_only: if true then only check existaince of similar search pattern in + * check_only: if true then only check existence of similar search pattern in * text. * * Returns word similarity. @@ -878,14 +878,14 @@ trgm2int(trgm *ptr) Datum show_trgm(PG_FUNCTION_ARGS) { - text *in = PG_GETARG_TEXT_P(0); + text *in = PG_GETARG_TEXT_PP(0); TRGM *trg; Datum *d; ArrayType *a; trgm *ptr; int i; - trg = generate_trgm(VARDATA(in), VARSIZE(in) - VARHDRSZ); + trg = generate_trgm(VARDATA_ANY(in), VARSIZE_ANY_EXHDR(in)); d = (Datum *) palloc(sizeof(Datum) * (1 + ARRNELEM(trg))); for (i = 0, ptr = GETARR(trg); i < ARRNELEM(trg); i++, ptr++) @@ -1053,14 +1053,14 @@ trgm_presence_map(TRGM *query, TRGM *key) Datum similarity(PG_FUNCTION_ARGS) { - text *in1 = PG_GETARG_TEXT_P(0); - text *in2 = PG_GETARG_TEXT_P(1); + text *in1 = PG_GETARG_TEXT_PP(0); + text *in2 = PG_GETARG_TEXT_PP(1); TRGM *trg1, *trg2; float4 res; - trg1 = generate_trgm(VARDATA(in1), VARSIZE(in1) - VARHDRSZ); - trg2 = generate_trgm(VARDATA(in2), VARSIZE(in2) - VARHDRSZ); + trg1 = generate_trgm(VARDATA_ANY(in1), VARSIZE_ANY_EXHDR(in1)); + trg2 = generate_trgm(VARDATA_ANY(in2), VARSIZE_ANY_EXHDR(in2)); res = cnt_sml(trg1, trg2, false); diff --git a/contrib/pg_trgm/trgm_regexp.c b/contrib/pg_trgm/trgm_regexp.c index 3f09a9c718..6ab2e49eb9 100644 --- a/contrib/pg_trgm/trgm_regexp.c +++ b/contrib/pg_trgm/trgm_regexp.c @@ -181,7 +181,7 @@ * 7) Mark state 3 final because state 5 of source NFA is marked as final. * * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION @@ -200,9 +200,10 @@ /* - * Uncomment to print intermediate stages, for exploring and debugging the - * algorithm implementation. This produces three graph files in /tmp, - * in Graphviz .dot format. + * Uncomment (or use -DTRGM_REGEXP_DEBUG) to print debug info, + * for exploring and debugging the algorithm implementation. + * This produces three graph files in /tmp, in Graphviz .dot format. + * Some progress information is also printed to postmaster stderr. */ /* #define TRGM_REGEXP_DEBUG */ @@ -226,7 +227,7 @@ * Penalty multipliers for trigram counts depending on whitespace contents. * Numbers based on analysis of real-life texts. */ -const float4 penalties[8] = { +static const float4 penalties[8] = { 1.0f, /* "aaa" */ 3.5f, /* "aa " */ 0.0f, /* "a a" (impossible) */ @@ -318,22 +319,27 @@ typedef struct * arcs - outgoing arcs of this state (List of TrgmArc) * enterKeys - enter keys reachable from this state without reading any * predictable trigram (List of TrgmStateKey) - * fin - flag indicating this state is final - * init - flag indicating this state is initial + * flags - flag bits + * snumber - number of this state (initially assigned as -1, -2, etc, + * for debugging purposes only; then at the packaging stage, + * surviving states are renumbered with positive numbers) * parent - parent state, if this state has been merged into another - * children - child states (states that have been merged into this one) - * number - number of this state (used at the packaging stage) + * tentFlags - flags this state would acquire via planned merges + * tentParent - planned parent state, if considering a merge */ +#define TSTATE_INIT 0x01 /* flag indicating this state is initial */ +#define TSTATE_FIN 0x02 /* flag indicating this state is final */ + typedef struct TrgmState { TrgmStateKey stateKey; /* hashtable key: must be first field */ List *arcs; List *enterKeys; - bool fin; - bool init; + int flags; + int snumber; struct TrgmState *parent; - List *children; - int number; + int tentFlags; + struct TrgmState *tentParent; } TrgmState; /* @@ -360,7 +366,7 @@ typedef struct * Information about color trigram (used in stage 3) * * ctrgm - trigram itself - * number - number of this trigram (used in the packaging stage) + * cnumber - number of this trigram (used in the packaging stage) * count - number of simple trigrams created from this color trigram * expanded - indicates this color trigram is expanded into simple trigrams * arcs - list of all arcs labeled with this color trigram. @@ -368,7 +374,7 @@ typedef struct typedef struct { ColorTrgm ctrgm; - int number; + int cnumber; int count; float4 penalty; bool expanded; @@ -402,6 +408,7 @@ typedef struct /* Expanded graph (stage 2) */ HTAB *states; TrgmState *initState; + int nstates; /* Workspace for stage 2 */ List *queue; @@ -529,9 +536,7 @@ createTrgmNFA(text *text_re, Oid collation, */ tmpcontext = AllocSetContextCreate(CurrentMemoryContext, "createTrgmNFA temporary context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); oldcontext = MemoryContextSwitchTo(tmpcontext); /* @@ -601,7 +606,7 @@ createTrgmNFAInternal(regex_t *regex, TrgmPackedGraph **graph, * get from the initial state to the final state without reading any * predictable trigram. */ - if (trgmNFA.initState->fin) + if (trgmNFA.initState->flags & TSTATE_FIN) return NULL; /* @@ -919,6 +924,7 @@ transformGraph(TrgmNFA *trgmNFA) 1024, &hashCtl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + trgmNFA->nstates = 0; /* Create initial state: ambiguous prefix, NFA's initial state */ MemSet(&initkey, 0, sizeof(initkey)); @@ -927,7 +933,7 @@ transformGraph(TrgmNFA *trgmNFA) initkey.nstate = pg_reg_getinitialstate(trgmNFA->regex); initstate = getState(trgmNFA, &initkey); - initstate->init = true; + initstate->flags |= TSTATE_INIT; trgmNFA->initState = initstate; /* @@ -945,7 +951,7 @@ transformGraph(TrgmNFA *trgmNFA) * actual processing. */ if (trgmNFA->overflowed) - state->fin = true; + state->flags |= TSTATE_FIN; else processState(trgmNFA, state); @@ -970,7 +976,7 @@ processState(TrgmNFA *trgmNFA, TrgmState *state) * queue is empty. But we can quit if the state gets marked final. */ addKey(trgmNFA, state, &state->stateKey); - while (trgmNFA->keysQueue != NIL && !state->fin) + while (trgmNFA->keysQueue != NIL && !(state->flags & TSTATE_FIN)) { TrgmStateKey *key = (TrgmStateKey *) linitial(trgmNFA->keysQueue); @@ -982,7 +988,7 @@ processState(TrgmNFA *trgmNFA, TrgmState *state) * Add outgoing arcs only if state isn't final (we have no interest in * outgoing arcs if we already match) */ - if (!state->fin) + if (!(state->flags & TSTATE_FIN)) addArcs(trgmNFA, state); } @@ -991,7 +997,7 @@ processState(TrgmNFA *trgmNFA, TrgmState *state) * whether this should result in any further enter keys being added. * If so, add those keys to keysQueue so that processState will handle them. * - * If the enter key is for the NFA's final state, set state->fin = TRUE. + * If the enter key is for the NFA's final state, mark state as TSTATE_FIN. * This situation means that we can reach the final state from this expanded * state without reading any predictable trigram, so we must consider this * state as an accepting one. @@ -1061,7 +1067,7 @@ addKey(TrgmNFA *trgmNFA, TrgmState *state, TrgmStateKey *key) /* If state is now known final, mark it and we're done */ if (key->nstate == pg_reg_getfinalstate(trgmNFA->regex)) { - state->fin = true; + state->flags |= TSTATE_FIN; return; } @@ -1387,11 +1393,12 @@ getState(TrgmNFA *trgmNFA, TrgmStateKey *key) /* New state: initialize and queue it */ state->arcs = NIL; state->enterKeys = NIL; - state->init = false; - state->fin = false; + state->flags = 0; + /* states are initially given negative numbers */ + state->snumber = -(++trgmNFA->nstates); state->parent = NULL; - state->children = NIL; - state->number = -1; + state->tentFlags = 0; + state->tentParent = NULL; trgmNFA->queue = lappend(trgmNFA->queue, state); } @@ -1456,10 +1463,10 @@ selectColorTrigrams(TrgmNFA *trgmNFA) ColorTrgmInfo *colorTrgms; int64 totalTrgmCount; float4 totalTrgmPenalty; - int number; + int cnumber; /* Collect color trigrams from all arcs */ - colorTrgms = (ColorTrgmInfo *) palloc(sizeof(ColorTrgmInfo) * arcsCount); + colorTrgms = (ColorTrgmInfo *) palloc0(sizeof(ColorTrgmInfo) * arcsCount); trgmNFA->colorTrgms = colorTrgms; i = 0; @@ -1472,12 +1479,15 @@ selectColorTrigrams(TrgmNFA *trgmNFA) { TrgmArc *arc = (TrgmArc *) lfirst(cell); TrgmArcInfo *arcInfo = (TrgmArcInfo *) palloc(sizeof(TrgmArcInfo)); + ColorTrgmInfo *trgmInfo = &colorTrgms[i]; arcInfo->source = state; arcInfo->target = arc->target; - colorTrgms[i].arcs = list_make1(arcInfo); - colorTrgms[i].expanded = true; - colorTrgms[i].ctrgm = arc->ctrgm; + trgmInfo->ctrgm = arc->ctrgm; + trgmInfo->cnumber = -1; + /* count and penalty will be set below */ + trgmInfo->expanded = true; + trgmInfo->arcs = list_make1(arcInfo); i++; } } @@ -1575,6 +1585,15 @@ selectColorTrigrams(TrgmNFA *trgmNFA) if (totalTrgmPenalty <= WISH_TRGM_PENALTY) break; +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, "considering ctrgm %d %d %d, penalty %f, %d arcs\n", + trgmInfo->ctrgm.colors[0], + trgmInfo->ctrgm.colors[1], + trgmInfo->ctrgm.colors[2], + trgmInfo->penalty, + list_length(trgmInfo->arcs)); +#endif + /* * Does any arc of this color trigram connect initial and final * states? If so we can't remove it. @@ -1584,6 +1603,14 @@ selectColorTrigrams(TrgmNFA *trgmNFA) TrgmArcInfo *arcInfo = (TrgmArcInfo *) lfirst(cell); TrgmState *source = arcInfo->source, *target = arcInfo->target; + int source_flags, + target_flags; + +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, "examining arc to s%d (%x) from s%d (%x)\n", + -target->snumber, target->flags, + -source->snumber, source->flags); +#endif /* examine parent states, if any merging has already happened */ while (source->parent) @@ -1591,15 +1618,98 @@ selectColorTrigrams(TrgmNFA *trgmNFA) while (target->parent) target = target->parent; - if ((source->init || target->init) && - (source->fin || target->fin)) +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... after completed merges: to s%d (%x) from s%d (%x)\n", + -target->snumber, target->flags, + -source->snumber, source->flags); +#endif + + /* we must also consider merges we are planning right now */ + source_flags = source->flags | source->tentFlags; + while (source->tentParent) + { + source = source->tentParent; + source_flags |= source->flags | source->tentFlags; + } + target_flags = target->flags | target->tentFlags; + while (target->tentParent) + { + target = target->tentParent; + target_flags |= target->flags | target->tentFlags; + } + +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... after tentative merges: to s%d (%x) from s%d (%x)\n", + -target->snumber, target_flags, + -source->snumber, source_flags); +#endif + + /* would fully-merged state have both INIT and FIN set? */ + if (((source_flags | target_flags) & (TSTATE_INIT | TSTATE_FIN)) == + (TSTATE_INIT | TSTATE_FIN)) { canRemove = false; break; } + + /* ok so far, so remember planned merge */ + if (source != target) + { +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... tentatively merging s%d into s%d\n", + -target->snumber, -source->snumber); +#endif + target->tentParent = source; + source->tentFlags |= target_flags; + } + } + + /* + * We must reset all the tentFlags/tentParent fields before + * continuing. tentFlags could only have become set in states that + * are the source or parent or tentative parent of one of the current + * arcs; likewise tentParent could only have become set in states that + * are the target or parent or tentative parent of one of the current + * arcs. There might be some overlap between those sets, but if we + * clear tentFlags in target states as well as source states, we + * should be okay even if we visit a state as target before visiting + * it as a source. + */ + foreach(cell, trgmInfo->arcs) + { + TrgmArcInfo *arcInfo = (TrgmArcInfo *) lfirst(cell); + TrgmState *source = arcInfo->source, + *target = arcInfo->target; + TrgmState *ttarget; + + /* no need to touch previously-merged states */ + while (source->parent) + source = source->parent; + while (target->parent) + target = target->parent; + + while (source) + { + source->tentFlags = 0; + source = source->tentParent; + } + + while ((ttarget = target->tentParent) != NULL) + { + target->tentParent = NULL; + target->tentFlags = 0; /* in case it was also a source */ + target = ttarget; + } } + + /* Now, move on if we can't drop this trigram */ if (!canRemove) + { +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, " ... not ok to merge\n"); +#endif continue; + } /* OK, merge states linked by each arc labeled by the trigram */ foreach(cell, trgmInfo->arcs) @@ -1613,7 +1723,16 @@ selectColorTrigrams(TrgmNFA *trgmNFA) while (target->parent) target = target->parent; if (source != target) + { +#ifdef TRGM_REGEXP_DEBUG + fprintf(stderr, "merging s%d into s%d\n", + -target->snumber, -source->snumber); +#endif mergeStates(source, target); + /* Assert we didn't merge initial and final states */ + Assert((source->flags & (TSTATE_INIT | TSTATE_FIN)) != + (TSTATE_INIT | TSTATE_FIN)); + } } /* Mark trigram unexpanded, and update totals */ @@ -1632,15 +1751,15 @@ selectColorTrigrams(TrgmNFA *trgmNFA) * Sort color trigrams by colors (will be useful for bsearch in packGraph) * and enumerate the color trigrams that are expanded. */ - number = 0; + cnumber = 0; qsort(colorTrgms, trgmNFA->colorTrgmsCount, sizeof(ColorTrgmInfo), colorTrgmInfoCmp); for (i = 0; i < trgmNFA->colorTrgmsCount; i++) { if (colorTrgms[i].expanded) { - colorTrgms[i].number = number; - number++; + colorTrgms[i].cnumber = cnumber; + cnumber++; } } @@ -1756,27 +1875,15 @@ fillTrgm(trgm *ptrgm, trgm_mb_char s[3]) static void mergeStates(TrgmState *state1, TrgmState *state2) { - ListCell *cell; - Assert(state1 != state2); Assert(!state1->parent); Assert(!state2->parent); - /* state1 absorbs state2's init/fin flags */ - state1->init |= state2->init; - state1->fin |= state2->fin; + /* state1 absorbs state2's flags */ + state1->flags |= state2->flags; - /* state2, and all its children, become children of state1 */ - foreach(cell, state2->children) - { - TrgmState *state = (TrgmState *) lfirst(cell); - - state->parent = state1; - } + /* state2, and indirectly all its children, become children of state1 */ state2->parent = state1; - state1->children = list_concat(state1->children, state2->children); - state1->children = lappend(state1->children, state2); - state2->children = NIL; } /* @@ -1823,7 +1930,7 @@ colorTrgmInfoPenaltyCmp(const void *p1, const void *p2) static TrgmPackedGraph * packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext) { - int number = 2, + int snumber = 2, arcIndex, arcsCount; HASH_SEQ_STATUS scan_status; @@ -1843,16 +1950,16 @@ packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext) while (state->parent) state = state->parent; - if (state->number < 0) + if (state->snumber < 0) { - if (state->init) - state->number = 0; - else if (state->fin) - state->number = 1; + if (state->flags & TSTATE_INIT) + state->snumber = 0; + else if (state->flags & TSTATE_FIN) + state->snumber = 1; else { - state->number = number; - number++; + state->snumber = snumber; + snumber++; } } } @@ -1878,7 +1985,7 @@ packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext) while (target->parent) target = target->parent; - if (source->number != target->number) + if (source->snumber != target->snumber) { ColorTrgmInfo *ctrgm; @@ -1890,9 +1997,9 @@ packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext) Assert(ctrgm != NULL); Assert(ctrgm->expanded); - arcs[arcIndex].sourceState = source->number; - arcs[arcIndex].targetState = target->number; - arcs[arcIndex].colorTrgm = ctrgm->number; + arcs[arcIndex].sourceState = source->snumber; + arcs[arcIndex].targetState = target->snumber; + arcs[arcIndex].colorTrgm = ctrgm->cnumber; arcIndex++; } } @@ -1938,13 +2045,13 @@ packGraph(TrgmNFA *trgmNFA, MemoryContext rcontext) } /* Pack states and arcs information */ - result->statesCount = number; + result->statesCount = snumber; result->states = (TrgmPackedState *) - MemoryContextAlloc(rcontext, number * sizeof(TrgmPackedState)); + MemoryContextAlloc(rcontext, snumber * sizeof(TrgmPackedState)); packedArcs = (TrgmPackedArc *) MemoryContextAlloc(rcontext, arcsCount * sizeof(TrgmPackedArc)); j = 0; - for (i = 0; i < number; i++) + for (i = 0; i < snumber; i++) { int cnt = 0; @@ -2110,10 +2217,10 @@ printTrgmNFA(TrgmNFA *trgmNFA) { ListCell *cell; - appendStringInfo(&buf, "s%p", (void *) state); - if (state->fin) + appendStringInfo(&buf, "s%d", -state->snumber); + if (state->flags & TSTATE_FIN) appendStringInfoString(&buf, " [shape = doublecircle]"); - if (state->init) + if (state->flags & TSTATE_INIT) initstate = state; appendStringInfo(&buf, " [label = \"%d\"]", state->stateKey.nstate); appendStringInfoString(&buf, ";\n"); @@ -2122,8 +2229,8 @@ printTrgmNFA(TrgmNFA *trgmNFA) { TrgmArc *arc = (TrgmArc *) lfirst(cell); - appendStringInfo(&buf, " s%p -> s%p [label = \"", - (void *) state, (void *) arc->target); + appendStringInfo(&buf, " s%d -> s%d [label = \"", + -state->snumber, -arc->target->snumber); printTrgmColor(&buf, arc->ctrgm.colors[0]); appendStringInfoChar(&buf, ' '); printTrgmColor(&buf, arc->ctrgm.colors[1]); @@ -2136,7 +2243,7 @@ printTrgmNFA(TrgmNFA *trgmNFA) if (initstate) { appendStringInfoString(&buf, " node [shape = point ]; initial;\n"); - appendStringInfo(&buf, " initial -> s%p;\n", (void *) initstate); + appendStringInfo(&buf, " initial -> s%d;\n", -initstate->snumber); } appendStringInfoString(&buf, "}\n"); diff --git a/contrib/pg_visibility/.gitignore b/contrib/pg_visibility/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/contrib/pg_visibility/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/pg_visibility/Makefile b/contrib/pg_visibility/Makefile index 379591a098..21d787ddf7 100644 --- a/contrib/pg_visibility/Makefile +++ b/contrib/pg_visibility/Makefile @@ -4,9 +4,12 @@ MODULE_big = pg_visibility OBJS = pg_visibility.o $(WIN32RES) EXTENSION = pg_visibility -DATA = pg_visibility--1.1.sql pg_visibility--1.0--1.1.sql +DATA = pg_visibility--1.1.sql pg_visibility--1.1--1.2.sql \ + pg_visibility--1.0--1.1.sql PGFILEDESC = "pg_visibility - page visibility information" +REGRESS = pg_visibility + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/pg_visibility/expected/pg_visibility.out b/contrib/pg_visibility/expected/pg_visibility.out new file mode 100644 index 0000000000..f0dcb897c4 --- /dev/null +++ b/contrib/pg_visibility/expected/pg_visibility.out @@ -0,0 +1,142 @@ +CREATE EXTENSION pg_visibility; +-- +-- check that using the module's functions with unsupported relations will fail +-- +-- partitioned tables (the parent ones) don't have visibility maps +create table test_partitioned (a int) partition by list (a); +-- these should all fail +select pg_visibility('test_partitioned', 0); +ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +select pg_visibility_map('test_partitioned'); +ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +select pg_visibility_map_summary('test_partitioned'); +ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +select pg_check_frozen('test_partitioned'); +ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +select pg_truncate_visibility_map('test_partitioned'); +ERROR: "test_partitioned" is not a table, materialized view, or TOAST table +create table test_partition partition of test_partitioned for values in (1); +create index test_index on test_partition (a); +-- indexes do not, so these all fail +select pg_visibility('test_index', 0); +ERROR: "test_index" is not a table, materialized view, or TOAST table +select pg_visibility_map('test_index'); +ERROR: "test_index" is not a table, materialized view, or TOAST table +select pg_visibility_map_summary('test_index'); +ERROR: "test_index" is not a table, materialized view, or TOAST table +select pg_check_frozen('test_index'); +ERROR: "test_index" is not a table, materialized view, or TOAST table +select pg_truncate_visibility_map('test_index'); +ERROR: "test_index" is not a table, materialized view, or TOAST table +create view test_view as select 1; +-- views do not have VMs, so these all fail +select pg_visibility('test_view', 0); +ERROR: "test_view" is not a table, materialized view, or TOAST table +select pg_visibility_map('test_view'); +ERROR: "test_view" is not a table, materialized view, or TOAST table +select pg_visibility_map_summary('test_view'); +ERROR: "test_view" is not a table, materialized view, or TOAST table +select pg_check_frozen('test_view'); +ERROR: "test_view" is not a table, materialized view, or TOAST table +select pg_truncate_visibility_map('test_view'); +ERROR: "test_view" is not a table, materialized view, or TOAST table +create sequence test_sequence; +-- sequences do not have VMs, so these all fail +select pg_visibility('test_sequence', 0); +ERROR: "test_sequence" is not a table, materialized view, or TOAST table +select pg_visibility_map('test_sequence'); +ERROR: "test_sequence" is not a table, materialized view, or TOAST table +select pg_visibility_map_summary('test_sequence'); +ERROR: "test_sequence" is not a table, materialized view, or TOAST table +select pg_check_frozen('test_sequence'); +ERROR: "test_sequence" is not a table, materialized view, or TOAST table +select pg_truncate_visibility_map('test_sequence'); +ERROR: "test_sequence" is not a table, materialized view, or TOAST table +create foreign data wrapper dummy; +create server dummy_server foreign data wrapper dummy; +create foreign table test_foreign_table () server dummy_server; +-- foreign tables do not have VMs, so these all fail +select pg_visibility('test_foreign_table', 0); +ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +select pg_visibility_map('test_foreign_table'); +ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +select pg_visibility_map_summary('test_foreign_table'); +ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +select pg_check_frozen('test_foreign_table'); +ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +select pg_truncate_visibility_map('test_foreign_table'); +ERROR: "test_foreign_table" is not a table, materialized view, or TOAST table +-- check some of the allowed relkinds +create table regular_table (a int); +insert into regular_table values (1), (2); +vacuum regular_table; +select count(*) > 0 from pg_visibility('regular_table'); + ?column? +---------- + t +(1 row) + +truncate regular_table; +select count(*) > 0 from pg_visibility('regular_table'); + ?column? +---------- + f +(1 row) + +create materialized view matview_visibility_test as select * from regular_table; +vacuum matview_visibility_test; +select count(*) > 0 from pg_visibility('matview_visibility_test'); + ?column? +---------- + f +(1 row) + +insert into regular_table values (1), (2); +refresh materialized view matview_visibility_test; +select count(*) > 0 from pg_visibility('matview_visibility_test'); + ?column? +---------- + t +(1 row) + +-- regular tables which are part of a partition *do* have visibility maps +insert into test_partition values (1); +vacuum test_partition; +select count(*) > 0 from pg_visibility('test_partition', 0); + ?column? +---------- + t +(1 row) + +select count(*) > 0 from pg_visibility_map('test_partition'); + ?column? +---------- + t +(1 row) + +select count(*) > 0 from pg_visibility_map_summary('test_partition'); + ?column? +---------- + t +(1 row) + +select * from pg_check_frozen('test_partition'); -- hopefully none + t_ctid +-------- +(0 rows) + +select pg_truncate_visibility_map('test_partition'); + pg_truncate_visibility_map +---------------------------- + +(1 row) + +-- cleanup +drop table test_partitioned; +drop view test_view; +drop sequence test_sequence; +drop foreign table test_foreign_table; +drop server dummy_server; +drop foreign data wrapper dummy; +drop materialized view matview_visibility_test; +drop table regular_table; diff --git a/contrib/pg_visibility/pg_visibility--1.1--1.2.sql b/contrib/pg_visibility/pg_visibility--1.1--1.2.sql new file mode 100644 index 0000000000..a5a4fe7ca8 --- /dev/null +++ b/contrib/pg_visibility/pg_visibility--1.1--1.2.sql @@ -0,0 +1,13 @@ +/* contrib/pg_visibility/pg_visibility--1.1--1.2.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pg_visibility UPDATE TO '1.2'" to load this file. \quit + +-- Allow use of monitoring functions by pg_monitor members +GRANT EXECUTE ON FUNCTION pg_visibility_map(regclass, bigint) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_visibility(regclass, bigint) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_visibility_map(regclass) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_visibility(regclass) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_visibility_map_summary(regclass) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_check_frozen(regclass) TO pg_stat_scan_tables; +GRANT EXECUTE ON FUNCTION pg_check_visible(regclass) TO pg_stat_scan_tables; diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c index 7034066663..480f917d08 100644 --- a/contrib/pg_visibility/pg_visibility.c +++ b/contrib/pg_visibility/pg_visibility.c @@ -3,6 +3,8 @@ * pg_visibility.c * display visibility map information and page-level visibility bits * + * Copyright (c) 2016-2017, PostgreSQL Global Development Group + * * contrib/pg_visibility/pg_visibility.c *------------------------------------------------------------------------- */ @@ -51,9 +53,14 @@ static corrupt_items *collect_corrupt_items(Oid relid, bool all_visible, static void record_corrupt_item(corrupt_items *items, ItemPointer tid); static bool tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer); +static void check_relation_relkind(Relation rel); /* * Visibility map information for a single block of a relation. + * + * Note: the VM code will silently return zeroes for pages past the end + * of the map, so we allow probes up to MaxBlockNumber regardless of the + * actual relation size. */ Datum pg_visibility_map(PG_FUNCTION_ARGS) @@ -69,6 +76,9 @@ pg_visibility_map(PG_FUNCTION_ARGS) rel = relation_open(relid, AccessShareLock); + /* Only some relkinds have a visibility map */ + check_relation_relkind(rel); + if (blkno < 0 || blkno > MaxBlockNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -108,6 +118,9 @@ pg_visibility(PG_FUNCTION_ARGS) rel = relation_open(relid, AccessShareLock); + /* Only some relkinds have a visibility map */ + check_relation_relkind(rel); + if (blkno < 0 || blkno > MaxBlockNumber) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -122,13 +135,22 @@ pg_visibility(PG_FUNCTION_ARGS) values[0] = BoolGetDatum((mapbits & VISIBILITYMAP_ALL_VISIBLE) != 0); values[1] = BoolGetDatum((mapbits & VISIBILITYMAP_ALL_FROZEN) != 0); - buffer = ReadBuffer(rel, blkno); - LockBuffer(buffer, BUFFER_LOCK_SHARE); + /* Here we have to explicitly check rel size ... */ + if (blkno < RelationGetNumberOfBlocks(rel)) + { + buffer = ReadBuffer(rel, blkno); + LockBuffer(buffer, BUFFER_LOCK_SHARE); - page = BufferGetPage(buffer); - values[2] = BoolGetDatum(PageIsAllVisible(page)); + page = BufferGetPage(buffer); + values[2] = BoolGetDatum(PageIsAllVisible(page)); - UnlockReleaseBuffer(buffer); + UnlockReleaseBuffer(buffer); + } + else + { + /* As with the vismap, silently return 0 for pages past EOF */ + values[2] = BoolGetDatum(false); + } relation_close(rel, AccessShareLock); @@ -152,6 +174,7 @@ pg_visibility_map_rel(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = pg_visibility_tupdesc(true, false); + /* collect_visibility_data will verify the relkind */ funcctx->user_fctx = collect_visibility_data(relid, false); MemoryContextSwitchTo(oldcontext); } @@ -196,6 +219,7 @@ pg_visibility_rel(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); funcctx->tuple_desc = pg_visibility_tupdesc(true, true); + /* collect_visibility_data will verify the relkind */ funcctx->user_fctx = collect_visibility_data(relid, true); MemoryContextSwitchTo(oldcontext); } @@ -242,6 +266,10 @@ pg_visibility_map_summary(PG_FUNCTION_ARGS) bool nulls[2]; rel = relation_open(relid, AccessShareLock); + + /* Only some relkinds have a visibility map */ + check_relation_relkind(rel); + nblocks = RelationGetNumberOfBlocks(rel); for (blkno = 0; blkno < nblocks; ++blkno) @@ -294,6 +322,7 @@ pg_check_frozen(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + /* collect_corrupt_items will verify the relkind */ funcctx->user_fctx = collect_corrupt_items(relid, false, true); MemoryContextSwitchTo(oldcontext); } @@ -325,6 +354,7 @@ pg_check_visible(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + /* collect_corrupt_items will verify the relkind */ funcctx->user_fctx = collect_corrupt_items(relid, true, false); MemoryContextSwitchTo(oldcontext); } @@ -354,13 +384,8 @@ pg_truncate_visibility_map(PG_FUNCTION_ARGS) rel = relation_open(relid, AccessExclusiveLock); - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); + /* Only some relkinds have a visibility map */ + check_relation_relkind(rel); RelationOpenSmgr(rel); rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber; @@ -436,6 +461,9 @@ pg_visibility_tupdesc(bool include_blkno, bool include_pd) /* * Collect visibility data about a relation. + * + * Checks relkind of relid and will throw an error if the relation does not + * have a VM. */ static vbits * collect_visibility_data(Oid relid, bool include_pd) @@ -449,6 +477,9 @@ collect_visibility_data(Oid relid, bool include_pd) rel = relation_open(relid, AccessShareLock); + /* Only some relkinds have a visibility map */ + check_relation_relkind(rel); + nblocks = RelationGetNumberOfBlocks(rel); info = palloc0(offsetof(vbits, bits) +nblocks); info->next = 0; @@ -508,6 +539,9 @@ collect_visibility_data(Oid relid, bool include_pd) * * If all_frozen is passed as true, this will include all items which are * on pages marked as all-frozen but which do not seem to in fact be frozen. + * + * Checks relkind of relid and will throw an error if the relation does not + * have a VM. */ static corrupt_items * collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) @@ -523,18 +557,13 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) if (all_visible) { /* Don't pass rel; that will fail in recovery. */ - OldestXmin = GetOldestXmin(NULL, true); + OldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_VACUUM); } rel = relation_open(relid, AccessShareLock); - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW && - rel->rd_rel->relkind != RELKIND_TOASTVALUE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, materialized view, or TOAST table", - RelationGetRelationName(rel)))); + /* Only some relkinds have a visibility map */ + check_relation_relkind(rel); nblocks = RelationGetNumberOfBlocks(rel); @@ -611,14 +640,13 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) /* Dead line pointers are neither all-visible nor frozen. */ if (ItemIdIsDead(itemid)) { - ItemPointerData tid; - - ItemPointerSet(&tid, blkno, offnum); - record_corrupt_item(items, &tid); + ItemPointerSet(&(tuple.t_self), blkno, offnum); + record_corrupt_item(items, &tuple.t_self); continue; } /* Initialize a HeapTupleData structure for checks below. */ + ItemPointerSet(&(tuple.t_self), blkno, offnum); tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid); tuple.t_len = ItemIdGetLength(itemid); tuple.t_tableOid = relid; @@ -646,15 +674,15 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) * a buffer lock. And this shouldn't happen often, so it's * worth being careful so as to avoid false positives. */ - RecomputedOldestXmin = GetOldestXmin(NULL, true); + RecomputedOldestXmin = GetOldestXmin(NULL, PROCARRAY_FLAGS_VACUUM); if (!TransactionIdPrecedes(OldestXmin, RecomputedOldestXmin)) - record_corrupt_item(items, &tuple.t_data->t_ctid); + record_corrupt_item(items, &tuple.t_self); else { OldestXmin = RecomputedOldestXmin; if (!tuple_all_visible(&tuple, OldestXmin, buffer)) - record_corrupt_item(items, &tuple.t_data->t_ctid); + record_corrupt_item(items, &tuple.t_self); } } @@ -665,7 +693,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen) if (check_frozen) { if (heap_tuple_needs_eventual_freeze(tuple.t_data)) - record_corrupt_item(items, &tuple.t_data->t_ctid); + record_corrupt_item(items, &tuple.t_self); } } @@ -733,3 +761,19 @@ tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer) return true; } + +/* + * check_relation_relkind - convenience routine to check that relation + * is of the relkind supported by the callers + */ +static void +check_relation_relkind(Relation rel) +{ + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_MATVIEW && + rel->rd_rel->relkind != RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, materialized view, or TOAST table", + RelationGetRelationName(rel)))); +} diff --git a/contrib/pg_visibility/pg_visibility.control b/contrib/pg_visibility/pg_visibility.control index f93ed0176e..3cffa08b01 100644 --- a/contrib/pg_visibility/pg_visibility.control +++ b/contrib/pg_visibility/pg_visibility.control @@ -1,5 +1,5 @@ # pg_visibility extension comment = 'examine the visibility map (VM) and page-level visibility info' -default_version = '1.1' +default_version = '1.2' module_pathname = '$libdir/pg_visibility' relocatable = true diff --git a/contrib/pg_visibility/sql/pg_visibility.sql b/contrib/pg_visibility/sql/pg_visibility.sql new file mode 100644 index 0000000000..c2a7f1d9e4 --- /dev/null +++ b/contrib/pg_visibility/sql/pg_visibility.sql @@ -0,0 +1,83 @@ +CREATE EXTENSION pg_visibility; + +-- +-- check that using the module's functions with unsupported relations will fail +-- + +-- partitioned tables (the parent ones) don't have visibility maps +create table test_partitioned (a int) partition by list (a); +-- these should all fail +select pg_visibility('test_partitioned', 0); +select pg_visibility_map('test_partitioned'); +select pg_visibility_map_summary('test_partitioned'); +select pg_check_frozen('test_partitioned'); +select pg_truncate_visibility_map('test_partitioned'); + +create table test_partition partition of test_partitioned for values in (1); +create index test_index on test_partition (a); +-- indexes do not, so these all fail +select pg_visibility('test_index', 0); +select pg_visibility_map('test_index'); +select pg_visibility_map_summary('test_index'); +select pg_check_frozen('test_index'); +select pg_truncate_visibility_map('test_index'); + +create view test_view as select 1; +-- views do not have VMs, so these all fail +select pg_visibility('test_view', 0); +select pg_visibility_map('test_view'); +select pg_visibility_map_summary('test_view'); +select pg_check_frozen('test_view'); +select pg_truncate_visibility_map('test_view'); + +create sequence test_sequence; +-- sequences do not have VMs, so these all fail +select pg_visibility('test_sequence', 0); +select pg_visibility_map('test_sequence'); +select pg_visibility_map_summary('test_sequence'); +select pg_check_frozen('test_sequence'); +select pg_truncate_visibility_map('test_sequence'); + +create foreign data wrapper dummy; +create server dummy_server foreign data wrapper dummy; +create foreign table test_foreign_table () server dummy_server; +-- foreign tables do not have VMs, so these all fail +select pg_visibility('test_foreign_table', 0); +select pg_visibility_map('test_foreign_table'); +select pg_visibility_map_summary('test_foreign_table'); +select pg_check_frozen('test_foreign_table'); +select pg_truncate_visibility_map('test_foreign_table'); + +-- check some of the allowed relkinds +create table regular_table (a int); +insert into regular_table values (1), (2); +vacuum regular_table; +select count(*) > 0 from pg_visibility('regular_table'); +truncate regular_table; +select count(*) > 0 from pg_visibility('regular_table'); + +create materialized view matview_visibility_test as select * from regular_table; +vacuum matview_visibility_test; +select count(*) > 0 from pg_visibility('matview_visibility_test'); +insert into regular_table values (1), (2); +refresh materialized view matview_visibility_test; +select count(*) > 0 from pg_visibility('matview_visibility_test'); + +-- regular tables which are part of a partition *do* have visibility maps +insert into test_partition values (1); +vacuum test_partition; +select count(*) > 0 from pg_visibility('test_partition', 0); +select count(*) > 0 from pg_visibility_map('test_partition'); +select count(*) > 0 from pg_visibility_map_summary('test_partition'); +select * from pg_check_frozen('test_partition'); -- hopefully none +select pg_truncate_visibility_map('test_partition'); + +-- cleanup +drop table test_partitioned; +drop view test_view; +drop sequence test_sequence; +drop foreign table test_foreign_table; +drop server dummy_server; +drop foreign data wrapper dummy; +drop materialized view matview_visibility_test; +drop table regular_table; diff --git a/contrib/pgcrypto/Makefile b/contrib/pgcrypto/Makefile index 805db7626b..573bc6df79 100644 --- a/contrib/pgcrypto/Makefile +++ b/contrib/pgcrypto/Makefile @@ -1,7 +1,7 @@ # contrib/pgcrypto/Makefile -INT_SRCS = md5.c sha1.c sha2.c internal.c internal-sha2.c blf.c rijndael.c \ - fortuna.c random.c pgp-mpi-internal.c imath.c +INT_SRCS = md5.c sha1.c internal.c internal-sha2.c blf.c rijndael.c \ + pgp-mpi-internal.c imath.c INT_TESTS = sha2 OSSL_SRCS = openssl.c pgp-mpi-openssl.c diff --git a/contrib/pgcrypto/crypt-des.c b/contrib/pgcrypto/crypt-des.c index a4aa4966bf..44a5731dde 100644 --- a/contrib/pgcrypto/crypt-des.c +++ b/contrib/pgcrypto/crypt-des.c @@ -416,7 +416,7 @@ des_setkey(const char *key) && rawkey1 == old_rawkey1) { /* - * Already setup for this key. This optimisation fails on a zero key + * Already setup for this key. This optimization fails on a zero key * (which is weak and has bad parity anyway) in order to simplify the * starting conditions. */ diff --git a/contrib/pgcrypto/expected/pgp-compression_1.out b/contrib/pgcrypto/expected/pgp-compression_1.out new file mode 100644 index 0000000000..25d5c35bf7 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-compression_1.out @@ -0,0 +1,42 @@ +-- +-- PGP compression support +-- +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- + +ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+ +DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA== +=tbSn +-----END PGP MESSAGE----- +'), 'key', 'expect-compress-algo=1'); + pgp_sym_decrypt +----------------- + Secret message +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'), + 'key', 'expect-compress-algo=0'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'), + 'key', 'expect-compress-algo=1'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'), + 'key', 'expect-compress-algo=2'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- level=0 should turn compression off +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', + 'compress-algo=2, compress-level=0'), + 'key', 'expect-compress-algo=0'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random diff --git a/contrib/pgcrypto/expected/pgp-decrypt_1.out b/contrib/pgcrypto/expected/pgp-decrypt_1.out new file mode 100644 index 0000000000..d9e1e386a2 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-decrypt_1.out @@ -0,0 +1,424 @@ +-- +-- pgp_descrypt tests +-- +-- Checking ciphers +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.blowfish.sha1.mdc.s2k3.z0 + +jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS +yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE= +=JcP+ +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest +UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA== +=XtrP +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k3.z0 + +jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI +5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g== +=rCZt +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k3.z0 + +jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/ +lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog== +=fB6S +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking MDC modes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.nomdc.s2k3.z0 + +jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+ +u9YkgfJfsuRJmgQ9tmo= +=60ui +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE +8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q== +=moGf +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking hashes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.md5.mdc.s2k3.z0 + +jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO +KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA== +=NyLk +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m +/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw== +=FxbQ +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking S2K modes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k0.z0 + +jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw +Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw== +=YvkV +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k1.z0 + +jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK +4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz +=dbXm +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl +z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg== +=VJKg +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k0.z0 + +jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E +Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw== +=cg+i +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k1.z0 + +jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9 +7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt +=aHmC +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k3.z0 + +jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv +q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw== +=K0LS +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k0.z0 + +jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu +rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w== +=RGts +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k1.z0 + +jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO ++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR +=SUrU +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k3.z0 + +jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+ +4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA== +=XZrG +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking longer passwords +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi +tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw== +=XKKG +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6 +CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg== +=gWDh +-----END PGP MESSAGE----- +'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt +FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q== +=OxOF +-----END PGP MESSAGE----- +'), 'x'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking various data +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8 +Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg== +=W/ik +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); + encode +------------------------------------------ + 0225e3ede6f2587b076d021a189ff60aad67e066 +(1 row) + +-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066 +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat2.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB +SaV9L04ky1qECNDx3XjnoKLC+H7IOQ== +=Fxen +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); + encode +------------------------------------------ + da39a3ee5e6b4b0d3255bfef95601890afd80709 +(1 row) + +-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709 +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat3.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8 +gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk +73Hb8m1yRhQK +=ivrD +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); + encode +------------------------------------------ + 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c +(1 row) + +-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c +-- Checking CRLF +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: crlf mess + +ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms +a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs +=mBP9 +-----END PGP MESSAGE----- +'), 'key', 'convert-crlf=0'), 'sha1'), 'hex'); + encode +------------------------------------------ + 9353062be7720f1446d30b9e75573a4833886784 +(1 row) + +-- expected: 9353062be7720f1446d30b9e75573a4833886784 +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: crlf mess + +ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms +a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs +=mBP9 +-----END PGP MESSAGE----- +'), 'key', 'convert-crlf=1'), 'sha1'), 'hex'); + encode +------------------------------------------ + 7efefcab38467f7484d6fa43dc86cf5281bd78e2 +(1 row) + +-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2 +-- check BUG #11905, problem with messages 6 less than a power of 2. +select pgp_sym_decrypt(pgp_sym_encrypt(repeat('x',65530),'1'),'1') = repeat('x',65530); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- expected: true +-- Negative tests +-- Decryption with a certain incorrect key yields an apparent Literal Data +-- packet reporting its content to be binary data. Ciphertext source: +-- iterative pgp_sym_encrypt('secret', 'key') until the random prefix gave +-- rise to that property. +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- + +ww0EBwMCxf8PTrQBmJdl0jcB6y2joE7GSLKRv7trbNsF5Z8ou5NISLUg31llVH/S0B2wl4bvzZjV +VsxxqLSPzNLAeIspJk5G +=mSd/ +-----END PGP MESSAGE----- +'), 'wrong-key', 'debug=1'); +NOTICE: dbg: prefix_init: corrupt prefix +NOTICE: dbg: parse_literal_data: data type=b +NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr +ERROR: Wrong key or corrupt data +-- Routine text/binary mismatch. +select pgp_sym_decrypt(pgp_sym_encrypt_bytea('P', 'key'), 'key', 'debug=1'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- Decryption with a certain incorrect key yields an apparent BZip2-compressed +-- plaintext. Ciphertext source: iterative pgp_sym_encrypt('secret', 'key') +-- until the random prefix gave rise to that property. +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- + +ww0EBwMC9rK/dMkF5Zlt0jcBlzAQ1mQY2qYbKYbw8h3EZ5Jk0K2IiY92R82TRhWzBIF/8cmXDPtP +GXsd65oYJZp3Khz0qfyn +=Nmpq +-----END PGP MESSAGE----- +'), 'wrong-key', 'debug=1'); +NOTICE: dbg: prefix_init: corrupt prefix +NOTICE: dbg: parse_compressed_data: bzip2 unsupported +NOTICE: dbg: mdcbuf_finish: bad MDC pkt hdr +ERROR: Wrong key or corrupt data +-- Routine use of BZip2 compression. Ciphertext source: +-- echo x | gpg --homedir /nonexistent --personal-compress-preferences bzip2 \ +-- --personal-cipher-preferences aes --no-emit-version --batch \ +-- --symmetric --passphrase key --armor +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- + +jA0EBwMCRhFrAKNcLVJg0mMBLJG1cCASNk/x/3dt1zJ+2eo7jHfjgg3N6wpB3XIe +QCwkWJwlBG5pzbO5gu7xuPQN+TbPJ7aQ2sLx3bAHhtYb0i3vV9RO10Gw++yUyd4R +UCAAw2JRIISttRHMfDpDuZJpvYo= +=AZ9M +-----END PGP MESSAGE----- +'), 'key', 'debug=1'); +NOTICE: dbg: parse_compressed_data: bzip2 unsupported +ERROR: Unsupported compression algorithm diff --git a/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out b/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out deleted file mode 100644 index 4122300d97..0000000000 --- a/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out +++ /dev/null @@ -1 +0,0 @@ --- no random source diff --git a/contrib/pgcrypto/expected/pgp-encrypt_1.out b/contrib/pgcrypto/expected/pgp-encrypt_1.out new file mode 100644 index 0000000000..2291e662ec --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-encrypt_1.out @@ -0,0 +1,161 @@ +-- +-- PGP encrypt +-- +-- ensure consistent test output regardless of the default bytea format +SET bytea_output TO escape; +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- check whether the defaults are ok +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), + 'key', 'expect-cipher-algo=aes128, + expect-disable-mdc=0, + expect-sess-key=0, + expect-s2k-mode=3, + expect-s2k-digest-algo=sha1, + expect-compress-algo=0 + '); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- maybe the expect- stuff simply does not work +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), + 'key', 'expect-cipher-algo=bf, + expect-disable-mdc=1, + expect-sess-key=1, + expect-s2k-mode=0, + expect-s2k-digest-algo=md5, + expect-compress-algo=1 + '); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- bytea as text +select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- text as bytea +select pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- algorithm change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'), + 'key', 'expect-cipher-algo=bf'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'), + 'key', 'expect-cipher-algo=aes128'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'), + 'key', 'expect-cipher-algo=aes192'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- s2k change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'), + 'key', 'expect-s2k-mode=0'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'), + 'key', 'expect-s2k-mode=1'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'), + 'key', 'expect-s2k-mode=3'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- s2k count change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-count=1024'), + 'key', 'expect-s2k-count=1024'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- s2k_count rounds up +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-count=65000000'), + 'key', 'expect-s2k-count=65000000'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- s2k digest change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'), + 'key', 'expect-s2k-digest-algo=md5'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'), + 'key', 'expect-s2k-digest-algo=sha1'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- sess key +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'), + 'key', 'expect-sess-key=0'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'), + 'key', 'expect-sess-key=1'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'), + 'key', 'expect-sess-key=1, expect-cipher-algo=bf'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'), + 'key', 'expect-sess-key=1, expect-cipher-algo=aes192'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'), + 'key', 'expect-sess-key=1, expect-cipher-algo=aes256'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- no mdc +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'), + 'key', 'expect-disable-mdc=1'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- crlf +select encode(pgp_sym_decrypt_bytea( + pgp_sym_encrypt(E'1\n2\n3\r\n', 'key', 'convert-crlf=1'), + 'key'), 'hex'); +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- conversion should be lossless +select encode(digest(pgp_sym_decrypt( + pgp_sym_encrypt(E'\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'), + 'key', 'convert-crlf=1'), 'sha1'), 'hex') as result, + encode(digest(E'\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random diff --git a/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out b/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out deleted file mode 100644 index d35c0971ba..0000000000 --- a/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out +++ /dev/null @@ -1 +0,0 @@ --- no bignum support diff --git a/contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out b/contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out new file mode 100644 index 0000000000..3b1822ed91 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-pubkey-encrypt_1.out @@ -0,0 +1,62 @@ +-- +-- PGP Public Key Encryption +-- +-- ensure consistent test output regardless of the default bytea format +SET bytea_output TO escape; +-- successful encrypt/decrypt +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=2; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=3; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=6; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- try with rsa-sign only +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=4; +ERROR: No encryption key found +-- try with secret key +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(seckey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; +ERROR: Refusing to encrypt with secret key +-- does text-to-bytea works +select pgp_pub_decrypt_bytea( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random +-- and bytea-to-text? +select pgp_pub_decrypt( + pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; +ERROR: pg_random_bytes() is not supported by this build +DETAIL: This functionality requires a source of strong random numbers +HINT: You need to rebuild PostgreSQL using --enable-strong-random diff --git a/contrib/pgcrypto/fortuna.c b/contrib/pgcrypto/fortuna.c deleted file mode 100644 index 5028203479..0000000000 --- a/contrib/pgcrypto/fortuna.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * fortuna.c - * Fortuna-like PRNG. - * - * Copyright (c) 2005 Marko Kreen - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * contrib/pgcrypto/fortuna.c - */ - -#include "postgres.h" - -#include <sys/time.h> -#include <time.h> - -#include "px.h" -#include "rijndael.h" -#include "sha2.h" -#include "fortuna.h" - - -/* - * Why Fortuna-like: There does not seem to be any definitive reference - * on Fortuna in the net. Instead this implementation is based on - * following references: - * - * http://en.wikipedia.org/wiki/Fortuna_(PRNG) - * - Wikipedia article - * http://jlcooke.ca/random/ - * - Jean-Luc Cooke Fortuna-based /dev/random driver for Linux. - */ - -/* - * There is some confusion about whether and how to carry forward - * the state of the pools. Seems like original Fortuna does not - * do it, resetting hash after each request. I guess expecting - * feeding to happen more often that requesting. This is absolutely - * unsuitable for pgcrypto, as nothing asynchronous happens here. - * - * J.L. Cooke fixed this by feeding previous hash to new re-initialized - * hash context. - * - * Fortuna predecessor Yarrow requires ability to query intermediate - * 'final result' from hash, without affecting it. - * - * This implementation uses the Yarrow method - asking intermediate - * results, but continuing with old state. - */ - - -/* - * Algorithm parameters - */ - -/* - * How many pools. - * - * Original Fortuna uses 32 pools, that means 32'th pool is - * used not earlier than in 13th year. This is a waste in - * pgcrypto, as we have very low-frequancy seeding. Here - * is preferable to have all entropy usable in reasonable time. - * - * With 23 pools, 23th pool is used after 9 days which seems - * more sane. - * - * In our case the minimal cycle time would be bit longer - * than the system-randomness feeding frequency. - */ -#define NUM_POOLS 23 - -/* in microseconds */ -#define RESEED_INTERVAL 100000 /* 0.1 sec */ - -/* for one big request, reseed after this many bytes */ -#define RESEED_BYTES (1024*1024) - -/* - * Skip reseed if pool 0 has less than this many - * bytes added since last reseed. - */ -#define POOL0_FILL (256/8) - -/* - * Algorithm constants - */ - -/* Both cipher key size and hash result size */ -#define BLOCK 32 - -/* cipher block size */ -#define CIPH_BLOCK 16 - -/* for internal wrappers */ -#define MD_CTX SHA256_CTX -#define CIPH_CTX rijndael_ctx - -struct fortuna_state -{ - uint8 counter[CIPH_BLOCK]; - uint8 result[CIPH_BLOCK]; - uint8 key[BLOCK]; - MD_CTX pool[NUM_POOLS]; - CIPH_CTX ciph; - unsigned reseed_count; - struct timeval last_reseed_time; - unsigned pool0_bytes; - unsigned rnd_pos; - int tricks_done; -}; -typedef struct fortuna_state FState; - - -/* - * Use our own wrappers here. - * - Need to get intermediate result from digest, without affecting it. - * - Need re-set key on a cipher context. - * - Algorithms are guaranteed to exist. - * - No memory allocations. - */ - -static void -ciph_init(CIPH_CTX * ctx, const uint8 *key, int klen) -{ - rijndael_set_key(ctx, (const uint32 *) key, klen, 1); -} - -static void -ciph_encrypt(CIPH_CTX * ctx, const uint8 *in, uint8 *out) -{ - rijndael_encrypt(ctx, (const uint32 *) in, (uint32 *) out); -} - -static void -md_init(MD_CTX * ctx) -{ - SHA256_Init(ctx); -} - -static void -md_update(MD_CTX * ctx, const uint8 *data, int len) -{ - SHA256_Update(ctx, data, len); -} - -static void -md_result(MD_CTX * ctx, uint8 *dst) -{ - SHA256_CTX tmp; - - memcpy(&tmp, ctx, sizeof(*ctx)); - SHA256_Final(dst, &tmp); - px_memset(&tmp, 0, sizeof(tmp)); -} - -/* - * initialize state - */ -static void -init_state(FState *st) -{ - int i; - - memset(st, 0, sizeof(*st)); - for (i = 0; i < NUM_POOLS; i++) - md_init(&st->pool[i]); -} - -/* - * Endianess does not matter. - * It just needs to change without repeating. - */ -static void -inc_counter(FState *st) -{ - uint32 *val = (uint32 *) st->counter; - - if (++val[0]) - return; - if (++val[1]) - return; - if (++val[2]) - return; - ++val[3]; -} - -/* - * This is called 'cipher in counter mode'. - */ -static void -encrypt_counter(FState *st, uint8 *dst) -{ - ciph_encrypt(&st->ciph, st->counter, dst); - inc_counter(st); -} - - -/* - * The time between reseed must be at least RESEED_INTERVAL - * microseconds. - */ -static int -enough_time_passed(FState *st) -{ - int ok; - struct timeval tv; - struct timeval *last = &st->last_reseed_time; - - gettimeofday(&tv, NULL); - - /* check how much time has passed */ - ok = 0; - if (tv.tv_sec > last->tv_sec + 1) - ok = 1; - else if (tv.tv_sec == last->tv_sec + 1) - { - if (1000000 + tv.tv_usec - last->tv_usec >= RESEED_INTERVAL) - ok = 1; - } - else if (tv.tv_usec - last->tv_usec >= RESEED_INTERVAL) - ok = 1; - - /* reseed will happen, update last_reseed_time */ - if (ok) - memcpy(last, &tv, sizeof(tv)); - - px_memset(&tv, 0, sizeof(tv)); - - return ok; -} - -/* - * generate new key from all the pools - */ -static void -reseed(FState *st) -{ - unsigned k; - unsigned n; - MD_CTX key_md; - uint8 buf[BLOCK]; - - /* set pool as empty */ - st->pool0_bytes = 0; - - /* - * Both #0 and #1 reseed would use only pool 0. Just skip #0 then. - */ - n = ++st->reseed_count; - - /* - * The goal: use k-th pool only 1/(2^k) of the time. - */ - md_init(&key_md); - for (k = 0; k < NUM_POOLS; k++) - { - md_result(&st->pool[k], buf); - md_update(&key_md, buf, BLOCK); - - if (n & 1 || !n) - break; - n >>= 1; - } - - /* add old key into mix too */ - md_update(&key_md, st->key, BLOCK); - - /* now we have new key */ - md_result(&key_md, st->key); - - /* use new key */ - ciph_init(&st->ciph, st->key, BLOCK); - - px_memset(&key_md, 0, sizeof(key_md)); - px_memset(buf, 0, BLOCK); -} - -/* - * Pick a random pool. This uses key bytes as random source. - */ -static unsigned -get_rand_pool(FState *st) -{ - unsigned rnd; - - /* - * This slightly prefers lower pools - that is OK. - */ - rnd = st->key[st->rnd_pos] % NUM_POOLS; - - st->rnd_pos++; - if (st->rnd_pos >= BLOCK) - st->rnd_pos = 0; - - return rnd; -} - -/* - * update pools - */ -static void -add_entropy(FState *st, const uint8 *data, unsigned len) -{ - unsigned pos; - uint8 hash[BLOCK]; - MD_CTX md; - - /* hash given data */ - md_init(&md); - md_update(&md, data, len); - md_result(&md, hash); - - /* - * Make sure the pool 0 is initialized, then update randomly. - */ - if (st->reseed_count == 0) - pos = 0; - else - pos = get_rand_pool(st); - md_update(&st->pool[pos], hash, BLOCK); - - if (pos == 0) - st->pool0_bytes += len; - - px_memset(hash, 0, BLOCK); - px_memset(&md, 0, sizeof(md)); -} - -/* - * Just take 2 next blocks as new key - */ -static void -rekey(FState *st) -{ - encrypt_counter(st, st->key); - encrypt_counter(st, st->key + CIPH_BLOCK); - ciph_init(&st->ciph, st->key, BLOCK); -} - -/* - * Hide public constants. (counter, pools > 0) - * - * This can also be viewed as spreading the startup - * entropy over all of the components. - */ -static void -startup_tricks(FState *st) -{ - int i; - uint8 buf[BLOCK]; - - /* Use next block as counter. */ - encrypt_counter(st, st->counter); - - /* Now shuffle pools, excluding #0 */ - for (i = 1; i < NUM_POOLS; i++) - { - encrypt_counter(st, buf); - encrypt_counter(st, buf + CIPH_BLOCK); - md_update(&st->pool[i], buf, BLOCK); - } - px_memset(buf, 0, BLOCK); - - /* Hide the key. */ - rekey(st); - - /* This can be done only once. */ - st->tricks_done = 1; -} - -static void -extract_data(FState *st, unsigned count, uint8 *dst) -{ - unsigned n; - unsigned block_nr = 0; - - /* Should we reseed? */ - if (st->pool0_bytes >= POOL0_FILL || st->reseed_count == 0) - if (enough_time_passed(st)) - reseed(st); - - /* Do some randomization on first call */ - if (!st->tricks_done) - startup_tricks(st); - - while (count > 0) - { - /* produce bytes */ - encrypt_counter(st, st->result); - - /* copy result */ - if (count > CIPH_BLOCK) - n = CIPH_BLOCK; - else - n = count; - memcpy(dst, st->result, n); - dst += n; - count -= n; - - /* must not give out too many bytes with one key */ - block_nr++; - if (block_nr > (RESEED_BYTES / CIPH_BLOCK)) - { - rekey(st); - block_nr = 0; - } - } - /* Set new key for next request. */ - rekey(st); -} - -/* - * public interface - */ - -static FState main_state; -static int init_done = 0; - -void -fortuna_add_entropy(const uint8 *data, unsigned len) -{ - if (!init_done) - { - init_state(&main_state); - init_done = 1; - } - if (!data || !len) - return; - add_entropy(&main_state, data, len); -} - -void -fortuna_get_bytes(unsigned len, uint8 *dst) -{ - if (!init_done) - { - init_state(&main_state); - init_done = 1; - } - if (!dst || !len) - return; - extract_data(&main_state, len, dst); -} diff --git a/contrib/pgcrypto/fortuna.h b/contrib/pgcrypto/fortuna.h deleted file mode 100644 index bf9f4768d1..0000000000 --- a/contrib/pgcrypto/fortuna.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * fortuna.c - * Fortuna PRNG. - * - * Copyright (c) 2005 Marko Kreen - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * contrib/pgcrypto/fortuna.h - */ - -#ifndef __FORTUNA_H -#define __FORTUNA_H - -void fortuna_get_bytes(unsigned len, uint8 *dst); -void fortuna_add_entropy(const uint8 *data, unsigned len); - -#endif diff --git a/contrib/pgcrypto/internal-sha2.c b/contrib/pgcrypto/internal-sha2.c index 55ec7e16bd..e06f55445e 100644 --- a/contrib/pgcrypto/internal-sha2.c +++ b/contrib/pgcrypto/internal-sha2.c @@ -33,8 +33,8 @@ #include <time.h> +#include "common/sha2.h" #include "px.h" -#include "sha2.h" void init_sha224(PX_MD *h); void init_sha256(PX_MD *h); @@ -46,43 +46,43 @@ void init_sha512(PX_MD *h); static unsigned int_sha224_len(PX_MD *h) { - return SHA224_DIGEST_LENGTH; + return PG_SHA224_DIGEST_LENGTH; } static unsigned int_sha224_block_len(PX_MD *h) { - return SHA224_BLOCK_LENGTH; + return PG_SHA224_BLOCK_LENGTH; } static void int_sha224_update(PX_MD *h, const uint8 *data, unsigned dlen) { - SHA224_CTX *ctx = (SHA224_CTX *) h->p.ptr; + pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - SHA224_Update(ctx, data, dlen); + pg_sha224_update(ctx, data, dlen); } static void int_sha224_reset(PX_MD *h) { - SHA224_CTX *ctx = (SHA224_CTX *) h->p.ptr; + pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - SHA224_Init(ctx); + pg_sha224_init(ctx); } static void int_sha224_finish(PX_MD *h, uint8 *dst) { - SHA224_CTX *ctx = (SHA224_CTX *) h->p.ptr; + pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; - SHA224_Final(dst, ctx); + pg_sha224_final(ctx, dst); } static void int_sha224_free(PX_MD *h) { - SHA224_CTX *ctx = (SHA224_CTX *) h->p.ptr; + pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr; px_memset(ctx, 0, sizeof(*ctx)); px_free(ctx); @@ -94,43 +94,43 @@ int_sha224_free(PX_MD *h) static unsigned int_sha256_len(PX_MD *h) { - return SHA256_DIGEST_LENGTH; + return PG_SHA256_DIGEST_LENGTH; } static unsigned int_sha256_block_len(PX_MD *h) { - return SHA256_BLOCK_LENGTH; + return PG_SHA256_BLOCK_LENGTH; } static void int_sha256_update(PX_MD *h, const uint8 *data, unsigned dlen) { - SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - SHA256_Update(ctx, data, dlen); + pg_sha256_update(ctx, data, dlen); } static void int_sha256_reset(PX_MD *h) { - SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - SHA256_Init(ctx); + pg_sha256_init(ctx); } static void int_sha256_finish(PX_MD *h, uint8 *dst) { - SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; - SHA256_Final(dst, ctx); + pg_sha256_final(ctx, dst); } static void int_sha256_free(PX_MD *h) { - SHA256_CTX *ctx = (SHA256_CTX *) h->p.ptr; + pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr; px_memset(ctx, 0, sizeof(*ctx)); px_free(ctx); @@ -142,43 +142,43 @@ int_sha256_free(PX_MD *h) static unsigned int_sha384_len(PX_MD *h) { - return SHA384_DIGEST_LENGTH; + return PG_SHA384_DIGEST_LENGTH; } static unsigned int_sha384_block_len(PX_MD *h) { - return SHA384_BLOCK_LENGTH; + return PG_SHA384_BLOCK_LENGTH; } static void int_sha384_update(PX_MD *h, const uint8 *data, unsigned dlen) { - SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - SHA384_Update(ctx, data, dlen); + pg_sha384_update(ctx, data, dlen); } static void int_sha384_reset(PX_MD *h) { - SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - SHA384_Init(ctx); + pg_sha384_init(ctx); } static void int_sha384_finish(PX_MD *h, uint8 *dst) { - SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; - SHA384_Final(dst, ctx); + pg_sha384_final(ctx, dst); } static void int_sha384_free(PX_MD *h) { - SHA384_CTX *ctx = (SHA384_CTX *) h->p.ptr; + pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr; px_memset(ctx, 0, sizeof(*ctx)); px_free(ctx); @@ -190,43 +190,43 @@ int_sha384_free(PX_MD *h) static unsigned int_sha512_len(PX_MD *h) { - return SHA512_DIGEST_LENGTH; + return PG_SHA512_DIGEST_LENGTH; } static unsigned int_sha512_block_len(PX_MD *h) { - return SHA512_BLOCK_LENGTH; + return PG_SHA512_BLOCK_LENGTH; } static void int_sha512_update(PX_MD *h, const uint8 *data, unsigned dlen) { - SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; - SHA512_Update(ctx, data, dlen); + pg_sha512_update(ctx, data, dlen); } static void int_sha512_reset(PX_MD *h) { - SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; - SHA512_Init(ctx); + pg_sha512_init(ctx); } static void int_sha512_finish(PX_MD *h, uint8 *dst) { - SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; - SHA512_Final(dst, ctx); + pg_sha512_final(ctx, dst); } static void int_sha512_free(PX_MD *h) { - SHA512_CTX *ctx = (SHA512_CTX *) h->p.ptr; + pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr; px_memset(ctx, 0, sizeof(*ctx)); px_free(ctx); @@ -238,7 +238,7 @@ int_sha512_free(PX_MD *h) void init_sha224(PX_MD *md) { - SHA224_CTX *ctx; + pg_sha224_ctx *ctx; ctx = px_alloc(sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx)); @@ -258,7 +258,7 @@ init_sha224(PX_MD *md) void init_sha256(PX_MD *md) { - SHA256_CTX *ctx; + pg_sha256_ctx *ctx; ctx = px_alloc(sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx)); @@ -278,7 +278,7 @@ init_sha256(PX_MD *md) void init_sha384(PX_MD *md) { - SHA384_CTX *ctx; + pg_sha384_ctx *ctx; ctx = px_alloc(sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx)); @@ -298,7 +298,7 @@ init_sha384(PX_MD *md) void init_sha512(PX_MD *md) { - SHA512_CTX *ctx; + pg_sha512_ctx *ctx; ctx = px_alloc(sizeof(*ctx)); memset(ctx, 0, sizeof(*ctx)); diff --git a/contrib/pgcrypto/internal.c b/contrib/pgcrypto/internal.c index cb8ba2633d..2516092ba4 100644 --- a/contrib/pgcrypto/internal.c +++ b/contrib/pgcrypto/internal.c @@ -38,7 +38,6 @@ #include "sha1.h" #include "blf.h" #include "rijndael.h" -#include "fortuna.h" /* * System reseeds should be separated at least this much. @@ -615,74 +614,3 @@ px_find_cipher(const char *name, PX_Cipher **res) *res = c; return 0; } - -/* - * Randomness provider - */ - -/* - * Use always strong randomness. - */ -int -px_get_pseudo_random_bytes(uint8 *dst, unsigned count) -{ - return px_get_random_bytes(dst, count); -} - -static time_t seed_time = 0; -static time_t check_time = 0; - -static void -system_reseed(void) -{ - uint8 buf[1024]; - int n; - time_t t; - int skip = 1; - - t = time(NULL); - - if (seed_time == 0) - skip = 0; - else if ((t - seed_time) < SYSTEM_RESEED_MIN) - skip = 1; - else if ((t - seed_time) > SYSTEM_RESEED_MAX) - skip = 0; - else if (check_time == 0 || - (t - check_time) > SYSTEM_RESEED_CHECK_TIME) - { - check_time = t; - - /* roll dice */ - px_get_random_bytes(buf, 1); - skip = buf[0] >= SYSTEM_RESEED_CHANCE; - } - /* clear 1 byte */ - px_memset(buf, 0, sizeof(buf)); - - if (skip) - return; - - n = px_acquire_system_randomness(buf); - if (n > 0) - fortuna_add_entropy(buf, n); - - seed_time = t; - px_memset(buf, 0, sizeof(buf)); -} - -int -px_get_random_bytes(uint8 *dst, unsigned count) -{ - system_reseed(); - fortuna_get_bytes(count, dst); - return 0; -} - -int -px_add_entropy(const uint8 *data, unsigned count) -{ - system_reseed(); - fortuna_add_entropy(data, count); - return 0; -} diff --git a/contrib/pgcrypto/mbuf.c b/contrib/pgcrypto/mbuf.c index 44d9adcd2a..73dbfbd08f 100644 --- a/contrib/pgcrypto/mbuf.c +++ b/contrib/pgcrypto/mbuf.c @@ -311,7 +311,7 @@ pullf_read_max(PullFilter *pf, int len, uint8 **data_p, uint8 *tmpbuf) } /* - * caller wants exatly len bytes and dont bother with references + * caller wants exactly len bytes and don't bother with references */ int pullf_read_fixed(PullFilter *src, int len, uint8 *dst) diff --git a/contrib/pgcrypto/openssl.c b/contrib/pgcrypto/openssl.c index 976af70591..f71a933407 100644 --- a/contrib/pgcrypto/openssl.c +++ b/contrib/pgcrypto/openssl.c @@ -34,11 +34,11 @@ #include "px.h" #include <openssl/evp.h> -#include <openssl/blowfish.h> -#include <openssl/cast.h> -#include <openssl/des.h> -#include <openssl/rand.h> #include <openssl/err.h> +#include <openssl/rand.h> + +#include "utils/memutils.h" +#include "utils/resowner.h" /* * Max lengths we might want to handle. @@ -47,170 +47,76 @@ #define MAX_IV (128/8) /* - * Compatibility with OpenSSL 0.9.6 - * - * It needs AES and newer DES and digest API. - */ -#if OPENSSL_VERSION_NUMBER >= 0x00907000L - -/* - * Nothing needed for OpenSSL 0.9.7+ + * Hashes */ -#include <openssl/aes.h> -#else /* old OPENSSL */ - /* - * Emulate OpenSSL AES. + * To make sure we don't leak OpenSSL handles on abort, we keep OSSLDigest + * objects in a linked list, allocated in TopMemoryContext. We use the + * ResourceOwner mechanism to free them on abort. */ - -#include "rijndael.c" - -#define AES_ENCRYPT 1 -#define AES_DECRYPT 0 -#define AES_KEY rijndael_ctx - -static int -AES_set_encrypt_key(const uint8 *key, int kbits, AES_KEY *ctx) +typedef struct OSSLDigest { - aes_set_key(ctx, key, kbits, 1); - return 0; -} + const EVP_MD *algo; + EVP_MD_CTX *ctx; -static int -AES_set_decrypt_key(const uint8 *key, int kbits, AES_KEY *ctx) -{ - aes_set_key(ctx, key, kbits, 0); - return 0; -} + ResourceOwner owner; + struct OSSLDigest *next; + struct OSSLDigest *prev; +} OSSLDigest; -static void -AES_ecb_encrypt(const uint8 *src, uint8 *dst, AES_KEY *ctx, int enc) -{ - memcpy(dst, src, 16); - if (enc) - aes_ecb_encrypt(ctx, dst, 16); - else - aes_ecb_decrypt(ctx, dst, 16); -} +static OSSLDigest *open_digests = NULL; +static bool digest_resowner_callback_registered = false; static void -AES_cbc_encrypt(const uint8 *src, uint8 *dst, int len, AES_KEY *ctx, uint8 *iv, int enc) +free_openssl_digest(OSSLDigest *digest) { - memcpy(dst, src, len); - if (enc) - { - aes_cbc_encrypt(ctx, iv, dst, len); - memcpy(iv, dst + len - 16, 16); - } + EVP_MD_CTX_destroy(digest->ctx); + if (digest->prev) + digest->prev->next = digest->next; else - { - aes_cbc_decrypt(ctx, iv, dst, len); - memcpy(iv, src + len - 16, 16); - } + open_digests = digest->next; + if (digest->next) + digest->next->prev = digest->prev; + pfree(digest); } /* - * Emulate DES_* API - */ - -#define DES_key_schedule des_key_schedule -#define DES_cblock des_cblock -#define DES_set_key(k, ks) \ - des_set_key((k), *(ks)) -#define DES_ecb_encrypt(i, o, k, e) \ - des_ecb_encrypt((i), (o), *(k), (e)) -#define DES_ncbc_encrypt(i, o, l, k, iv, e) \ - des_ncbc_encrypt((i), (o), (l), *(k), (iv), (e)) -#define DES_ecb3_encrypt(i, o, k1, k2, k3, e) \ - des_ecb3_encrypt((des_cblock *)(i), (des_cblock *)(o), \ - *(k1), *(k2), *(k3), (e)) -#define DES_ede3_cbc_encrypt(i, o, l, k1, k2, k3, iv, e) \ - des_ede3_cbc_encrypt((i), (o), \ - (l), *(k1), *(k2), *(k3), (iv), (e)) - -/* - * Emulate newer digest API. + * Close any open OpenSSL handles on abort. */ - static void -EVP_MD_CTX_init(EVP_MD_CTX *ctx) +digest_free_callback(ResourceReleasePhase phase, + bool isCommit, + bool isTopLevel, + void *arg) { - memset(ctx, 0, sizeof(*ctx)); -} + OSSLDigest *curr; + OSSLDigest *next; -static int -EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) -{ - px_memset(ctx, 0, sizeof(*ctx)); - return 1; -} + if (phase != RESOURCE_RELEASE_AFTER_LOCKS) + return; -static int -EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, void *engine) -{ - EVP_DigestInit(ctx, md); - return 1; -} - -static int -EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *res, unsigned int *len) -{ - EVP_DigestFinal(ctx, res, len); - return 1; -} -#endif /* old OpenSSL */ - -/* - * Provide SHA2 for older OpenSSL < 0.9.8 - */ -#if OPENSSL_VERSION_NUMBER < 0x00908000L - -#include "sha2.c" -#include "internal-sha2.c" - -typedef void (*init_f) (PX_MD *md); - -static int -compat_find_digest(const char *name, PX_MD **res) -{ - init_f init = NULL; - - if (pg_strcasecmp(name, "sha224") == 0) - init = init_sha224; - else if (pg_strcasecmp(name, "sha256") == 0) - init = init_sha256; - else if (pg_strcasecmp(name, "sha384") == 0) - init = init_sha384; - else if (pg_strcasecmp(name, "sha512") == 0) - init = init_sha512; - else - return PXE_NO_HASH; + next = open_digests; + while (next) + { + curr = next; + next = curr->next; - *res = px_alloc(sizeof(PX_MD)); - init(*res); - return 0; + if (curr->owner == CurrentResourceOwner) + { + if (isCommit) + elog(WARNING, "pgcrypto digest reference leak: digest %p still referenced", curr); + free_openssl_digest(curr); + } + } } -#else -#define compat_find_digest(name, res) (PXE_NO_HASH) -#endif - -/* - * Hashes - */ - -typedef struct OSSLDigest -{ - const EVP_MD *algo; - EVP_MD_CTX ctx; -} OSSLDigest; static unsigned digest_result_size(PX_MD *h) { OSSLDigest *digest = (OSSLDigest *) h->p.ptr; - return EVP_MD_CTX_size(&digest->ctx); + return EVP_MD_CTX_size(digest->ctx); } static unsigned @@ -218,7 +124,7 @@ digest_block_size(PX_MD *h) { OSSLDigest *digest = (OSSLDigest *) h->p.ptr; - return EVP_MD_CTX_block_size(&digest->ctx); + return EVP_MD_CTX_block_size(digest->ctx); } static void @@ -226,7 +132,7 @@ digest_reset(PX_MD *h) { OSSLDigest *digest = (OSSLDigest *) h->p.ptr; - EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL); + EVP_DigestInit_ex(digest->ctx, digest->algo, NULL); } static void @@ -234,7 +140,7 @@ digest_update(PX_MD *h, const uint8 *data, unsigned dlen) { OSSLDigest *digest = (OSSLDigest *) h->p.ptr; - EVP_DigestUpdate(&digest->ctx, data, dlen); + EVP_DigestUpdate(digest->ctx, data, dlen); } static void @@ -242,7 +148,7 @@ digest_finish(PX_MD *h, uint8 *dst) { OSSLDigest *digest = (OSSLDigest *) h->p.ptr; - EVP_DigestFinal_ex(&digest->ctx, dst, NULL); + EVP_DigestFinal_ex(digest->ctx, dst, NULL); } static void @@ -250,9 +156,7 @@ digest_free(PX_MD *h) { OSSLDigest *digest = (OSSLDigest *) h->p.ptr; - EVP_MD_CTX_cleanup(&digest->ctx); - - px_free(digest); + free_openssl_digest(digest); px_free(h); } @@ -264,6 +168,7 @@ int px_find_digest(const char *name, PX_MD **res) { const EVP_MD *md; + EVP_MD_CTX *ctx; PX_MD *h; OSSLDigest *digest; @@ -273,17 +178,43 @@ px_find_digest(const char *name, PX_MD **res) OpenSSL_add_all_algorithms(); } + if (!digest_resowner_callback_registered) + { + RegisterResourceReleaseCallback(digest_free_callback, NULL); + digest_resowner_callback_registered = true; + } + md = EVP_get_digestbyname(name); if (md == NULL) - return compat_find_digest(name, res); + return PXE_NO_HASH; - digest = px_alloc(sizeof(*digest)); - digest->algo = md; + /* + * Create an OSSLDigest object, an OpenSSL MD object, and a PX_MD object. + * The order is crucial, to make sure we don't leak anything on + * out-of-memory or other error. + */ + digest = MemoryContextAlloc(TopMemoryContext, sizeof(*digest)); - EVP_MD_CTX_init(&digest->ctx); - if (EVP_DigestInit_ex(&digest->ctx, digest->algo, NULL) == 0) + ctx = EVP_MD_CTX_create(); + if (!ctx) + { + pfree(digest); return -1; + } + if (EVP_DigestInit_ex(ctx, md, NULL) == 0) + { + pfree(digest); + return -1; + } + + digest->algo = md; + digest->ctx = ctx; + digest->owner = CurrentResourceOwner; + digest->next = open_digests; + digest->prev = NULL; + open_digests = digest; + /* The PX_MD object is allocated in the current memory context. */ h = px_alloc(sizeof(*h)); h->result_size = digest_result_size; h->block_size = digest_block_size; @@ -300,60 +231,101 @@ px_find_digest(const char *name, PX_MD **res) /* * Ciphers * - * The problem with OpenSSL is that the EVP* family - * of functions does not allow enough flexibility - * and forces some of the parameters (keylen, - * padding) to SSL defaults. - * - * So need to manage ciphers ourselves. + * We use OpenSSL's EVP* family of functions for these. */ +/* + * prototype for the EVP functions that return an algorithm, e.g. + * EVP_aes_128_cbc(). + */ +typedef const EVP_CIPHER *(*ossl_EVP_cipher_func) (void); + +/* + * ossl_cipher contains the static information about each cipher. + */ struct ossl_cipher { int (*init) (PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv); - int (*encrypt) (PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res); - int (*decrypt) (PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res); - + ossl_EVP_cipher_func cipher_func; int block_size; int max_key_size; - int stream_cipher; }; -typedef struct +/* + * OSSLCipher contains the state for using a cipher. A separate OSSLCipher + * object is allocated in each px_find_cipher() call. + * + * To make sure we don't leak OpenSSL handles on abort, we keep OSSLCipher + * objects in a linked list, allocated in TopMemoryContext. We use the + * ResourceOwner mechanism to free them on abort. + */ +typedef struct OSSLCipher { - union - { - struct - { - BF_KEY key; - int num; - } bf; - struct - { - DES_key_schedule key_schedule; - } des; - struct - { - DES_key_schedule k1, - k2, - k3; - } des3; - CAST_KEY cast_key; - AES_KEY aes_key; - } u; + EVP_CIPHER_CTX *evp_ctx; + const EVP_CIPHER *evp_ciph; uint8 key[MAX_KEY]; uint8 iv[MAX_IV]; unsigned klen; unsigned init; const struct ossl_cipher *ciph; -} ossldata; -/* generic */ + ResourceOwner owner; + struct OSSLCipher *next; + struct OSSLCipher *prev; +} OSSLCipher; + +static OSSLCipher *open_ciphers = NULL; +static bool cipher_resowner_callback_registered = false; + +static void +free_openssl_cipher(OSSLCipher *od) +{ + EVP_CIPHER_CTX_free(od->evp_ctx); + if (od->prev) + od->prev->next = od->next; + else + open_ciphers = od->next; + if (od->next) + od->next->prev = od->prev; + pfree(od); +} + +/* + * Close any open OpenSSL cipher handles on abort. + */ +static void +cipher_free_callback(ResourceReleasePhase phase, + bool isCommit, + bool isTopLevel, + void *arg) +{ + OSSLCipher *curr; + OSSLCipher *next; + + if (phase != RESOURCE_RELEASE_AFTER_LOCKS) + return; + + next = open_ciphers; + while (next) + { + curr = next; + next = curr->next; + + if (curr->owner == CurrentResourceOwner) + { + if (isCommit) + elog(WARNING, "pgcrypto cipher reference leak: cipher %p still referenced", curr); + free_openssl_cipher(curr); + } + } +} + +/* Common routines for all algorithms */ static unsigned gen_ossl_block_size(PX_Cipher *c) { - ossldata *od = (ossldata *) c->ptr; + OSSLCipher *od = (OSSLCipher *) c->ptr; return od->ciph->block_size; } @@ -361,7 +333,7 @@ gen_ossl_block_size(PX_Cipher *c) static unsigned gen_ossl_key_size(PX_Cipher *c) { - ossldata *od = (ossldata *) c->ptr; + OSSLCipher *od = (OSSLCipher *) c->ptr; return od->ciph->max_key_size; } @@ -370,7 +342,7 @@ static unsigned gen_ossl_iv_size(PX_Cipher *c) { unsigned ivlen; - ossldata *od = (ossldata *) c->ptr; + OSSLCipher *od = (OSSLCipher *) c->ptr; ivlen = od->ciph->block_size; return ivlen; @@ -379,13 +351,60 @@ gen_ossl_iv_size(PX_Cipher *c) static void gen_ossl_free(PX_Cipher *c) { - ossldata *od = (ossldata *) c->ptr; + OSSLCipher *od = (OSSLCipher *) c->ptr; - px_memset(od, 0, sizeof(*od)); - px_free(od); + free_openssl_cipher(od); px_free(c); } +static int +gen_ossl_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, + uint8 *res) +{ + OSSLCipher *od = c->ptr; + int outlen; + + if (!od->init) + { + if (!EVP_DecryptInit_ex(od->evp_ctx, od->evp_ciph, NULL, NULL, NULL)) + return PXE_CIPHER_INIT; + if (!EVP_CIPHER_CTX_set_key_length(od->evp_ctx, od->klen)) + return PXE_CIPHER_INIT; + if (!EVP_DecryptInit_ex(od->evp_ctx, NULL, NULL, od->key, od->iv)) + return PXE_CIPHER_INIT; + od->init = true; + } + + if (!EVP_DecryptUpdate(od->evp_ctx, res, &outlen, data, dlen)) + return PXE_DECRYPT_FAILED; + + return 0; +} + +static int +gen_ossl_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, + uint8 *res) +{ + OSSLCipher *od = c->ptr; + int outlen; + + if (!od->init) + { + if (!EVP_EncryptInit_ex(od->evp_ctx, od->evp_ciph, NULL, NULL, NULL)) + return PXE_CIPHER_INIT; + if (!EVP_CIPHER_CTX_set_key_length(od->evp_ctx, od->klen)) + return PXE_CIPHER_INIT; + if (!EVP_EncryptInit_ex(od->evp_ctx, NULL, NULL, od->key, od->iv)) + return PXE_CIPHER_INIT; + od->init = true; + } + + if (!EVP_EncryptUpdate(od->evp_ctx, res, &outlen, data, dlen)) + return PXE_ERR_GENERIC; + + return 0; +} + /* Blowfish */ /* @@ -407,24 +426,40 @@ bf_check_supported_key_len(void) static const uint8 data[8] = {0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}; static const uint8 res[8] = {0xc0, 0x45, 0x04, 0x01, 0x2e, 0x4e, 0x1f, 0x53}; - static uint8 out[8]; - - BF_KEY bf_key; + uint8 out[8]; + EVP_CIPHER_CTX *evp_ctx; + int outlen; + int status = 0; /* encrypt with 448bits key and verify output */ - BF_set_key(&bf_key, 56, key); - BF_ecb_encrypt(data, out, &bf_key, BF_ENCRYPT); + evp_ctx = EVP_CIPHER_CTX_new(); + if (!evp_ctx) + return 0; + if (!EVP_EncryptInit_ex(evp_ctx, EVP_bf_ecb(), NULL, NULL, NULL)) + goto leave; + if (!EVP_CIPHER_CTX_set_key_length(evp_ctx, 56)) + goto leave; + if (!EVP_EncryptInit_ex(evp_ctx, NULL, NULL, key, NULL)) + goto leave; + + if (!EVP_EncryptUpdate(evp_ctx, out, &outlen, data, 8)) + goto leave; if (memcmp(out, res, 8) != 0) - return 0; /* Output does not match -> strong cipher is + goto leave; /* Output does not match -> strong cipher is * not supported */ - return 1; + status = 1; + +leave: + EVP_CIPHER_CTX_free(evp_ctx); + return status; } static int bf_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { - ossldata *od = c->ptr; + OSSLCipher *od = c->ptr; + unsigned bs = gen_ossl_block_size(c); static int bf_is_strong = -1; /* @@ -440,74 +475,13 @@ bf_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) return PXE_KEY_TOO_BIG; /* Key len is supported. We can use it. */ - BF_set_key(&od->u.bf.key, klen, key); + od->klen = klen; + memcpy(od->key, key, klen); + if (iv) - memcpy(od->iv, iv, BF_BLOCK); + memcpy(od->iv, iv, bs); else - memset(od->iv, 0, BF_BLOCK); - od->u.bf.num = 0; - return 0; -} - -static int -bf_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - unsigned i; - ossldata *od = c->ptr; - - for (i = 0; i < dlen / bs; i++) - BF_ecb_encrypt(data + i * bs, res + i * bs, &od->u.bf.key, BF_ENCRYPT); - return 0; -} - -static int -bf_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c), - i; - ossldata *od = c->ptr; - - for (i = 0; i < dlen / bs; i++) - BF_ecb_encrypt(data + i * bs, res + i * bs, &od->u.bf.key, BF_DECRYPT); - return 0; -} - -static int -bf_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - ossldata *od = c->ptr; - - BF_cbc_encrypt(data, res, dlen, &od->u.bf.key, od->iv, BF_ENCRYPT); - return 0; -} - -static int -bf_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - ossldata *od = c->ptr; - - BF_cbc_encrypt(data, res, dlen, &od->u.bf.key, od->iv, BF_DECRYPT); - return 0; -} - -static int -bf_cfb64_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - ossldata *od = c->ptr; - - BF_cfb64_encrypt(data, res, dlen, &od->u.bf.key, od->iv, - &od->u.bf.num, BF_ENCRYPT); - return 0; -} - -static int -bf_cfb64_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - ossldata *od = c->ptr; - - BF_cfb64_encrypt(data, res, dlen, &od->u.bf.key, od->iv, - &od->u.bf.num, BF_DECRYPT); + memset(od->iv, 0, bs); return 0; } @@ -516,70 +490,17 @@ bf_cfb64_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) static int ossl_des_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { - ossldata *od = c->ptr; - DES_cblock xkey; + OSSLCipher *od = c->ptr; + unsigned bs = gen_ossl_block_size(c); - memset(&xkey, 0, sizeof(xkey)); - memcpy(&xkey, key, klen > 8 ? 8 : klen); - DES_set_key(&xkey, &od->u.des.key_schedule); - memset(&xkey, 0, sizeof(xkey)); + od->klen = 8; + memset(od->key, 0, 8); + memcpy(od->key, key, klen > 8 ? 8 : klen); if (iv) - memcpy(od->iv, iv, 8); + memcpy(od->iv, iv, bs); else - memset(od->iv, 0, 8); - return 0; -} - -static int -ossl_des_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - unsigned i; - ossldata *od = c->ptr; - - for (i = 0; i < dlen / bs; i++) - DES_ecb_encrypt((DES_cblock *) (data + i * bs), - (DES_cblock *) (res + i * bs), - &od->u.des.key_schedule, 1); - return 0; -} - -static int -ossl_des_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - unsigned i; - ossldata *od = c->ptr; - - for (i = 0; i < dlen / bs; i++) - DES_ecb_encrypt((DES_cblock *) (data + i * bs), - (DES_cblock *) (res + i * bs), - &od->u.des.key_schedule, 0); - return 0; -} - -static int -ossl_des_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - ossldata *od = c->ptr; - - DES_ncbc_encrypt(data, res, dlen, &od->u.des.key_schedule, - (DES_cblock *) od->iv, 1); - return 0; -} - -static int -ossl_des_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - ossldata *od = c->ptr; - - DES_ncbc_encrypt(data, res, dlen, &od->u.des.key_schedule, - (DES_cblock *) od->iv, 0); + memset(od->iv, 0, bs); return 0; } @@ -588,83 +509,17 @@ ossl_des_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, static int ossl_des3_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { - ossldata *od = c->ptr; - DES_cblock xkey1, - xkey2, - xkey3; - - memset(&xkey1, 0, sizeof(xkey1)); - memset(&xkey2, 0, sizeof(xkey2)); - memset(&xkey3, 0, sizeof(xkey3)); - memcpy(&xkey1, key, klen > 8 ? 8 : klen); - if (klen > 8) - memcpy(&xkey2, key + 8, (klen - 8) > 8 ? 8 : (klen - 8)); - if (klen > 16) - memcpy(&xkey3, key + 16, (klen - 16) > 8 ? 8 : (klen - 16)); - - DES_set_key(&xkey1, &od->u.des3.k1); - DES_set_key(&xkey2, &od->u.des3.k2); - DES_set_key(&xkey3, &od->u.des3.k3); - memset(&xkey1, 0, sizeof(xkey1)); - memset(&xkey2, 0, sizeof(xkey2)); - memset(&xkey3, 0, sizeof(xkey3)); - - if (iv) - memcpy(od->iv, iv, 8); - else - memset(od->iv, 0, 8); - return 0; -} - -static int -ossl_des3_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ + OSSLCipher *od = c->ptr; unsigned bs = gen_ossl_block_size(c); - unsigned i; - ossldata *od = c->ptr; - for (i = 0; i < dlen / bs; i++) - DES_ecb3_encrypt((void *) (data + i * bs), (void *) (res + i * bs), - &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, 1); - return 0; -} - -static int -ossl_des3_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - unsigned i; - ossldata *od = c->ptr; - - for (i = 0; i < dlen / bs; i++) - DES_ecb3_encrypt((void *) (data + i * bs), (void *) (res + i * bs), - &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, 0); - return 0; -} - -static int -ossl_des3_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - ossldata *od = c->ptr; + od->klen = 24; + memset(od->key, 0, 24); + memcpy(od->key, key, klen > 24 ? 24 : klen); - DES_ede3_cbc_encrypt(data, res, dlen, - &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, - (DES_cblock *) od->iv, 1); - return 0; -} - -static int -ossl_des3_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - ossldata *od = c->ptr; - - DES_ede3_cbc_encrypt(data, res, dlen, - &od->u.des3.k1, &od->u.des3.k2, &od->u.des3.k3, - (DES_cblock *) od->iv, 0); + if (iv) + memcpy(od->iv, iv, bs); + else + memset(od->iv, 0, bs); return 0; } @@ -673,10 +528,12 @@ ossl_des3_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, static int ossl_cast_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { - ossldata *od = c->ptr; + OSSLCipher *od = c->ptr; unsigned bs = gen_ossl_block_size(c); - CAST_set_key(&od->u.cast_key, klen, key); + od->klen = klen; + memcpy(od->key, key, klen); + if (iv) memcpy(od->iv, iv, bs); else @@ -684,54 +541,12 @@ ossl_cast_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) return 0; } -static int -ossl_cast_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - ossldata *od = c->ptr; - const uint8 *end = data + dlen - bs; - - for (; data <= end; data += bs, res += bs) - CAST_ecb_encrypt(data, res, &od->u.cast_key, CAST_ENCRYPT); - return 0; -} - -static int -ossl_cast_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - ossldata *od = c->ptr; - const uint8 *end = data + dlen - bs; - - for (; data <= end; data += bs, res += bs) - CAST_ecb_encrypt(data, res, &od->u.cast_key, CAST_DECRYPT); - return 0; -} - -static int -ossl_cast_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - ossldata *od = c->ptr; - - CAST_cbc_encrypt(data, res, dlen, &od->u.cast_key, od->iv, CAST_ENCRYPT); - return 0; -} - -static int -ossl_cast_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, uint8 *res) -{ - ossldata *od = c->ptr; - - CAST_cbc_encrypt(data, res, dlen, &od->u.cast_key, od->iv, CAST_DECRYPT); - return 0; -} - /* AES */ static int ossl_aes_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { - ossldata *od = c->ptr; + OSSLCipher *od = c->ptr; unsigned bs = gen_ossl_block_size(c); if (klen <= 128 / 8) @@ -749,96 +564,68 @@ ossl_aes_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) memcpy(od->iv, iv, bs); else memset(od->iv, 0, bs); + return 0; } static int -ossl_aes_key_init(ossldata *od, int type) +ossl_aes_ecb_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { + OSSLCipher *od = c->ptr; int err; - /* - * Strong key support could be missing on some openssl installations. We - * must check return value from set key function. - */ - if (type == AES_ENCRYPT) - err = AES_set_encrypt_key(od->key, od->klen * 8, &od->u.aes_key); - else - err = AES_set_decrypt_key(od->key, od->klen * 8, &od->u.aes_key); + err = ossl_aes_init(c, key, klen, iv); + if (err) + return err; - if (err == 0) + switch (od->klen) { - od->init = 1; - return 0; + case 128 / 8: + od->evp_ciph = EVP_aes_128_ecb(); + break; + case 192 / 8: + od->evp_ciph = EVP_aes_192_ecb(); + break; + case 256 / 8: + od->evp_ciph = EVP_aes_256_ecb(); + break; + default: + /* shouldn't happen */ + err = PXE_CIPHER_INIT; + break; } - od->init = 0; - return PXE_KEY_TOO_BIG; -} - -static int -ossl_aes_ecb_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - ossldata *od = c->ptr; - const uint8 *end = data + dlen - bs; - int err; - - if (!od->init) - if ((err = ossl_aes_key_init(od, AES_ENCRYPT)) != 0) - return err; - for (; data <= end; data += bs, res += bs) - AES_ecb_encrypt(data, res, &od->u.aes_key, AES_ENCRYPT); - return 0; -} - -static int -ossl_aes_ecb_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - unsigned bs = gen_ossl_block_size(c); - ossldata *od = c->ptr; - const uint8 *end = data + dlen - bs; - int err; - - if (!od->init) - if ((err = ossl_aes_key_init(od, AES_DECRYPT)) != 0) - return err; - - for (; data <= end; data += bs, res += bs) - AES_ecb_encrypt(data, res, &od->u.aes_key, AES_DECRYPT); - return 0; + return err; } static int -ossl_aes_cbc_encrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) +ossl_aes_cbc_init(PX_Cipher *c, const uint8 *key, unsigned klen, const uint8 *iv) { - ossldata *od = c->ptr; + OSSLCipher *od = c->ptr; int err; - if (!od->init) - if ((err = ossl_aes_key_init(od, AES_ENCRYPT)) != 0) - return err; - - AES_cbc_encrypt(data, res, dlen, &od->u.aes_key, od->iv, AES_ENCRYPT); - return 0; -} + err = ossl_aes_init(c, key, klen, iv); + if (err) + return err; -static int -ossl_aes_cbc_decrypt(PX_Cipher *c, const uint8 *data, unsigned dlen, - uint8 *res) -{ - ossldata *od = c->ptr; - int err; - - if (!od->init) - if ((err = ossl_aes_key_init(od, AES_DECRYPT)) != 0) - return err; + switch (od->klen) + { + case 128 / 8: + od->evp_ciph = EVP_aes_128_cbc(); + break; + case 192 / 8: + od->evp_ciph = EVP_aes_192_cbc(); + break; + case 256 / 8: + od->evp_ciph = EVP_aes_256_cbc(); + break; + default: + /* shouldn't happen */ + err = PXE_CIPHER_INIT; + break; + } - AES_cbc_encrypt(data, res, dlen, &od->u.aes_key, od->iv, AES_DECRYPT); - return 0; + return err; } /* @@ -864,58 +651,71 @@ static PX_Alias ossl_aliases[] = { }; static const struct ossl_cipher ossl_bf_cbc = { - bf_init, bf_cbc_encrypt, bf_cbc_decrypt, - 64 / 8, 448 / 8, 0 + bf_init, + EVP_bf_cbc, + 64 / 8, 448 / 8 }; static const struct ossl_cipher ossl_bf_ecb = { - bf_init, bf_ecb_encrypt, bf_ecb_decrypt, - 64 / 8, 448 / 8, 0 + bf_init, + EVP_bf_ecb, + 64 / 8, 448 / 8 }; static const struct ossl_cipher ossl_bf_cfb = { - bf_init, bf_cfb64_encrypt, bf_cfb64_decrypt, - 64 / 8, 448 / 8, 1 + bf_init, + EVP_bf_cfb, + 64 / 8, 448 / 8 }; static const struct ossl_cipher ossl_des_ecb = { - ossl_des_init, ossl_des_ecb_encrypt, ossl_des_ecb_decrypt, - 64 / 8, 64 / 8, 0 + ossl_des_init, + EVP_des_ecb, + 64 / 8, 64 / 8 }; static const struct ossl_cipher ossl_des_cbc = { - ossl_des_init, ossl_des_cbc_encrypt, ossl_des_cbc_decrypt, - 64 / 8, 64 / 8, 0 + ossl_des_init, + EVP_des_cbc, + 64 / 8, 64 / 8 }; static const struct ossl_cipher ossl_des3_ecb = { - ossl_des3_init, ossl_des3_ecb_encrypt, ossl_des3_ecb_decrypt, - 64 / 8, 192 / 8, 0 + ossl_des3_init, + EVP_des_ede3_ecb, + 64 / 8, 192 / 8 }; static const struct ossl_cipher ossl_des3_cbc = { - ossl_des3_init, ossl_des3_cbc_encrypt, ossl_des3_cbc_decrypt, - 64 / 8, 192 / 8, 0 + ossl_des3_init, + EVP_des_ede3_cbc, + 64 / 8, 192 / 8 }; static const struct ossl_cipher ossl_cast_ecb = { - ossl_cast_init, ossl_cast_ecb_encrypt, ossl_cast_ecb_decrypt, - 64 / 8, 128 / 8, 0 + ossl_cast_init, + EVP_cast5_ecb, + 64 / 8, 128 / 8 }; static const struct ossl_cipher ossl_cast_cbc = { - ossl_cast_init, ossl_cast_cbc_encrypt, ossl_cast_cbc_decrypt, - 64 / 8, 128 / 8, 0 + ossl_cast_init, + EVP_cast5_cbc, + 64 / 8, 128 / 8 }; static const struct ossl_cipher ossl_aes_ecb = { - ossl_aes_init, ossl_aes_ecb_encrypt, ossl_aes_ecb_decrypt, - 128 / 8, 256 / 8, 0 + ossl_aes_ecb_init, + NULL, /* EVP_aes_XXX_ecb(), determined in init + * function */ + 128 / 8, 256 / 8 }; static const struct ossl_cipher ossl_aes_cbc = { - ossl_aes_init, ossl_aes_cbc_encrypt, ossl_aes_cbc_decrypt, - 128 / 8, 256 / 8, 0 + ossl_aes_cbc_init, + NULL, /* EVP_aes_XXX_cbc(), determined in init + * function */ + 128 / 8, 256 / 8 }; /* @@ -949,7 +749,8 @@ px_find_cipher(const char *name, PX_Cipher **res) { const struct ossl_cipher_lookup *i; PX_Cipher *c = NULL; - ossldata *od; + EVP_CIPHER_CTX *ctx; + OSSLCipher *od; name = px_resolve_alias(ossl_aliases, name); for (i = ossl_cipher_types; i->name; i++) @@ -958,75 +759,48 @@ px_find_cipher(const char *name, PX_Cipher **res) if (i->name == NULL) return PXE_NO_CIPHER; - od = px_alloc(sizeof(*od)); - memset(od, 0, sizeof(*od)); + if (!cipher_resowner_callback_registered) + { + RegisterResourceReleaseCallback(cipher_free_callback, NULL); + cipher_resowner_callback_registered = true; + } + + /* + * Create an OSSLCipher object, an EVP_CIPHER_CTX object and a PX_Cipher. + * The order is crucial, to make sure we don't leak anything on + * out-of-memory or other error. + */ + od = MemoryContextAllocZero(TopMemoryContext, sizeof(*od)); od->ciph = i->ciph; + /* Allocate an EVP_CIPHER_CTX object. */ + ctx = EVP_CIPHER_CTX_new(); + if (!ctx) + { + pfree(od); + return PXE_CIPHER_INIT; + } + + od->evp_ctx = ctx; + od->owner = CurrentResourceOwner; + od->next = open_ciphers; + od->prev = NULL; + open_ciphers = od; + + if (i->ciph->cipher_func) + od->evp_ciph = i->ciph->cipher_func(); + + /* The PX_Cipher is allocated in current memory context */ c = px_alloc(sizeof(*c)); c->block_size = gen_ossl_block_size; c->key_size = gen_ossl_key_size; c->iv_size = gen_ossl_iv_size; c->free = gen_ossl_free; c->init = od->ciph->init; - c->encrypt = od->ciph->encrypt; - c->decrypt = od->ciph->decrypt; + c->encrypt = gen_ossl_encrypt; + c->decrypt = gen_ossl_decrypt; c->ptr = od; *res = c; return 0; } - - -static int openssl_random_init = 0; - -/* - * OpenSSL random should re-feeded occasionally. From /dev/urandom - * preferably. - */ -static void -init_openssl_rand(void) -{ - if (RAND_get_rand_method() == NULL) - RAND_set_rand_method(RAND_SSLeay()); - openssl_random_init = 1; -} - -int -px_get_random_bytes(uint8 *dst, unsigned count) -{ - int res; - - if (!openssl_random_init) - init_openssl_rand(); - - res = RAND_bytes(dst, count); - if (res == 1) - return count; - - return PXE_OSSL_RAND_ERROR; -} - -int -px_get_pseudo_random_bytes(uint8 *dst, unsigned count) -{ - int res; - - if (!openssl_random_init) - init_openssl_rand(); - - res = RAND_pseudo_bytes(dst, count); - if (res == 0 || res == 1) - return count; - - return PXE_OSSL_RAND_ERROR; -} - -int -px_add_entropy(const uint8 *data, unsigned count) -{ - /* - * estimate 0 bits - */ - RAND_add(data, count, 0); - return 0; -} diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c index 2d446d8cc9..4e3516a86a 100644 --- a/contrib/pgcrypto/pgcrypto.c +++ b/contrib/pgcrypto/pgcrypto.c @@ -34,6 +34,7 @@ #include <ctype.h> #include "parser/scansup.h" +#include "utils/backend_random.h" #include "utils/builtins.h" #include "utils/uuid.h" @@ -61,7 +62,7 @@ pg_digest(PG_FUNCTION_ARGS) PX_MD *md; bytea *res; - name = PG_GETARG_TEXT_P(1); + name = PG_GETARG_TEXT_PP(1); /* will give error if fails */ md = find_provider(name, (PFN) px_find_digest, "Digest", 0); @@ -71,10 +72,10 @@ pg_digest(PG_FUNCTION_ARGS) res = (text *) palloc(hlen + VARHDRSZ); SET_VARSIZE(res, hlen + VARHDRSZ); - arg = PG_GETARG_BYTEA_P(0); - len = VARSIZE(arg) - VARHDRSZ; + arg = PG_GETARG_BYTEA_PP(0); + len = VARSIZE_ANY_EXHDR(arg); - px_md_update(md, (uint8 *) VARDATA(arg), len); + px_md_update(md, (uint8 *) VARDATA_ANY(arg), len); px_md_finish(md, (uint8 *) VARDATA(res)); px_md_free(md); @@ -99,7 +100,7 @@ pg_hmac(PG_FUNCTION_ARGS) PX_HMAC *h; bytea *res; - name = PG_GETARG_TEXT_P(2); + name = PG_GETARG_TEXT_PP(2); /* will give error if fails */ h = find_provider(name, (PFN) px_find_hmac, "HMAC", 0); @@ -109,13 +110,13 @@ pg_hmac(PG_FUNCTION_ARGS) res = (text *) palloc(hlen + VARHDRSZ); SET_VARSIZE(res, hlen + VARHDRSZ); - arg = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); - len = VARSIZE(arg) - VARHDRSZ; - klen = VARSIZE(key) - VARHDRSZ; + arg = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); + len = VARSIZE_ANY_EXHDR(arg); + klen = VARSIZE_ANY_EXHDR(key); - px_hmac_init(h, (uint8 *) VARDATA(key), klen); - px_hmac_update(h, (uint8 *) VARDATA(arg), len); + px_hmac_init(h, (uint8 *) VARDATA_ANY(key), klen); + px_hmac_update(h, (uint8 *) VARDATA_ANY(arg), len); px_hmac_finish(h, (uint8 *) VARDATA(res)); px_hmac_free(h); @@ -227,20 +228,20 @@ pg_encrypt(PG_FUNCTION_ARGS) klen, rlen; - type = PG_GETARG_TEXT_P(2); + type = PG_GETARG_TEXT_PP(2); c = find_provider(type, (PFN) px_find_combo, "Cipher", 0); - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); - dlen = VARSIZE(data) - VARHDRSZ; - klen = VARSIZE(key) - VARHDRSZ; + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); + dlen = VARSIZE_ANY_EXHDR(data); + klen = VARSIZE_ANY_EXHDR(key); rlen = px_combo_encrypt_len(c, dlen); res = palloc(VARHDRSZ + rlen); - err = px_combo_init(c, (uint8 *) VARDATA(key), klen, NULL, 0); + err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0); if (!err) - err = px_combo_encrypt(c, (uint8 *) VARDATA(data), dlen, + err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen, (uint8 *) VARDATA(res), &rlen); px_combo_free(c); @@ -276,20 +277,20 @@ pg_decrypt(PG_FUNCTION_ARGS) klen, rlen; - type = PG_GETARG_TEXT_P(2); + type = PG_GETARG_TEXT_PP(2); c = find_provider(type, (PFN) px_find_combo, "Cipher", 0); - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); - dlen = VARSIZE(data) - VARHDRSZ; - klen = VARSIZE(key) - VARHDRSZ; + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); + dlen = VARSIZE_ANY_EXHDR(data); + klen = VARSIZE_ANY_EXHDR(key); rlen = px_combo_decrypt_len(c, dlen); res = palloc(VARHDRSZ + rlen); - err = px_combo_init(c, (uint8 *) VARDATA(key), klen, NULL, 0); + err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, NULL, 0); if (!err) - err = px_combo_decrypt(c, (uint8 *) VARDATA(data), dlen, + err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen, (uint8 *) VARDATA(res), &rlen); px_combo_free(c); @@ -326,23 +327,23 @@ pg_encrypt_iv(PG_FUNCTION_ARGS) ivlen, rlen; - type = PG_GETARG_TEXT_P(3); + type = PG_GETARG_TEXT_PP(3); c = find_provider(type, (PFN) px_find_combo, "Cipher", 0); - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); - iv = PG_GETARG_BYTEA_P(2); - dlen = VARSIZE(data) - VARHDRSZ; - klen = VARSIZE(key) - VARHDRSZ; - ivlen = VARSIZE(iv) - VARHDRSZ; + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); + iv = PG_GETARG_BYTEA_PP(2); + dlen = VARSIZE_ANY_EXHDR(data); + klen = VARSIZE_ANY_EXHDR(key); + ivlen = VARSIZE_ANY_EXHDR(iv); rlen = px_combo_encrypt_len(c, dlen); res = palloc(VARHDRSZ + rlen); - err = px_combo_init(c, (uint8 *) VARDATA(key), klen, - (uint8 *) VARDATA(iv), ivlen); + err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, + (uint8 *) VARDATA_ANY(iv), ivlen); if (!err) - err = px_combo_encrypt(c, (uint8 *) VARDATA(data), dlen, + err = px_combo_encrypt(c, (uint8 *) VARDATA_ANY(data), dlen, (uint8 *) VARDATA(res), &rlen); px_combo_free(c); @@ -380,23 +381,23 @@ pg_decrypt_iv(PG_FUNCTION_ARGS) rlen, ivlen; - type = PG_GETARG_TEXT_P(3); + type = PG_GETARG_TEXT_PP(3); c = find_provider(type, (PFN) px_find_combo, "Cipher", 0); - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); - iv = PG_GETARG_BYTEA_P(2); - dlen = VARSIZE(data) - VARHDRSZ; - klen = VARSIZE(key) - VARHDRSZ; - ivlen = VARSIZE(iv) - VARHDRSZ; + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); + iv = PG_GETARG_BYTEA_PP(2); + dlen = VARSIZE_ANY_EXHDR(data); + klen = VARSIZE_ANY_EXHDR(key); + ivlen = VARSIZE_ANY_EXHDR(iv); rlen = px_combo_decrypt_len(c, dlen); res = palloc(VARHDRSZ + rlen); - err = px_combo_init(c, (uint8 *) VARDATA(key), klen, - (uint8 *) VARDATA(iv), ivlen); + err = px_combo_init(c, (uint8 *) VARDATA_ANY(key), klen, + (uint8 *) VARDATA_ANY(iv), ivlen); if (!err) - err = px_combo_decrypt(c, (uint8 *) VARDATA(data), dlen, + err = px_combo_decrypt(c, (uint8 *) VARDATA_ANY(data), dlen, (uint8 *) VARDATA(res), &rlen); px_combo_free(c); @@ -422,7 +423,7 @@ PG_FUNCTION_INFO_V1(pg_random_bytes); Datum pg_random_bytes(PG_FUNCTION_ARGS) { - int err; +#ifdef HAVE_STRONG_RANDOM int len = PG_GETARG_INT32(0); bytea *res; @@ -435,13 +436,13 @@ pg_random_bytes(PG_FUNCTION_ARGS) SET_VARSIZE(res, VARHDRSZ + len); /* generate result */ - err = px_get_random_bytes((uint8 *) VARDATA(res), len); - if (err < 0) - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("Random generator error: %s", px_strerror(err)))); + if (!pg_strong_random(VARDATA(res), len)) + px_THROW_ERROR(PXE_NO_RANDOM); PG_RETURN_BYTEA_P(res); +#else + px_THROW_ERROR(PXE_NO_RANDOM); +#endif } /* SQL function: gen_random_uuid() returns uuid */ @@ -451,14 +452,14 @@ Datum pg_random_uuid(PG_FUNCTION_ARGS) { uint8 *buf = (uint8 *) palloc(UUID_LEN); - int err; - /* generate random bits */ - err = px_get_pseudo_random_bytes(buf, UUID_LEN); - if (err < 0) - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("Random generator error: %s", px_strerror(err)))); + /* + * Generate random bits. pg_backend_random() will do here, we don't promis + * UUIDs to be cryptographically random, when built with + * --disable-strong-random. + */ + if (!pg_backend_random((char *) buf, UUID_LEN)) + px_THROW_ERROR(PXE_NO_RANDOM); /* * Set magic numbers for a "version 4" (pseudorandom) UUID, see @@ -479,8 +480,8 @@ find_provider(text *name, char *buf; int err; - buf = downcase_truncate_identifier(VARDATA(name), - VARSIZE(name) - VARHDRSZ, + buf = downcase_truncate_identifier(VARDATA_ANY(name), + VARSIZE_ANY_EXHDR(name), false); err = provider_lookup(buf, &res); diff --git a/contrib/pgcrypto/pgcrypto.h b/contrib/pgcrypto/pgcrypto.h index dfc7a10590..65a1ed3157 100644 --- a/contrib/pgcrypto/pgcrypto.h +++ b/contrib/pgcrypto/pgcrypto.h @@ -34,17 +34,4 @@ #include "fmgr.h" -/* exported functions */ -Datum pg_digest(PG_FUNCTION_ARGS); -Datum pg_hmac(PG_FUNCTION_ARGS); -Datum pg_gen_salt(PG_FUNCTION_ARGS); -Datum pg_gen_salt_rounds(PG_FUNCTION_ARGS); -Datum pg_crypt(PG_FUNCTION_ARGS); -Datum pg_encrypt(PG_FUNCTION_ARGS); -Datum pg_decrypt(PG_FUNCTION_ARGS); -Datum pg_encrypt_iv(PG_FUNCTION_ARGS); -Datum pg_decrypt_iv(PG_FUNCTION_ARGS); -Datum pg_random_bytes(PG_FUNCTION_ARGS); -Datum pg_random_uuid(PG_FUNCTION_ARGS); - #endif diff --git a/contrib/pgcrypto/pgp-encrypt.c b/contrib/pgcrypto/pgp-encrypt.c index c9148fd2fc..d510729e5b 100644 --- a/contrib/pgcrypto/pgp-encrypt.c +++ b/contrib/pgcrypto/pgp-encrypt.c @@ -37,6 +37,8 @@ #include "px.h" #include "pgp.h" +#include "utils/backend_random.h" + #define MDC_DIGEST_LEN 20 #define STREAM_ID 0xE0 @@ -217,6 +219,8 @@ encrypt_free(void *priv) { struct EncStat *st = priv; + if (st->ciph) + pgp_cfb_free(st->ciph); px_memset(st, 0, sizeof(*st)); px_free(st); } @@ -477,14 +481,14 @@ init_encdata_packet(PushFilter **pf_res, PGP_Context *ctx, PushFilter *dst) static int write_prefix(PGP_Context *ctx, PushFilter *dst) { +#ifdef HAVE_STRONG_RANDOM uint8 prefix[PGP_MAX_BLOCK + 2]; int res, bs; bs = pgp_get_cipher_block_size(ctx->cipher_algo); - res = px_get_random_bytes(prefix, bs); - if (res < 0) - return res; + if (!pg_backend_random((char *) prefix, bs)) + return PXE_NO_RANDOM; prefix[bs + 0] = prefix[bs - 2]; prefix[bs + 1] = prefix[bs - 1]; @@ -492,6 +496,9 @@ write_prefix(PGP_Context *ctx, PushFilter *dst) res = pushf_write(dst, prefix, bs + 2); px_memset(prefix, 0, bs + 2); return res < 0 ? res : 0; +#else + return PXE_NO_RANDOM; +#endif } /* @@ -578,14 +585,15 @@ init_s2k_key(PGP_Context *ctx) static int init_sess_key(PGP_Context *ctx) { - int res; - if (ctx->use_sess_key || ctx->pub_key) { +#ifdef HAVE_STRONG_RANDOM ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo); - res = px_get_random_bytes(ctx->sess_key, ctx->sess_key_len); - if (res < 0) - return res; + if (!pg_strong_random((char *) ctx->sess_key, ctx->sess_key_len)) + return PXE_NO_RANDOM; +#else + return PXE_NO_RANDOM; +#endif } else { diff --git a/contrib/pgcrypto/pgp-mpi-internal.c b/contrib/pgcrypto/pgp-mpi-internal.c index be95f2d092..545009ce19 100644 --- a/contrib/pgcrypto/pgp-mpi-internal.c +++ b/contrib/pgcrypto/pgp-mpi-internal.c @@ -57,17 +57,16 @@ mp_clear_free(mpz_t *a) static int mp_px_rand(uint32 bits, mpz_t *res) { - int err; +#ifdef HAVE_STRONG_RANDOM unsigned bytes = (bits + 7) / 8; int last_bits = bits & 7; uint8 *buf; buf = px_alloc(bytes); - err = px_get_random_bytes(buf, bytes); - if (err < 0) + if (!pg_strong_random((char *) buf, bytes)) { px_free(buf); - return err; + return PXE_NO_RANDOM; } /* clear unnecessary bits and set last bit to one */ @@ -84,6 +83,9 @@ mp_px_rand(uint32 bits, mpz_t *res) px_free(buf); return 0; +#else + return PXE_NO_RANDOM; +#endif } static void @@ -139,7 +141,7 @@ bn_to_mpi(mpz_t *bn) } /* - * Decide the number of bits in the random componont k + * Decide the number of bits in the random component k * * It should be in the same range as p for signing (which * is deprecated), but can be much smaller for encrypting. @@ -147,8 +149,8 @@ bn_to_mpi(mpz_t *bn) * Until I research it further, I just mimic gpg behaviour. * It has a special mapping table, for values <= 5120, * above that it uses 'arbitrary high number'. Following - * algorihm hovers 10-70 bits above gpg values. And for - * larger p, it uses gpg's algorihm. + * algorithm hovers 10-70 bits above gpg values. And for + * larger p, it uses gpg's algorithm. * * The point is - if k gets large, encryption will be * really slow. It does not matter for decryption. diff --git a/contrib/pgcrypto/pgp-mpi-openssl.c b/contrib/pgcrypto/pgp-mpi-openssl.c index 24484a6c54..afece26918 100644 --- a/contrib/pgcrypto/pgp-mpi-openssl.c +++ b/contrib/pgcrypto/pgp-mpi-openssl.c @@ -74,7 +74,7 @@ bn_to_mpi(BIGNUM *bn) } /* - * Decide the number of bits in the random componont k + * Decide the number of bits in the random component k * * It should be in the same range as p for signing (which * is deprecated), but can be much smaller for encrypting. @@ -82,8 +82,8 @@ bn_to_mpi(BIGNUM *bn) * Until I research it further, I just mimic gpg behaviour. * It has a special mapping table, for values <= 5120, * above that it uses 'arbitrary high number'. Following - * algorihm hovers 10-70 bits above gpg values. And for - * larger p, it uses gpg's algorihm. + * algorithm hovers 10-70 bits above gpg values. And for + * larger p, it uses gpg's algorithm. * * The point is - if k gets large, encryption will be * really slow. It does not matter for decryption. diff --git a/contrib/pgcrypto/pgp-pgsql.c b/contrib/pgcrypto/pgp-pgsql.c index 1f65b667ca..cc5df14725 100644 --- a/contrib/pgcrypto/pgp-pgsql.c +++ b/contrib/pgcrypto/pgp-pgsql.c @@ -62,73 +62,14 @@ PG_FUNCTION_INFO_V1(pg_dearmor); PG_FUNCTION_INFO_V1(pgp_armor_headers); /* - * Mix a block of data into RNG. - */ -static void -add_block_entropy(PX_MD *md, text *data) -{ - uint8 sha1[20]; - - px_md_reset(md); - px_md_update(md, (uint8 *) VARDATA(data), VARSIZE(data) - VARHDRSZ); - px_md_finish(md, sha1); - - px_add_entropy(sha1, 20); - - px_memset(sha1, 0, 20); -} - -/* - * Mix user data into RNG. It is for user own interests to have - * RNG state shuffled. - */ -static void -add_entropy(text *data1, text *data2, text *data3) -{ - PX_MD *md; - uint8 rnd[3]; - - if (!data1 && !data2 && !data3) - return; - - if (px_get_random_bytes(rnd, 3) < 0) - return; - - if (px_find_digest("sha1", &md) < 0) - return; - - /* - * Try to make the feeding unpredictable. - * - * Prefer data over keys, as it's rather likely that key is same in - * several calls. - */ - - /* chance: 7/8 */ - if (data1 && rnd[0] >= 32) - add_block_entropy(md, data1); - - /* chance: 5/8 */ - if (data2 && rnd[1] >= 160) - add_block_entropy(md, data2); - - /* chance: 5/8 */ - if (data3 && rnd[2] >= 160) - add_block_entropy(md, data3); - - px_md_free(md); - px_memset(rnd, 0, sizeof(rnd)); -} - -/* * returns src in case of no conversion or error */ static text * convert_charset(text *src, int cset_from, int cset_to) { - int src_len = VARSIZE(src) - VARHDRSZ; + int src_len = VARSIZE_ANY_EXHDR(src); unsigned char *dst; - unsigned char *csrc = (unsigned char *) VARDATA(src); + unsigned char *csrc = (unsigned char *) VARDATA_ANY(src); text *res; dst = pg_do_encoding_conversion(csrc, src_len, cset_from, cset_to); @@ -168,7 +109,7 @@ string_is_ascii(const char *str) static void clear_and_pfree(text *p) { - px_memset(p, 0, VARSIZE(p)); + px_memset(p, 0, VARSIZE_ANY(p)); pfree(p); } @@ -415,8 +356,8 @@ parse_args(PGP_Context *ctx, uint8 *args, int arg_len, static MBuf * create_mbuf_from_vardata(text *data) { - return mbuf_create_from_data((uint8 *) VARDATA(data), - VARSIZE(data) - VARHDRSZ); + return mbuf_create_from_data((uint8 *) VARDATA_ANY(data), + VARSIZE_ANY_EXHDR(data)); } static void @@ -428,15 +369,11 @@ init_work(PGP_Context **ctx_p, int is_text, fill_expect(ex, is_text); if (err == 0 && args != NULL) - err = parse_args(*ctx_p, (uint8 *) VARDATA(args), - VARSIZE(args) - VARHDRSZ, ex); + err = parse_args(*ctx_p, (uint8 *) VARDATA_ANY(args), + VARSIZE_ANY_EXHDR(args), ex); if (err) - { - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("%s", px_strerror(err)))); - } + px_THROW_ERROR(err); if (ex->debug) px_set_debug_handler(show_debug); @@ -459,11 +396,6 @@ encrypt_internal(int is_pubenc, int is_text, struct debug_expect ex; text *tmp_data = NULL; - /* - * Add data and key info RNG. - */ - add_entropy(data, key, NULL); - init_work(&ctx, is_text, args, &ex); if (is_text && pgp_get_unicode_mode(ctx)) @@ -476,7 +408,7 @@ encrypt_internal(int is_pubenc, int is_text, } src = create_mbuf_from_vardata(data); - dst = mbuf_create(VARSIZE(data) + 128); + dst = mbuf_create(VARSIZE_ANY(data) + 128); /* * reserve room for header @@ -495,8 +427,8 @@ encrypt_internal(int is_pubenc, int is_text, mbuf_free(kbuf); } else - err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key), - VARSIZE(key) - VARHDRSZ); + err = pgp_set_symkey(ctx, (uint8 *) VARDATA_ANY(key), + VARSIZE_ANY_EXHDR(key)); /* * encrypt @@ -516,9 +448,7 @@ encrypt_internal(int is_pubenc, int is_text, pgp_free(ctx); mbuf_free(src); mbuf_free(dst); - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("%s", px_strerror(err)))); + px_THROW_ERROR(err); } /* res_len includes VARHDRSZ */ @@ -555,9 +485,9 @@ decrypt_internal(int is_pubenc, int need_text, text *data, init_work(&ctx, need_text, args, &ex); - src = mbuf_create_from_data((uint8 *) VARDATA(data), - VARSIZE(data) - VARHDRSZ); - dst = mbuf_create(VARSIZE(data) + 2048); + src = mbuf_create_from_data((uint8 *) VARDATA_ANY(data), + VARSIZE_ANY_EXHDR(data)); + dst = mbuf_create(VARSIZE_ANY(data) + 2048); /* * reserve room for header @@ -575,16 +505,16 @@ decrypt_internal(int is_pubenc, int need_text, text *data, if (keypsw) { - psw = (uint8 *) VARDATA(keypsw); - psw_len = VARSIZE(keypsw) - VARHDRSZ; + psw = (uint8 *) VARDATA_ANY(keypsw); + psw_len = VARSIZE_ANY_EXHDR(keypsw); } kbuf = create_mbuf_from_vardata(key); err = pgp_set_pubkey(ctx, kbuf, psw, psw_len, 1); mbuf_free(kbuf); } else - err = pgp_set_symkey(ctx, (uint8 *) VARDATA(key), - VARSIZE(key) - VARHDRSZ); + err = pgp_set_symkey(ctx, (uint8 *) VARDATA_ANY(key), + VARSIZE_ANY_EXHDR(key)); /* decrypt */ if (err >= 0) @@ -605,9 +535,7 @@ decrypt_internal(int is_pubenc, int need_text, text *data, { px_set_debug_handler(NULL); mbuf_free(dst); - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("%s", px_strerror(err)))); + px_THROW_ERROR(err); } res_len = mbuf_steal_data(dst, &restmp); @@ -629,11 +557,6 @@ decrypt_internal(int is_pubenc, int need_text, text *data, } px_set_debug_handler(NULL); - /* - * add successful decryptions also into RNG - */ - add_entropy(res, key, keypsw); - return res; } @@ -648,10 +571,10 @@ pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS) text *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - arg = PG_GETARG_BYTEA_P(2); + arg = PG_GETARG_BYTEA_PP(2); res = encrypt_internal(0, 0, data, key, arg); @@ -670,10 +593,10 @@ pgp_sym_encrypt_text(PG_FUNCTION_ARGS) text *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - arg = PG_GETARG_BYTEA_P(2); + arg = PG_GETARG_BYTEA_PP(2); res = encrypt_internal(0, 1, data, key, arg); @@ -693,10 +616,10 @@ pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS) text *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - arg = PG_GETARG_BYTEA_P(2); + arg = PG_GETARG_BYTEA_PP(2); res = decrypt_internal(0, 0, data, key, NULL, arg); @@ -715,10 +638,10 @@ pgp_sym_decrypt_text(PG_FUNCTION_ARGS) text *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - arg = PG_GETARG_BYTEA_P(2); + arg = PG_GETARG_BYTEA_PP(2); res = decrypt_internal(0, 1, data, key, NULL, arg); @@ -741,10 +664,10 @@ pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS) text *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - arg = PG_GETARG_BYTEA_P(2); + arg = PG_GETARG_BYTEA_PP(2); res = encrypt_internal(1, 0, data, key, arg); @@ -763,10 +686,10 @@ pgp_pub_encrypt_text(PG_FUNCTION_ARGS) text *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - arg = PG_GETARG_BYTEA_P(2); + arg = PG_GETARG_BYTEA_PP(2); res = encrypt_internal(1, 1, data, key, arg); @@ -787,12 +710,12 @@ pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS) *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - psw = PG_GETARG_BYTEA_P(2); + psw = PG_GETARG_BYTEA_PP(2); if (PG_NARGS() > 3) - arg = PG_GETARG_BYTEA_P(3); + arg = PG_GETARG_BYTEA_PP(3); res = decrypt_internal(1, 0, data, key, psw, arg); @@ -814,12 +737,12 @@ pgp_pub_decrypt_text(PG_FUNCTION_ARGS) *arg = NULL; text *res; - data = PG_GETARG_BYTEA_P(0); - key = PG_GETARG_BYTEA_P(1); + data = PG_GETARG_BYTEA_PP(0); + key = PG_GETARG_BYTEA_PP(1); if (PG_NARGS() > 2) - psw = PG_GETARG_BYTEA_P(2); + psw = PG_GETARG_BYTEA_PP(2); if (PG_NARGS() > 3) - arg = PG_GETARG_BYTEA_P(3); + arg = PG_GETARG_BYTEA_PP(3); res = decrypt_internal(1, 1, data, key, psw, arg); @@ -942,8 +865,8 @@ pg_armor(PG_FUNCTION_ARGS) char **keys = NULL, **values = NULL; - data = PG_GETARG_BYTEA_P(0); - data_len = VARSIZE(data) - VARHDRSZ; + data = PG_GETARG_BYTEA_PP(0); + data_len = VARSIZE_ANY_EXHDR(data); if (PG_NARGS() == 3) { num_headers = parse_key_value_arrays(PG_GETARG_ARRAYTYPE_P(1), @@ -957,7 +880,7 @@ pg_armor(PG_FUNCTION_ARGS) initStringInfo(&buf); - pgp_armor_encode((uint8 *) VARDATA(data), data_len, &buf, + pgp_armor_encode((uint8 *) VARDATA_ANY(data), data_len, &buf, num_headers, keys, values); res = palloc(VARHDRSZ + buf.len); @@ -978,16 +901,14 @@ pg_dearmor(PG_FUNCTION_ARGS) int ret; StringInfoData buf; - data = PG_GETARG_TEXT_P(0); - data_len = VARSIZE(data) - VARHDRSZ; + data = PG_GETARG_TEXT_PP(0); + data_len = VARSIZE_ANY_EXHDR(data); initStringInfo(&buf); - ret = pgp_armor_decode((uint8 *) VARDATA(data), data_len, &buf); + ret = pgp_armor_decode((uint8 *) VARDATA_ANY(data), data_len, &buf); if (ret < 0) - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("%s", px_strerror(ret)))); + px_THROW_ERROR(ret); res = palloc(VARHDRSZ + buf.len); SET_VARSIZE(res, VARHDRSZ + buf.len); memcpy(VARDATA(res), buf.data, buf.len); @@ -1041,9 +962,7 @@ pgp_armor_headers(PG_FUNCTION_ARGS) &state->nheaders, &state->keys, &state->values); if (res < 0) - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("%s", px_strerror(res)))); + px_THROW_ERROR(res); MemoryContextSwitchTo(oldcontext); funcctx->user_fctx = state; @@ -1085,16 +1004,14 @@ pgp_key_id_w(PG_FUNCTION_ARGS) int res_len; MBuf *buf; - data = PG_GETARG_BYTEA_P(0); + data = PG_GETARG_BYTEA_PP(0); buf = create_mbuf_from_vardata(data); res = palloc(VARHDRSZ + 17); res_len = pgp_get_keyid(buf, VARDATA(res)); mbuf_free(buf); if (res_len < 0) - ereport(ERROR, - (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), - errmsg("%s", px_strerror(res_len)))); + px_THROW_ERROR(res_len); SET_VARSIZE(res, VARHDRSZ + res_len); PG_FREE_IF_COPY(data, 0); diff --git a/contrib/pgcrypto/pgp-pubenc.c b/contrib/pgcrypto/pgp-pubenc.c index 3b43bb61c0..4439876664 100644 --- a/contrib/pgcrypto/pgp-pubenc.c +++ b/contrib/pgcrypto/pgp-pubenc.c @@ -39,7 +39,7 @@ static int pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p) { - int res; +#ifdef HAVE_STRONG_RANDOM uint8 *buf, *p; int pad_len = res_len - 2 - data_len; @@ -49,11 +49,11 @@ pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p) buf = px_alloc(res_len); buf[0] = 0x02; - res = px_get_random_bytes(buf + 1, pad_len); - if (res < 0) + + if (!pg_strong_random((char *) buf + 1, pad_len)) { px_free(buf); - return res; + return PXE_NO_RANDOM; } /* pad must not contain zero bytes */ @@ -62,26 +62,26 @@ pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p) { if (*p == 0) { - res = px_get_random_bytes(p, 1); - if (res < 0) + if (!pg_strong_random((char *) p, 1)) + { + px_memset(buf, 0, res_len); + px_free(buf); break; + } } if (*p != 0) p++; } - if (res < 0) - { - px_memset(buf, 0, res_len); - px_free(buf); - return res; - } - buf[pad_len + 1] = 0; memcpy(buf + pad_len + 2, data, data_len); *res_p = buf; return 0; + +#else + return PXE_NO_RANDOM; +#endif } static int diff --git a/contrib/pgcrypto/pgp-s2k.c b/contrib/pgcrypto/pgp-s2k.c index 9937d154f2..a0fd8969ef 100644 --- a/contrib/pgcrypto/pgp-s2k.c +++ b/contrib/pgcrypto/pgp-s2k.c @@ -34,6 +34,8 @@ #include "px.h" #include "pgp.h" +#include "utils/backend_random.h" + static int calc_s2k_simple(PGP_S2K *s2k, PX_MD *md, const uint8 *key, unsigned key_len) @@ -233,15 +235,14 @@ pgp_s2k_fill(PGP_S2K *s2k, int mode, int digest_algo, int count) case PGP_S2K_SIMPLE: break; case PGP_S2K_SALTED: - res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT); + if (!pg_backend_random((char *) s2k->salt, PGP_S2K_SALT)) + return PXE_NO_RANDOM; break; case PGP_S2K_ISALTED: - res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT); - if (res < 0) - break; - res = px_get_pseudo_random_bytes(&tmp, 1); - if (res < 0) - break; + if (!pg_backend_random((char *) s2k->salt, PGP_S2K_SALT)) + return PXE_NO_RANDOM; + if (!pg_backend_random((char *) &tmp, 1)) + return PXE_NO_RANDOM; s2k->iter = decide_s2k_iter(tmp, count); break; default: diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c index e3246fc5b9..6c72c4ae83 100644 --- a/contrib/pgcrypto/px-crypt.c +++ b/contrib/pgcrypto/px-crypt.c @@ -34,6 +34,7 @@ #include "px.h" #include "px-crypt.h" +#include "utils/backend_random.h" static char * run_crypt_des(const char *psw, const char *salt, @@ -132,7 +133,6 @@ static struct generator gen_list[] = { int px_gen_salt(const char *salt_type, char *buf, int rounds) { - int res; struct generator *g; char *p; char rbuf[16]; @@ -153,9 +153,8 @@ px_gen_salt(const char *salt_type, char *buf, int rounds) return PXE_BAD_SALT_ROUNDS; } - res = px_get_pseudo_random_bytes((uint8 *) rbuf, g->input_len); - if (res < 0) - return res; + if (!pg_backend_random(rbuf, g->input_len)) + return PXE_NO_RANDOM; p = g->gen(rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN); px_memset(rbuf, 0, sizeof(rbuf)); diff --git a/contrib/pgcrypto/px.c b/contrib/pgcrypto/px.c index 7e69da696f..a5c02f3612 100644 --- a/contrib/pgcrypto/px.c +++ b/contrib/pgcrypto/px.c @@ -51,7 +51,6 @@ static const struct error_desc px_err_list[] = { {PXE_CIPHER_INIT, "Cipher cannot be initialized ?"}, {PXE_HASH_UNUSABLE_FOR_HMAC, "This hash algorithm is unusable for HMAC"}, {PXE_DEV_READ_ERROR, "Error reading from random device"}, - {PXE_OSSL_RAND_ERROR, "OpenSSL PRNG error"}, {PXE_BUG, "pgcrypto bug"}, {PXE_ARGUMENT_ERROR, "Illegal argument to function"}, {PXE_UNKNOWN_SALT_ALGO, "Unknown salt algorithm"}, @@ -67,12 +66,8 @@ static const struct error_desc px_err_list[] = { {PXE_PGP_COMPRESSION_ERROR, "Compression error"}, {PXE_PGP_NOT_TEXT, "Not text data"}, {PXE_PGP_UNEXPECTED_PKT, "Unexpected packet in key data"}, - {PXE_PGP_NO_BIGNUM, - "public-key functions disabled - " - "pgcrypto needs OpenSSL for bignums"}, {PXE_PGP_MATH_FAILED, "Math operation failed"}, {PXE_PGP_SHORT_ELGAMAL_KEY, "Elgamal keys must be at least 1024 bits long"}, - {PXE_PGP_RSA_UNSUPPORTED, "pgcrypto does not support RSA keys"}, {PXE_PGP_UNKNOWN_PUBALGO, "Unknown public-key encryption algorithm"}, {PXE_PGP_WRONG_KEY, "Wrong key"}, {PXE_PGP_MULTIPLE_KEYS, @@ -90,6 +85,39 @@ static const struct error_desc px_err_list[] = { {0, NULL}, }; +/* + * Call ereport(ERROR, ...), with an error code and message corresponding to + * the PXE_* error code given as argument. + * + * This is similar to px_strerror(err), but for some errors, we fill in the + * error code and detail fields more appropriately. + */ +void +px_THROW_ERROR(int err) +{ + if (err == PXE_NO_RANDOM) + { +#ifdef HAVE_STRONG_RANDOM + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate a random number"))); +#else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("pg_random_bytes() is not supported by this build"), + errdetail("This functionality requires a source of strong random numbers"), + errhint("You need to rebuild PostgreSQL using --enable-strong-random"))); +#endif + } + else + { + /* For other errors, use the message from the above list. */ + ereport(ERROR, + (errcode(ERRCODE_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION), + errmsg("%s", px_strerror(err)))); + } +} + const char * px_strerror(int err) { diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h index 0f6bbd7a8d..e68a95a058 100644 --- a/contrib/pgcrypto/px.h +++ b/contrib/pgcrypto/px.h @@ -32,7 +32,6 @@ #ifndef __PX_H #define __PX_H -#include <sys/types.h> #include <sys/param.h> /* keep debug messages? */ @@ -71,7 +70,6 @@ void px_free(void *p); #define PXE_CIPHER_INIT -8 #define PXE_HASH_UNUSABLE_FOR_HMAC -9 #define PXE_DEV_READ_ERROR -10 -#define PXE_OSSL_RAND_ERROR -11 #define PXE_BUG -12 #define PXE_ARGUMENT_ERROR -13 #define PXE_UNKNOWN_SALT_ALGO -14 @@ -88,10 +86,10 @@ void px_free(void *p); #define PXE_PGP_COMPRESSION_ERROR -105 #define PXE_PGP_NOT_TEXT -106 #define PXE_PGP_UNEXPECTED_PKT -107 -#define PXE_PGP_NO_BIGNUM -108 +/* -108 is unused */ #define PXE_PGP_MATH_FAILED -109 #define PXE_PGP_SHORT_ELGAMAL_KEY -110 -#define PXE_PGP_RSA_UNSUPPORTED -111 +/* -111 is unused */ #define PXE_PGP_UNKNOWN_PUBALGO -112 #define PXE_PGP_WRONG_KEY -113 #define PXE_PGP_MULTIPLE_KEYS -114 @@ -189,12 +187,7 @@ int px_find_hmac(const char *name, PX_HMAC **res); int px_find_cipher(const char *name, PX_Cipher **res); int px_find_combo(const char *name, PX_Combo **res); -int px_get_random_bytes(uint8 *dst, unsigned count); -int px_get_pseudo_random_bytes(uint8 *dst, unsigned count); -int px_add_entropy(const uint8 *data, unsigned count); - -unsigned px_acquire_system_randomness(uint8 *dst); - +void px_THROW_ERROR(int err) pg_attribute_noreturn(); const char *px_strerror(int err); const char *px_resolve_alias(const PX_Alias *aliases, const char *name); diff --git a/contrib/pgcrypto/random.c b/contrib/pgcrypto/random.c deleted file mode 100644 index d72679e412..0000000000 --- a/contrib/pgcrypto/random.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * random.c - * Acquire randomness from system. For seeding RNG. - * - * Copyright (c) 2001 Marko Kreen - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * contrib/pgcrypto/random.c - */ - -#include "postgres.h" - -#include "px.h" -#include "utils/memdebug.h" - -/* how many bytes to ask from system random provider */ -#define RND_BYTES 32 - -/* - * Try to read from /dev/urandom or /dev/random on these OS'es. - * - * The list can be pretty liberal, as the device not existing - * is expected event. - */ -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \ - || defined(__NetBSD__) || defined(__DragonFly__) \ - || defined(__darwin__) || defined(__SOLARIS__) \ - || defined(__hpux) || defined(__HPUX__) \ - || defined(__CYGWIN__) || defined(_AIX) - -#define TRY_DEV_RANDOM - -#include <fcntl.h> -#include <unistd.h> - -static int -safe_read(int fd, void *buf, size_t count) -{ - int done = 0; - char *p = buf; - int res; - - while (count) - { - res = read(fd, p, count); - if (res <= 0) - { - if (errno == EINTR) - continue; - return PXE_DEV_READ_ERROR; - } - p += res; - done += res; - count -= res; - } - return done; -} - -static uint8 * -try_dev_random(uint8 *dst) -{ - int fd; - int res; - - fd = open("/dev/urandom", O_RDONLY, 0); - if (fd == -1) - { - fd = open("/dev/random", O_RDONLY, 0); - if (fd == -1) - return dst; - } - res = safe_read(fd, dst, RND_BYTES); - close(fd); - if (res > 0) - dst += res; - return dst; -} -#endif - -/* - * Try to find randomness on Windows - */ -#ifdef WIN32 - -#define TRY_WIN32_GENRAND -#define TRY_WIN32_PERFC - -#include <windows.h> -#include <wincrypt.h> - -/* - * this function is from libtomcrypt - * - * try to use Microsoft crypto API - */ -static uint8 * -try_win32_genrand(uint8 *dst) -{ - int res; - HCRYPTPROV h = 0; - - res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, - (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)); - if (!res) - res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET); - if (!res) - return dst; - - res = CryptGenRandom(h, RND_BYTES, dst); - if (res == TRUE) - dst += RND_BYTES; - - CryptReleaseContext(h, 0); - return dst; -} - -static uint8 * -try_win32_perfc(uint8 *dst) -{ - int res; - LARGE_INTEGER time; - - res = QueryPerformanceCounter(&time); - if (!res) - return dst; - - memcpy(dst, &time, sizeof(time)); - return dst + sizeof(time); -} -#endif /* WIN32 */ - - -/* - * If we are not on Windows, then hopefully we are - * on a unix-like system. Use the usual suspects - * for randomness. - */ -#ifndef WIN32 - -#define TRY_UNIXSTD - -#include <sys/types.h> -#include <sys/time.h> -#include <time.h> -#include <unistd.h> - -/* - * Everything here is predictible, only needs some patience. - * - * But there is a chance that the system-specific functions - * did not work. So keep faith and try to slow the attacker down. - */ -static uint8 * -try_unix_std(uint8 *dst) -{ - pid_t pid; - int x; - PX_MD *md; - struct timeval tv; - int res; - - /* process id */ - pid = getpid(); - memcpy(dst, (uint8 *) &pid, sizeof(pid)); - dst += sizeof(pid); - - /* time */ - gettimeofday(&tv, NULL); - memcpy(dst, (uint8 *) &tv, sizeof(tv)); - dst += sizeof(tv); - - /* pointless, but should not hurt */ - x = random(); - memcpy(dst, (uint8 *) &x, sizeof(x)); - dst += sizeof(x); - - /* hash of uninitialized stack and heap allocations */ - res = px_find_digest("sha1", &md); - if (res >= 0) - { - uint8 *ptr; - uint8 stack[8192]; - int alloc = 32 * 1024; - - VALGRIND_MAKE_MEM_DEFINED(stack, sizeof(stack)); - px_md_update(md, stack, sizeof(stack)); - ptr = px_alloc(alloc); - VALGRIND_MAKE_MEM_DEFINED(ptr, alloc); - px_md_update(md, ptr, alloc); - px_free(ptr); - - px_md_finish(md, dst); - px_md_free(md); - - dst += 20; - } - - return dst; -} -#endif - -/* - * try to extract some randomness for initial seeding - * - * dst should have room for 1024 bytes. - */ -unsigned -px_acquire_system_randomness(uint8 *dst) -{ - uint8 *p = dst; - -#ifdef TRY_DEV_RANDOM - p = try_dev_random(p); -#endif -#ifdef TRY_WIN32_GENRAND - p = try_win32_genrand(p); -#endif -#ifdef TRY_WIN32_PERFC - p = try_win32_perfc(p); -#endif -#ifdef TRY_UNIXSTD - p = try_unix_std(p); -#endif - return p - dst; -} diff --git a/contrib/pgcrypto/sha2.c b/contrib/pgcrypto/sha2.c deleted file mode 100644 index 231f9dfbb0..0000000000 --- a/contrib/pgcrypto/sha2.c +++ /dev/null @@ -1,992 +0,0 @@ -/* $OpenBSD: sha2.c,v 1.6 2004/05/03 02:57:36 millert Exp $ */ - -/* - * FILE: sha2.c - * AUTHOR: Aaron D. Gifford <me@aarongifford.com> - * - * Copyright (c) 2000-2001, Aaron D. Gifford - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $From: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $ - * - * contrib/pgcrypto/sha2.c - */ - -#include "postgres.h" - -#include <sys/param.h> - -#include "px.h" -#include "sha2.h" - -/* - * UNROLLED TRANSFORM LOOP NOTE: - * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform - * loop version for the hash transform rounds (defined using macros - * later in this file). Either define on the command line, for example: - * - * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c - * - * or define below: - * - * #define SHA2_UNROLL_TRANSFORM - * - */ - -/*** SHA-256/384/512 Various Length Definitions ***********************/ -/* NOTE: Most of these are in sha2.h */ -#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) -#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) -#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) - - -/*** ENDIAN REVERSAL MACROS *******************************************/ -#ifndef WORDS_BIGENDIAN -#define REVERSE32(w,x) { \ - uint32 tmp = (w); \ - tmp = (tmp >> 16) | (tmp << 16); \ - (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ -} -#define REVERSE64(w,x) { \ - uint64 tmp = (w); \ - tmp = (tmp >> 32) | (tmp << 32); \ - tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ - ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ - (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ - ((tmp & 0x0000ffff0000ffffULL) << 16); \ -} -#endif /* not bigendian */ - -/* - * Macro for incrementally adding the unsigned 64-bit integer n to the - * unsigned 128-bit integer (represented using a two-element array of - * 64-bit words): - */ -#define ADDINC128(w,n) { \ - (w)[0] += (uint64)(n); \ - if ((w)[0] < (n)) { \ - (w)[1]++; \ - } \ -} - -/*** THE SIX LOGICAL FUNCTIONS ****************************************/ -/* - * Bit shifting and rotation (used by the six SHA-XYZ logical functions: - * - * NOTE: The naming of R and S appears backwards here (R is a SHIFT and - * S is a ROTATION) because the SHA-256/384/512 description document - * (see http://www.iwar.org.uk/comsec/resources/cipher/sha256-384-512.pdf) - * uses this same "backwards" definition. - */ -/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ -#define R(b,x) ((x) >> (b)) -/* 32-bit Rotate-right (used in SHA-256): */ -#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) -/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ -#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) - -/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ -#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) -#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) - -/* Four of six logical functions used in SHA-256: */ -#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) -#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) -#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) -#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) - -/* Four of six logical functions used in SHA-384 and SHA-512: */ -#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) -#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) -#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) -#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) - -/*** INTERNAL FUNCTION PROTOTYPES *************************************/ -/* NOTE: These should not be accessed directly from outside this - * library -- they are intended for private internal visibility/use - * only. - */ -static void SHA512_Last(SHA512_CTX *); -static void SHA256_Transform(SHA256_CTX *, const uint8 *); -static void SHA512_Transform(SHA512_CTX *, const uint8 *); - - -/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ -/* Hash constant words K for SHA-256: */ -static const uint32 K256[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, - 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, - 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, - 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, - 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, - 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, - 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, - 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, - 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, - 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, - 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, - 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, - 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL -}; - -/* Initial hash value H for SHA-224: */ -static const uint32 sha224_initial_hash_value[8] = { - 0xc1059ed8UL, - 0x367cd507UL, - 0x3070dd17UL, - 0xf70e5939UL, - 0xffc00b31UL, - 0x68581511UL, - 0x64f98fa7UL, - 0xbefa4fa4UL -}; - -/* Initial hash value H for SHA-256: */ -static const uint32 sha256_initial_hash_value[8] = { - 0x6a09e667UL, - 0xbb67ae85UL, - 0x3c6ef372UL, - 0xa54ff53aUL, - 0x510e527fUL, - 0x9b05688cUL, - 0x1f83d9abUL, - 0x5be0cd19UL -}; - -/* Hash constant words K for SHA-384 and SHA-512: */ -static const uint64 K512[80] = { - 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL -}; - -/* Initial hash value H for SHA-384 */ -static const uint64 sha384_initial_hash_value[8] = { - 0xcbbb9d5dc1059ed8ULL, - 0x629a292a367cd507ULL, - 0x9159015a3070dd17ULL, - 0x152fecd8f70e5939ULL, - 0x67332667ffc00b31ULL, - 0x8eb44a8768581511ULL, - 0xdb0c2e0d64f98fa7ULL, - 0x47b5481dbefa4fa4ULL -}; - -/* Initial hash value H for SHA-512 */ -static const uint64 sha512_initial_hash_value[8] = { - 0x6a09e667f3bcc908ULL, - 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, - 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, - 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, - 0x5be0cd19137e2179ULL -}; - - -/*** SHA-256: *********************************************************/ -void -SHA256_Init(SHA256_CTX *context) -{ - if (context == NULL) - return; - memcpy(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); - memset(context->buffer, 0, SHA256_BLOCK_LENGTH); - context->bitcount = 0; -} - -#ifdef SHA2_UNROLL_TRANSFORM - -/* Unrolled SHA-256 round macros: */ - -#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) do { \ - W256[j] = (uint32)data[3] | ((uint32)data[2] << 8) | \ - ((uint32)data[1] << 16) | ((uint32)data[0] << 24); \ - data += 4; \ - T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + W256[j]; \ - (d) += T1; \ - (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - -#define ROUND256(a,b,c,d,e,f,g,h) do { \ - s0 = W256[(j+1)&0x0f]; \ - s0 = sigma0_256(s0); \ - s1 = W256[(j+14)&0x0f]; \ - s1 = sigma1_256(s1); \ - T1 = (h) + Sigma1_256((e)) + Ch((e), (f), (g)) + K256[j] + \ - (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ - (d) += T1; \ - (h) = T1 + Sigma0_256((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - -static void -SHA256_Transform(SHA256_CTX *context, const uint8 *data) -{ - uint32 a, - b, - c, - d, - e, - f, - g, - h, - s0, - s1; - uint32 T1, - *W256; - int j; - - W256 = (uint32 *) context->buffer; - - /* Initialize registers with the prev. intermediate value */ - a = context->state[0]; - b = context->state[1]; - c = context->state[2]; - d = context->state[3]; - e = context->state[4]; - f = context->state[5]; - g = context->state[6]; - h = context->state[7]; - - j = 0; - do - { - /* Rounds 0 to 15 (unrolled): */ - ROUND256_0_TO_15(a, b, c, d, e, f, g, h); - ROUND256_0_TO_15(h, a, b, c, d, e, f, g); - ROUND256_0_TO_15(g, h, a, b, c, d, e, f); - ROUND256_0_TO_15(f, g, h, a, b, c, d, e); - ROUND256_0_TO_15(e, f, g, h, a, b, c, d); - ROUND256_0_TO_15(d, e, f, g, h, a, b, c); - ROUND256_0_TO_15(c, d, e, f, g, h, a, b); - ROUND256_0_TO_15(b, c, d, e, f, g, h, a); - } while (j < 16); - - /* Now for the remaining rounds to 64: */ - do - { - ROUND256(a, b, c, d, e, f, g, h); - ROUND256(h, a, b, c, d, e, f, g); - ROUND256(g, h, a, b, c, d, e, f); - ROUND256(f, g, h, a, b, c, d, e); - ROUND256(e, f, g, h, a, b, c, d); - ROUND256(d, e, f, g, h, a, b, c); - ROUND256(c, d, e, f, g, h, a, b); - ROUND256(b, c, d, e, f, g, h, a); - } while (j < 64); - - /* Compute the current intermediate hash value */ - context->state[0] += a; - context->state[1] += b; - context->state[2] += c; - context->state[3] += d; - context->state[4] += e; - context->state[5] += f; - context->state[6] += g; - context->state[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = 0; -} -#else /* SHA2_UNROLL_TRANSFORM */ - -static void -SHA256_Transform(SHA256_CTX *context, const uint8 *data) -{ - uint32 a, - b, - c, - d, - e, - f, - g, - h, - s0, - s1; - uint32 T1, - T2, - *W256; - int j; - - W256 = (uint32 *) context->buffer; - - /* Initialize registers with the prev. intermediate value */ - a = context->state[0]; - b = context->state[1]; - c = context->state[2]; - d = context->state[3]; - e = context->state[4]; - f = context->state[5]; - g = context->state[6]; - h = context->state[7]; - - j = 0; - do - { - W256[j] = (uint32) data[3] | ((uint32) data[2] << 8) | - ((uint32) data[1] << 16) | ((uint32) data[0] << 24); - data += 4; - /* Apply the SHA-256 compression function to update a..h */ - T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; - T2 = Sigma0_256(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 16); - - do - { - /* Part of the message block expansion: */ - s0 = W256[(j + 1) & 0x0f]; - s0 = sigma0_256(s0); - s1 = W256[(j + 14) & 0x0f]; - s1 = sigma1_256(s1); - - /* Apply the SHA-256 compression function to update a..h */ - T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + - (W256[j & 0x0f] += s1 + W256[(j + 9) & 0x0f] + s0); - T2 = Sigma0_256(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 64); - - /* Compute the current intermediate hash value */ - context->state[0] += a; - context->state[1] += b; - context->state[2] += c; - context->state[3] += d; - context->state[4] += e; - context->state[5] += f; - context->state[6] += g; - context->state[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = T2 = 0; -} -#endif /* SHA2_UNROLL_TRANSFORM */ - -void -SHA256_Update(SHA256_CTX *context, const uint8 *data, size_t len) -{ - size_t freespace, - usedspace; - - /* Calling with no data is valid (we do nothing) */ - if (len == 0) - return; - - usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; - if (usedspace > 0) - { - /* Calculate how much free space is available in the buffer */ - freespace = SHA256_BLOCK_LENGTH - usedspace; - - if (len >= freespace) - { - /* Fill the buffer completely and process it */ - memcpy(&context->buffer[usedspace], data, freespace); - context->bitcount += freespace << 3; - len -= freespace; - data += freespace; - SHA256_Transform(context, context->buffer); - } - else - { - /* The buffer is not yet full */ - memcpy(&context->buffer[usedspace], data, len); - context->bitcount += len << 3; - /* Clean up: */ - usedspace = freespace = 0; - return; - } - } - while (len >= SHA256_BLOCK_LENGTH) - { - /* Process as many complete blocks as we can */ - SHA256_Transform(context, data); - context->bitcount += SHA256_BLOCK_LENGTH << 3; - len -= SHA256_BLOCK_LENGTH; - data += SHA256_BLOCK_LENGTH; - } - if (len > 0) - { - /* There's left-overs, so save 'em */ - memcpy(context->buffer, data, len); - context->bitcount += len << 3; - } - /* Clean up: */ - usedspace = freespace = 0; -} - -static void -SHA256_Last(SHA256_CTX *context) -{ - unsigned int usedspace; - - usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; -#ifndef WORDS_BIGENDIAN - /* Convert FROM host byte order */ - REVERSE64(context->bitcount, context->bitcount); -#endif - if (usedspace > 0) - { - /* Begin padding with a 1 bit: */ - context->buffer[usedspace++] = 0x80; - - if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) - { - /* Set-up for the last transform: */ - memset(&context->buffer[usedspace], 0, SHA256_SHORT_BLOCK_LENGTH - usedspace); - } - else - { - if (usedspace < SHA256_BLOCK_LENGTH) - { - memset(&context->buffer[usedspace], 0, SHA256_BLOCK_LENGTH - usedspace); - } - /* Do second-to-last transform: */ - SHA256_Transform(context, context->buffer); - - /* And set-up for the last transform: */ - memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH); - } - } - else - { - /* Set-up for the last transform: */ - memset(context->buffer, 0, SHA256_SHORT_BLOCK_LENGTH); - - /* Begin padding with a 1 bit: */ - *context->buffer = 0x80; - } - /* Set the bit count: */ - *(uint64 *) &context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; - - /* Final transform: */ - SHA256_Transform(context, context->buffer); -} - -void -SHA256_Final(uint8 digest[], SHA256_CTX *context) -{ - /* If no digest buffer is passed, we don't bother doing this: */ - if (digest != NULL) - { - SHA256_Last(context); - -#ifndef WORDS_BIGENDIAN - { - /* Convert TO host byte order */ - int j; - - for (j = 0; j < 8; j++) - { - REVERSE32(context->state[j], context->state[j]); - } - } -#endif - memcpy(digest, context->state, SHA256_DIGEST_LENGTH); - } - - /* Clean up state data: */ - px_memset(context, 0, sizeof(*context)); -} - - -/*** SHA-512: *********************************************************/ -void -SHA512_Init(SHA512_CTX *context) -{ - if (context == NULL) - return; - memcpy(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); - memset(context->buffer, 0, SHA512_BLOCK_LENGTH); - context->bitcount[0] = context->bitcount[1] = 0; -} - -#ifdef SHA2_UNROLL_TRANSFORM - -/* Unrolled SHA-512 round macros: */ - -#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) do { \ - W512[j] = (uint64)data[7] | ((uint64)data[6] << 8) | \ - ((uint64)data[5] << 16) | ((uint64)data[4] << 24) | \ - ((uint64)data[3] << 32) | ((uint64)data[2] << 40) | \ - ((uint64)data[1] << 48) | ((uint64)data[0] << 56); \ - data += 8; \ - T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + W512[j]; \ - (d) += T1; \ - (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - - -#define ROUND512(a,b,c,d,e,f,g,h) do { \ - s0 = W512[(j+1)&0x0f]; \ - s0 = sigma0_512(s0); \ - s1 = W512[(j+14)&0x0f]; \ - s1 = sigma1_512(s1); \ - T1 = (h) + Sigma1_512((e)) + Ch((e), (f), (g)) + K512[j] + \ - (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ - (d) += T1; \ - (h) = T1 + Sigma0_512((a)) + Maj((a), (b), (c)); \ - j++; \ -} while(0) - -static void -SHA512_Transform(SHA512_CTX *context, const uint8 *data) -{ - uint64 a, - b, - c, - d, - e, - f, - g, - h, - s0, - s1; - uint64 T1, - *W512 = (uint64 *) context->buffer; - int j; - - /* Initialize registers with the prev. intermediate value */ - a = context->state[0]; - b = context->state[1]; - c = context->state[2]; - d = context->state[3]; - e = context->state[4]; - f = context->state[5]; - g = context->state[6]; - h = context->state[7]; - - j = 0; - do - { - ROUND512_0_TO_15(a, b, c, d, e, f, g, h); - ROUND512_0_TO_15(h, a, b, c, d, e, f, g); - ROUND512_0_TO_15(g, h, a, b, c, d, e, f); - ROUND512_0_TO_15(f, g, h, a, b, c, d, e); - ROUND512_0_TO_15(e, f, g, h, a, b, c, d); - ROUND512_0_TO_15(d, e, f, g, h, a, b, c); - ROUND512_0_TO_15(c, d, e, f, g, h, a, b); - ROUND512_0_TO_15(b, c, d, e, f, g, h, a); - } while (j < 16); - - /* Now for the remaining rounds up to 79: */ - do - { - ROUND512(a, b, c, d, e, f, g, h); - ROUND512(h, a, b, c, d, e, f, g); - ROUND512(g, h, a, b, c, d, e, f); - ROUND512(f, g, h, a, b, c, d, e); - ROUND512(e, f, g, h, a, b, c, d); - ROUND512(d, e, f, g, h, a, b, c); - ROUND512(c, d, e, f, g, h, a, b); - ROUND512(b, c, d, e, f, g, h, a); - } while (j < 80); - - /* Compute the current intermediate hash value */ - context->state[0] += a; - context->state[1] += b; - context->state[2] += c; - context->state[3] += d; - context->state[4] += e; - context->state[5] += f; - context->state[6] += g; - context->state[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = 0; -} -#else /* SHA2_UNROLL_TRANSFORM */ - -static void -SHA512_Transform(SHA512_CTX *context, const uint8 *data) -{ - uint64 a, - b, - c, - d, - e, - f, - g, - h, - s0, - s1; - uint64 T1, - T2, - *W512 = (uint64 *) context->buffer; - int j; - - /* Initialize registers with the prev. intermediate value */ - a = context->state[0]; - b = context->state[1]; - c = context->state[2]; - d = context->state[3]; - e = context->state[4]; - f = context->state[5]; - g = context->state[6]; - h = context->state[7]; - - j = 0; - do - { - W512[j] = (uint64) data[7] | ((uint64) data[6] << 8) | - ((uint64) data[5] << 16) | ((uint64) data[4] << 24) | - ((uint64) data[3] << 32) | ((uint64) data[2] << 40) | - ((uint64) data[1] << 48) | ((uint64) data[0] << 56); - data += 8; - /* Apply the SHA-512 compression function to update a..h */ - T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; - T2 = Sigma0_512(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 16); - - do - { - /* Part of the message block expansion: */ - s0 = W512[(j + 1) & 0x0f]; - s0 = sigma0_512(s0); - s1 = W512[(j + 14) & 0x0f]; - s1 = sigma1_512(s1); - - /* Apply the SHA-512 compression function to update a..h */ - T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + - (W512[j & 0x0f] += s1 + W512[(j + 9) & 0x0f] + s0); - T2 = Sigma0_512(a) + Maj(a, b, c); - h = g; - g = f; - f = e; - e = d + T1; - d = c; - c = b; - b = a; - a = T1 + T2; - - j++; - } while (j < 80); - - /* Compute the current intermediate hash value */ - context->state[0] += a; - context->state[1] += b; - context->state[2] += c; - context->state[3] += d; - context->state[4] += e; - context->state[5] += f; - context->state[6] += g; - context->state[7] += h; - - /* Clean up */ - a = b = c = d = e = f = g = h = T1 = T2 = 0; -} -#endif /* SHA2_UNROLL_TRANSFORM */ - -void -SHA512_Update(SHA512_CTX *context, const uint8 *data, size_t len) -{ - size_t freespace, - usedspace; - - /* Calling with no data is valid (we do nothing) */ - if (len == 0) - return; - - usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; - if (usedspace > 0) - { - /* Calculate how much free space is available in the buffer */ - freespace = SHA512_BLOCK_LENGTH - usedspace; - - if (len >= freespace) - { - /* Fill the buffer completely and process it */ - memcpy(&context->buffer[usedspace], data, freespace); - ADDINC128(context->bitcount, freespace << 3); - len -= freespace; - data += freespace; - SHA512_Transform(context, context->buffer); - } - else - { - /* The buffer is not yet full */ - memcpy(&context->buffer[usedspace], data, len); - ADDINC128(context->bitcount, len << 3); - /* Clean up: */ - usedspace = freespace = 0; - return; - } - } - while (len >= SHA512_BLOCK_LENGTH) - { - /* Process as many complete blocks as we can */ - SHA512_Transform(context, data); - ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); - len -= SHA512_BLOCK_LENGTH; - data += SHA512_BLOCK_LENGTH; - } - if (len > 0) - { - /* There's left-overs, so save 'em */ - memcpy(context->buffer, data, len); - ADDINC128(context->bitcount, len << 3); - } - /* Clean up: */ - usedspace = freespace = 0; -} - -static void -SHA512_Last(SHA512_CTX *context) -{ - unsigned int usedspace; - - usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; -#ifndef WORDS_BIGENDIAN - /* Convert FROM host byte order */ - REVERSE64(context->bitcount[0], context->bitcount[0]); - REVERSE64(context->bitcount[1], context->bitcount[1]); -#endif - if (usedspace > 0) - { - /* Begin padding with a 1 bit: */ - context->buffer[usedspace++] = 0x80; - - if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) - { - /* Set-up for the last transform: */ - memset(&context->buffer[usedspace], 0, SHA512_SHORT_BLOCK_LENGTH - usedspace); - } - else - { - if (usedspace < SHA512_BLOCK_LENGTH) - { - memset(&context->buffer[usedspace], 0, SHA512_BLOCK_LENGTH - usedspace); - } - /* Do second-to-last transform: */ - SHA512_Transform(context, context->buffer); - - /* And set-up for the last transform: */ - memset(context->buffer, 0, SHA512_BLOCK_LENGTH - 2); - } - } - else - { - /* Prepare for final transform: */ - memset(context->buffer, 0, SHA512_SHORT_BLOCK_LENGTH); - - /* Begin padding with a 1 bit: */ - *context->buffer = 0x80; - } - /* Store the length of input data (in bits): */ - *(uint64 *) &context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; - *(uint64 *) &context->buffer[SHA512_SHORT_BLOCK_LENGTH + 8] = context->bitcount[0]; - - /* Final transform: */ - SHA512_Transform(context, context->buffer); -} - -void -SHA512_Final(uint8 digest[], SHA512_CTX *context) -{ - /* If no digest buffer is passed, we don't bother doing this: */ - if (digest != NULL) - { - SHA512_Last(context); - - /* Save the hash data for output: */ -#ifndef WORDS_BIGENDIAN - { - /* Convert TO host byte order */ - int j; - - for (j = 0; j < 8; j++) - { - REVERSE64(context->state[j], context->state[j]); - } - } -#endif - memcpy(digest, context->state, SHA512_DIGEST_LENGTH); - } - - /* Zero out state data */ - px_memset(context, 0, sizeof(*context)); -} - - -/*** SHA-384: *********************************************************/ -void -SHA384_Init(SHA384_CTX *context) -{ - if (context == NULL) - return; - memcpy(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH); - memset(context->buffer, 0, SHA384_BLOCK_LENGTH); - context->bitcount[0] = context->bitcount[1] = 0; -} - -void -SHA384_Update(SHA384_CTX *context, const uint8 *data, size_t len) -{ - SHA512_Update((SHA512_CTX *) context, data, len); -} - -void -SHA384_Final(uint8 digest[], SHA384_CTX *context) -{ - /* If no digest buffer is passed, we don't bother doing this: */ - if (digest != NULL) - { - SHA512_Last((SHA512_CTX *) context); - - /* Save the hash data for output: */ -#ifndef WORDS_BIGENDIAN - { - /* Convert TO host byte order */ - int j; - - for (j = 0; j < 6; j++) - { - REVERSE64(context->state[j], context->state[j]); - } - } -#endif - memcpy(digest, context->state, SHA384_DIGEST_LENGTH); - } - - /* Zero out state data */ - px_memset(context, 0, sizeof(*context)); -} - -/*** SHA-224: *********************************************************/ -void -SHA224_Init(SHA224_CTX *context) -{ - if (context == NULL) - return; - memcpy(context->state, sha224_initial_hash_value, SHA256_DIGEST_LENGTH); - memset(context->buffer, 0, SHA256_BLOCK_LENGTH); - context->bitcount = 0; -} - -void -SHA224_Update(SHA224_CTX *context, const uint8 *data, size_t len) -{ - SHA256_Update((SHA256_CTX *) context, data, len); -} - -void -SHA224_Final(uint8 digest[], SHA224_CTX *context) -{ - /* If no digest buffer is passed, we don't bother doing this: */ - if (digest != NULL) - { - SHA256_Last(context); - -#ifndef WORDS_BIGENDIAN - { - /* Convert TO host byte order */ - int j; - - for (j = 0; j < 8; j++) - { - REVERSE32(context->state[j], context->state[j]); - } - } -#endif - memcpy(digest, context->state, SHA224_DIGEST_LENGTH); - } - - /* Clean up state data: */ - px_memset(context, 0, sizeof(*context)); -} diff --git a/contrib/pgcrypto/sha2.h b/contrib/pgcrypto/sha2.h deleted file mode 100644 index 501f0e0446..0000000000 --- a/contrib/pgcrypto/sha2.h +++ /dev/null @@ -1,100 +0,0 @@ -/* contrib/pgcrypto/sha2.h */ -/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */ - -/* - * FILE: sha2.h - * AUTHOR: Aaron D. Gifford <me@aarongifford.com> - * - * Copyright (c) 2000-2001, Aaron D. Gifford - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $ - */ - -#ifndef _SHA2_H -#define _SHA2_H - -/* avoid conflict with OpenSSL */ -#define SHA256_Init pg_SHA256_Init -#define SHA256_Update pg_SHA256_Update -#define SHA256_Final pg_SHA256_Final -#define SHA384_Init pg_SHA384_Init -#define SHA384_Update pg_SHA384_Update -#define SHA384_Final pg_SHA384_Final -#define SHA512_Init pg_SHA512_Init -#define SHA512_Update pg_SHA512_Update -#define SHA512_Final pg_SHA512_Final - -/*** SHA-224/256/384/512 Various Length Definitions ***********************/ -#define SHA224_BLOCK_LENGTH 64 -#define SHA224_DIGEST_LENGTH 28 -#define SHA224_DIGEST_STRING_LENGTH (SHA224_DIGEST_LENGTH * 2 + 1) -#define SHA256_BLOCK_LENGTH 64 -#define SHA256_DIGEST_LENGTH 32 -#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) -#define SHA384_BLOCK_LENGTH 128 -#define SHA384_DIGEST_LENGTH 48 -#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) -#define SHA512_BLOCK_LENGTH 128 -#define SHA512_DIGEST_LENGTH 64 -#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) - - -/*** SHA-256/384/512 Context Structures *******************************/ -typedef struct _SHA256_CTX -{ - uint32 state[8]; - uint64 bitcount; - uint8 buffer[SHA256_BLOCK_LENGTH]; -} SHA256_CTX; -typedef struct _SHA512_CTX -{ - uint64 state[8]; - uint64 bitcount[2]; - uint8 buffer[SHA512_BLOCK_LENGTH]; -} SHA512_CTX; - -typedef SHA256_CTX SHA224_CTX; -typedef SHA512_CTX SHA384_CTX; - -void SHA224_Init(SHA224_CTX *); -void SHA224_Update(SHA224_CTX *, const uint8 *, size_t); -void SHA224_Final(uint8[SHA224_DIGEST_LENGTH], SHA224_CTX *); - -void SHA256_Init(SHA256_CTX *); -void SHA256_Update(SHA256_CTX *, const uint8 *, size_t); -void SHA256_Final(uint8[SHA256_DIGEST_LENGTH], SHA256_CTX *); - -void SHA384_Init(SHA384_CTX *); -void SHA384_Update(SHA384_CTX *, const uint8 *, size_t); -void SHA384_Final(uint8[SHA384_DIGEST_LENGTH], SHA384_CTX *); - -void SHA512_Init(SHA512_CTX *); -void SHA512_Update(SHA512_CTX *, const uint8 *, size_t); -void SHA512_Final(uint8[SHA512_DIGEST_LENGTH], SHA512_CTX *); - -#endif /* _SHA2_H */ diff --git a/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql b/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql deleted file mode 100644 index 4122300d97..0000000000 --- a/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql +++ /dev/null @@ -1 +0,0 @@ --- no random source diff --git a/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql b/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql deleted file mode 100644 index d35c0971ba..0000000000 --- a/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql +++ /dev/null @@ -1 +0,0 @@ --- no bignum support diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c index e20e7f83de..00e2015c5c 100644 --- a/contrib/pgrowlocks/pgrowlocks.c +++ b/contrib/pgrowlocks/pgrowlocks.c @@ -28,6 +28,7 @@ #include "access/relscan.h" #include "access/xact.h" #include "catalog/namespace.h" +#include "catalog/pg_authid.h" #include "funcapi.h" #include "miscadmin.h" #include "storage/bufmgr.h" @@ -37,6 +38,7 @@ #include "utils/rel.h" #include "utils/snapmgr.h" #include "utils/tqual.h" +#include "utils/varlena.h" PG_MODULE_MAGIC; @@ -93,14 +95,20 @@ pgrowlocks(PG_FUNCTION_ARGS) attinmeta = TupleDescGetAttInMetadata(tupdesc); funcctx->attinmeta = attinmeta; - relname = PG_GETARG_TEXT_P(0); + relname = PG_GETARG_TEXT_PP(0); relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = heap_openrv(relrv, AccessShareLock); - /* check permissions: must have SELECT on table */ + /* + * check permissions: must have SELECT on table or be in + * pg_stat_scan_tables + */ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) + aclresult = is_member_of_role(GetUserId(), DEFAULT_ROLE_STAT_SCAN_TABLES) ? ACLCHECK_OK : ACLCHECK_NO_PRIV; + + if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); diff --git a/contrib/pgstattuple/Makefile b/contrib/pgstattuple/Makefile index e732680dea..294077d4c1 100644 --- a/contrib/pgstattuple/Makefile +++ b/contrib/pgstattuple/Makefile @@ -4,9 +4,10 @@ MODULE_big = pgstattuple OBJS = pgstattuple.o pgstatindex.o pgstatapprox.o $(WIN32RES) EXTENSION = pgstattuple -DATA = pgstattuple--1.4.sql pgstattuple--1.3--1.4.sql \ - pgstattuple--1.2--1.3.sql pgstattuple--1.1--1.2.sql \ - pgstattuple--1.0--1.1.sql pgstattuple--unpackaged--1.0.sql +DATA = pgstattuple--1.4.sql pgstattuple--1.4--1.5.sql \ + pgstattuple--1.3--1.4.sql pgstattuple--1.2--1.3.sql \ + pgstattuple--1.1--1.2.sql pgstattuple--1.0--1.1.sql \ + pgstattuple--unpackaged--1.0.sql PGFILEDESC = "pgstattuple - tuple-level statistics" REGRESS = pgstattuple diff --git a/contrib/pgstattuple/expected/pgstattuple.out b/contrib/pgstattuple/expected/pgstattuple.out index e920234488..c7c1732827 100644 --- a/contrib/pgstattuple/expected/pgstattuple.out +++ b/contrib/pgstattuple/expected/pgstattuple.out @@ -130,3 +130,116 @@ select * from pgstatginindex('test_ginidx'); 2 | 0 | 0 (1 row) +create index test_hashidx on test using hash (b); +select * from pgstathashindex('test_hashidx'); + version | bucket_pages | overflow_pages | bitmap_pages | unused_pages | live_items | dead_items | free_percent +---------+--------------+----------------+--------------+--------------+------------+------------+-------------- + 3 | 4 | 0 | 1 | 0 | 0 | 0 | 100 +(1 row) + +-- these should error with the wrong type +select pgstatginindex('test_pkey'); +ERROR: relation "test_pkey" is not a GIN index +select pgstathashindex('test_pkey'); +ERROR: relation "test_pkey" is not a HASH index +select pgstatindex('test_ginidx'); +ERROR: relation "test_ginidx" is not a btree index +select pgstathashindex('test_ginidx'); +ERROR: relation "test_ginidx" is not a HASH index +select pgstatindex('test_hashidx'); +ERROR: relation "test_hashidx" is not a btree index +select pgstatginindex('test_hashidx'); +ERROR: relation "test_hashidx" is not a GIN index +-- check that using any of these functions with unsupported relations will fail +create table test_partitioned (a int) partition by range (a); +-- these should all fail +select pgstattuple('test_partitioned'); +ERROR: "test_partitioned" (partitioned table) is not supported +select pgstattuple_approx('test_partitioned'); +ERROR: "test_partitioned" is not a table or materialized view +select pg_relpages('test_partitioned'); +ERROR: "test_partitioned" is not a table, index, materialized view, sequence, or TOAST table +select pgstatindex('test_partitioned'); +ERROR: relation "test_partitioned" is not a btree index +select pgstatginindex('test_partitioned'); +ERROR: relation "test_partitioned" is not a GIN index +select pgstathashindex('test_partitioned'); +ERROR: "test_partitioned" is not an index +create view test_view as select 1; +-- these should all fail +select pgstattuple('test_view'); +ERROR: "test_view" (view) is not supported +select pgstattuple_approx('test_view'); +ERROR: "test_view" is not a table or materialized view +select pg_relpages('test_view'); +ERROR: "test_view" is not a table, index, materialized view, sequence, or TOAST table +select pgstatindex('test_view'); +ERROR: relation "test_view" is not a btree index +select pgstatginindex('test_view'); +ERROR: relation "test_view" is not a GIN index +select pgstathashindex('test_view'); +ERROR: "test_view" is not an index +create foreign data wrapper dummy; +create server dummy_server foreign data wrapper dummy; +create foreign table test_foreign_table () server dummy_server; +-- these should all fail +select pgstattuple('test_foreign_table'); +ERROR: "test_foreign_table" (foreign table) is not supported +select pgstattuple_approx('test_foreign_table'); +ERROR: "test_foreign_table" is not a table or materialized view +select pg_relpages('test_foreign_table'); +ERROR: "test_foreign_table" is not a table, index, materialized view, sequence, or TOAST table +select pgstatindex('test_foreign_table'); +ERROR: relation "test_foreign_table" is not a btree index +select pgstatginindex('test_foreign_table'); +ERROR: relation "test_foreign_table" is not a GIN index +select pgstathashindex('test_foreign_table'); +ERROR: "test_foreign_table" is not an index +-- a partition of a partitioned table should work though +create table test_partition partition of test_partitioned for values from (1) to (100); +select pgstattuple('test_partition'); + pgstattuple +--------------------- + (0,0,0,0,0,0,0,0,0) +(1 row) + +select pgstattuple_approx('test_partition'); + pgstattuple_approx +----------------------- + (0,0,0,0,0,0,0,0,0,0) +(1 row) + +select pg_relpages('test_partition'); + pg_relpages +------------- + 0 +(1 row) + +-- not for the index calls though, of course +select pgstatindex('test_partition'); +ERROR: relation "test_partition" is not a btree index +select pgstatginindex('test_partition'); +ERROR: relation "test_partition" is not a GIN index +select pgstathashindex('test_partition'); +ERROR: "test_partition" is not an index +-- an actual index of a partitioned table should work though +create index test_partition_idx on test_partition(a); +create index test_partition_hash_idx on test_partition using hash (a); +-- these should work +select pgstatindex('test_partition_idx'); + pgstatindex +------------------------------ + (2,0,8192,0,0,0,0,0,NaN,NaN) +(1 row) + +select pgstathashindex('test_partition_hash_idx'); + pgstathashindex +--------------------- + (3,8,0,1,0,0,0,100) +(1 row) + +drop table test_partitioned; +drop view test_view; +drop foreign table test_foreign_table; +drop server dummy_server; +drop foreign data wrapper dummy; diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c index a49ff543d2..9facf65137 100644 --- a/contrib/pgstattuple/pgstatapprox.c +++ b/contrib/pgstattuple/pgstatapprox.c @@ -3,7 +3,7 @@ * pgstatapprox.c * Bloat estimation functions * - * Copyright (c) 2014-2016, PostgreSQL Global Development Group + * Copyright (c) 2014-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/pgstattuple/pgstatapprox.c @@ -29,6 +29,9 @@ #include "commands/vacuum.h" PG_FUNCTION_INFO_V1(pgstattuple_approx); +PG_FUNCTION_INFO_V1(pgstattuple_approx_v1_5); + +Datum pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo); typedef struct output_type { @@ -67,7 +70,7 @@ statapprox_heap(Relation rel, output_type *stat) TransactionId OldestXmin; uint64 misc_count = 0; - OldestXmin = GetOldestXmin(rel, true); + OldestXmin = GetOldestXmin(rel, PROCARRAY_FLAGS_VACUUM); bstrategy = GetAccessStrategy(BAS_BULKREAD); nblocks = RelationGetNumberOfBlocks(rel); @@ -204,11 +207,42 @@ statapprox_heap(Relation rel, output_type *stat) /* * Returns estimated live/dead tuple statistics for the given relid. + * + * The superuser() check here must be kept as the library might be upgraded + * without the extension being upgraded, meaning that in pre-1.5 installations + * these functions could be called by any user. */ Datum pgstattuple_approx(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use pgstattuple functions")))); + + PG_RETURN_DATUM(pgstattuple_approx_internal(relid, fcinfo)); +} + +/* + * As of pgstattuple version 1.5, we no longer need to check if the user + * is a superuser because we REVOKE EXECUTE on the SQL function from PUBLIC. + * Users can then grant access to it based on their policies. + * + * Otherwise identical to pgstattuple_approx (above). + */ +Datum +pgstattuple_approx_v1_5(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + + PG_RETURN_DATUM(pgstattuple_approx_internal(relid, fcinfo)); +} + +Datum +pgstattuple_approx_internal(Oid relid, FunctionCallInfo fcinfo) +{ Relation rel; output_type stat = {0}; TupleDesc tupdesc; @@ -217,11 +251,6 @@ pgstattuple_approx(PG_FUNCTION_ARGS) HeapTuple ret; int i = 0; - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to use pgstattuple functions")))); - if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c index 6084589e07..03b387f6b6 100644 --- a/contrib/pgstattuple/pgstatindex.c +++ b/contrib/pgstattuple/pgstatindex.c @@ -29,6 +29,7 @@ #include "access/gin_private.h" #include "access/heapam.h" +#include "access/hash.h" #include "access/htup_details.h" #include "access/nbtree.h" #include "catalog/namespace.h" @@ -36,8 +37,10 @@ #include "funcapi.h" #include "miscadmin.h" #include "storage/bufmgr.h" +#include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/rel.h" +#include "utils/varlena.h" /* @@ -53,10 +56,20 @@ PG_FUNCTION_INFO_V1(pgstatindexbyid); PG_FUNCTION_INFO_V1(pg_relpages); PG_FUNCTION_INFO_V1(pg_relpagesbyid); PG_FUNCTION_INFO_V1(pgstatginindex); +PG_FUNCTION_INFO_V1(pgstathashindex); + +PG_FUNCTION_INFO_V1(pgstatindex_v1_5); +PG_FUNCTION_INFO_V1(pgstatindexbyid_v1_5); +PG_FUNCTION_INFO_V1(pg_relpages_v1_5); +PG_FUNCTION_INFO_V1(pg_relpagesbyid_v1_5); +PG_FUNCTION_INFO_V1(pgstatginindex_v1_5); + +Datum pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo); #define IS_INDEX(r) ((r)->rd_rel->relkind == RELKIND_INDEX) #define IS_BTREE(r) ((r)->rd_rel->relam == BTREE_AM_OID) #define IS_GIN(r) ((r)->rd_rel->relam == GIN_AM_OID) +#define IS_HASH(r) ((r)->rd_rel->relam == HASH_AM_OID) /* ------------------------------------------------ * A structure for a whole btree index statistics @@ -93,18 +106,44 @@ typedef struct GinIndexStat int64 pending_tuples; } GinIndexStat; +/* ------------------------------------------------ + * A structure for a whole HASH index statistics + * used by pgstathashindex(). + * ------------------------------------------------ + */ +typedef struct HashIndexStat +{ + int32 version; + int32 space_per_page; + + BlockNumber bucket_pages; + BlockNumber overflow_pages; + BlockNumber bitmap_pages; + BlockNumber unused_pages; + + int64 live_items; + int64 dead_items; + uint64 free_space; +} HashIndexStat; + static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo); +static void GetHashPageStats(Page page, HashIndexStat *stats); +static void check_relation_relkind(Relation rel); /* ------------------------------------------------------ * pgstatindex() * * Usage: SELECT * FROM pgstatindex('t1_pkey'); + * + * The superuser() check here must be kept as the library might be upgraded + * without the extension being upgraded, meaning that in pre-1.5 installations + * these functions could be called by any user. * ------------------------------------------------------ */ Datum pgstatindex(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); Relation rel; RangeVar *relrv; @@ -119,6 +158,31 @@ pgstatindex(PG_FUNCTION_ARGS) PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo)); } +/* + * As of pgstattuple version 1.5, we no longer need to check if the user + * is a superuser because we REVOKE EXECUTE on the function from PUBLIC. + * Users can then grant access to it based on their policies. + * + * Otherwise identical to pgstatindex (above). + */ +Datum +pgstatindex_v1_5(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_PP(0); + Relation rel; + RangeVar *relrv; + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo)); +} + +/* + * The superuser() check here must be kept as the library might be upgraded + * without the extension being upgraded, meaning that in pre-1.5 installations + * these functions could be called by any user. + */ Datum pgstatindexbyid(PG_FUNCTION_ARGS) { @@ -135,6 +199,18 @@ pgstatindexbyid(PG_FUNCTION_ARGS) PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo)); } +/* No need for superuser checks in v1.5, see above */ +Datum +pgstatindexbyid_v1_5(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + Relation rel; + + rel = relation_open(relid, AccessShareLock); + + PG_RETURN_DATUM(pgstatindex_impl(rel, fcinfo)); +} + static Datum pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo) { @@ -145,8 +221,10 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo) BufferAccessStrategy bstrategy = GetAccessStrategy(BAS_BULKREAD); if (!IS_INDEX(rel) || !IS_BTREE(rel)) - elog(ERROR, "relation \"%s\" is not a btree index", - RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("relation \"%s\" is not a btree index", + RelationGetRelationName(rel)))); /* * Reject attempts to read non-local temporary relations; we would be @@ -292,12 +370,14 @@ pgstatindex_impl(Relation rel, FunctionCallInfo fcinfo) * * Usage: SELECT pg_relpages('t1'); * SELECT pg_relpages('t1_pkey'); + * + * Must keep superuser() check, see above. * -------------------------------------------------------- */ Datum pg_relpages(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); int64 relpages; Relation rel; RangeVar *relrv; @@ -310,6 +390,9 @@ pg_relpages(PG_FUNCTION_ARGS) relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); + /* only some relkinds have storage */ + check_relation_relkind(rel); + /* note: this will work OK on non-local temp tables */ relpages = RelationGetNumberOfBlocks(rel); @@ -319,6 +402,31 @@ pg_relpages(PG_FUNCTION_ARGS) PG_RETURN_INT64(relpages); } +/* No need for superuser checks in v1.5, see above */ +Datum +pg_relpages_v1_5(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_PP(0); + int64 relpages; + Relation rel; + RangeVar *relrv; + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + /* only some relkinds have storage */ + check_relation_relkind(rel); + + /* note: this will work OK on non-local temp tables */ + + relpages = RelationGetNumberOfBlocks(rel); + + relation_close(rel, AccessShareLock); + + PG_RETURN_INT64(relpages); +} + +/* Must keep superuser() check, see above. */ Datum pg_relpagesbyid(PG_FUNCTION_ARGS) { @@ -333,6 +441,31 @@ pg_relpagesbyid(PG_FUNCTION_ARGS) rel = relation_open(relid, AccessShareLock); + /* only some relkinds have storage */ + check_relation_relkind(rel); + + /* note: this will work OK on non-local temp tables */ + + relpages = RelationGetNumberOfBlocks(rel); + + relation_close(rel, AccessShareLock); + + PG_RETURN_INT64(relpages); +} + +/* No need for superuser checks in v1.5, see above */ +Datum +pg_relpagesbyid_v1_5(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + int64 relpages; + Relation rel; + + rel = relation_open(relid, AccessShareLock); + + /* only some relkinds have storage */ + check_relation_relkind(rel); + /* note: this will work OK on non-local temp tables */ relpages = RelationGetNumberOfBlocks(rel); @@ -346,12 +479,35 @@ pg_relpagesbyid(PG_FUNCTION_ARGS) * pgstatginindex() * * Usage: SELECT * FROM pgstatginindex('ginindex'); + * + * Must keep superuser() check, see above. * ------------------------------------------------------ */ Datum pgstatginindex(PG_FUNCTION_ARGS) { Oid relid = PG_GETARG_OID(0); + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use pgstattuple functions")))); + + PG_RETURN_DATUM(pgstatginindex_internal(relid, fcinfo)); +} + +/* No need for superuser checks in v1.5, see above */ +Datum +pgstatginindex_v1_5(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + + PG_RETURN_DATUM(pgstatginindex_internal(relid, fcinfo)); +} + +Datum +pgstatginindex_internal(Oid relid, FunctionCallInfo fcinfo) +{ Relation rel; Buffer buffer; Page page; @@ -363,16 +519,13 @@ pgstatginindex(PG_FUNCTION_ARGS) bool nulls[3] = {false, false, false}; Datum result; - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to use pgstattuple functions")))); - rel = relation_open(relid, AccessShareLock); if (!IS_INDEX(rel) || !IS_GIN(rel)) - elog(ERROR, "relation \"%s\" is not a GIN index", - RelationGetRelationName(rel)); + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("relation \"%s\" is not a GIN index", + RelationGetRelationName(rel)))); /* * Reject attempts to read non-local temporary relations; we would be @@ -415,5 +568,202 @@ pgstatginindex(PG_FUNCTION_ARGS) tuple = heap_form_tuple(tupleDesc, values, nulls); result = HeapTupleGetDatum(tuple); - PG_RETURN_DATUM(result); + return (result); +} + +/* ------------------------------------------------------ + * pgstathashindex() + * + * Usage: SELECT * FROM pgstathashindex('hashindex'); + * ------------------------------------------------------ + */ +Datum +pgstathashindex(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + BlockNumber nblocks; + BlockNumber blkno; + Relation rel; + HashIndexStat stats; + BufferAccessStrategy bstrategy; + HeapTuple tuple; + TupleDesc tupleDesc; + Datum values[8]; + bool nulls[8]; + Buffer metabuf; + HashMetaPage metap; + float8 free_percent; + uint64 total_space; + + rel = index_open(relid, AccessShareLock); + + /* index_open() checks that it's an index */ + if (!IS_HASH(rel)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("relation \"%s\" is not a HASH index", + RelationGetRelationName(rel)))); + + + /* + * Reject attempts to read non-local temporary relations; we would be + * likely to get wrong data since we have no visibility into the owning + * session's local buffers. + */ + if (RELATION_IS_OTHER_TEMP(rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary indexes of other sessions"))); + + /* Get the information we need from the metapage. */ + memset(&stats, 0, sizeof(stats)); + metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE); + metap = HashPageGetMeta(BufferGetPage(metabuf)); + stats.version = metap->hashm_version; + stats.space_per_page = metap->hashm_bsize; + _hash_relbuf(rel, metabuf); + + /* Get the current relation length */ + nblocks = RelationGetNumberOfBlocks(rel); + + /* prepare access strategy for this index */ + bstrategy = GetAccessStrategy(BAS_BULKREAD); + + /* Start from blkno 1 as 0th block is metapage */ + for (blkno = 1; blkno < nblocks; blkno++) + { + Buffer buf; + Page page; + + CHECK_FOR_INTERRUPTS(); + + buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, + bstrategy); + LockBuffer(buf, BUFFER_LOCK_SHARE); + page = (Page) BufferGetPage(buf); + + if (PageIsNew(page)) + stats.unused_pages++; + else if (PageGetSpecialSize(page) != + MAXALIGN(sizeof(HashPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("index \"%s\" contains corrupted page at block %u", + RelationGetRelationName(rel), + BufferGetBlockNumber(buf)))); + else + { + HashPageOpaque opaque; + int pagetype; + + opaque = (HashPageOpaque) PageGetSpecialPointer(page); + pagetype = opaque->hasho_flag & LH_PAGE_TYPE; + + if (pagetype == LH_BUCKET_PAGE) + { + stats.bucket_pages++; + GetHashPageStats(page, &stats); + } + else if (pagetype == LH_OVERFLOW_PAGE) + { + stats.overflow_pages++; + GetHashPageStats(page, &stats); + } + else if (pagetype == LH_BITMAP_PAGE) + stats.bitmap_pages++; + else if (pagetype == LH_UNUSED_PAGE) + stats.unused_pages++; + else + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("unexpected page type 0x%04X in HASH index \"%s\" block %u", + opaque->hasho_flag, RelationGetRelationName(rel), + BufferGetBlockNumber(buf)))); + } + UnlockReleaseBuffer(buf); + } + + /* Done accessing the index */ + index_close(rel, AccessShareLock); + + /* Count unused pages as free space. */ + stats.free_space += stats.unused_pages * stats.space_per_page; + + /* + * Total space available for tuples excludes the metapage and the bitmap + * pages. + */ + total_space = (nblocks - (stats.bitmap_pages + 1)) * stats.space_per_page; + + if (total_space == 0) + free_percent = 0.0; + else + free_percent = 100.0 * stats.free_space / total_space; + + /* + * Build a tuple descriptor for our result type + */ + if (get_call_result_type(fcinfo, NULL, &tupleDesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + tupleDesc = BlessTupleDesc(tupleDesc); + + /* + * Build and return the tuple + */ + MemSet(nulls, 0, sizeof(nulls)); + values[0] = Int32GetDatum(stats.version); + values[1] = Int64GetDatum((int64) stats.bucket_pages); + values[2] = Int64GetDatum((int64) stats.overflow_pages); + values[3] = Int64GetDatum((int64) stats.bitmap_pages); + values[4] = Int64GetDatum((int64) stats.unused_pages); + values[5] = Int64GetDatum(stats.live_items); + values[6] = Int64GetDatum(stats.dead_items); + values[7] = Float8GetDatum(free_percent); + tuple = heap_form_tuple(tupleDesc, values, nulls); + + PG_RETURN_DATUM(HeapTupleGetDatum(tuple)); +} + +/* ------------------------------------------------- + * GetHashPageStatis() + * + * Collect statistics of single hash page + * ------------------------------------------------- + */ +static void +GetHashPageStats(Page page, HashIndexStat *stats) +{ + OffsetNumber maxoff = PageGetMaxOffsetNumber(page); + int off; + + /* count live and dead tuples, and free space */ + for (off = FirstOffsetNumber; off <= maxoff; off++) + { + ItemId id = PageGetItemId(page, off); + + if (!ItemIdIsDead(id)) + stats->live_items++; + else + stats->dead_items++; + } + stats->free_space += PageGetExactFreeSpace(page); +} + +/* + * check_relation_relkind - convenience routine to check that relation + * is of the relkind supported by the callers + */ +static void +check_relation_relkind(Relation rel) +{ + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX && + rel->rd_rel->relkind != RELKIND_MATVIEW && + rel->rd_rel->relkind != RELKIND_SEQUENCE && + rel->rd_rel->relkind != RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, index, materialized view, sequence, or TOAST table", + RelationGetRelationName(rel)))); } diff --git a/contrib/pgstattuple/pgstattuple--1.4--1.5.sql b/contrib/pgstattuple/pgstattuple--1.4--1.5.sql new file mode 100644 index 0000000000..5d03544a21 --- /dev/null +++ b/contrib/pgstattuple/pgstattuple--1.4--1.5.sql @@ -0,0 +1,136 @@ +/* contrib/pgstattuple/pgstattuple--1.4--1.5.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION pgstattuple UPDATE TO '1.5'" to load this file. \quit + +CREATE OR REPLACE FUNCTION pgstattuple(IN relname text, + OUT table_len BIGINT, -- physical table length in bytes + OUT tuple_count BIGINT, -- number of live tuples + OUT tuple_len BIGINT, -- total tuples length in bytes + OUT tuple_percent FLOAT8, -- live tuples in % + OUT dead_tuple_count BIGINT, -- number of dead tuples + OUT dead_tuple_len BIGINT, -- total dead tuples length in bytes + OUT dead_tuple_percent FLOAT8, -- dead tuples in % + OUT free_space BIGINT, -- free space in bytes + OUT free_percent FLOAT8) -- free space in % +AS 'MODULE_PATHNAME', 'pgstattuple_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstattuple(text) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstattuple(text) TO pg_stat_scan_tables; + +CREATE OR REPLACE FUNCTION pgstatindex(IN relname text, + OUT version INT, + OUT tree_level INT, + OUT index_size BIGINT, + OUT root_block_no BIGINT, + OUT internal_pages BIGINT, + OUT leaf_pages BIGINT, + OUT empty_pages BIGINT, + OUT deleted_pages BIGINT, + OUT avg_leaf_density FLOAT8, + OUT leaf_fragmentation FLOAT8) +AS 'MODULE_PATHNAME', 'pgstatindex_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstatindex(text) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstatindex(text) TO pg_stat_scan_tables; + +CREATE OR REPLACE FUNCTION pg_relpages(IN relname text) +RETURNS BIGINT +AS 'MODULE_PATHNAME', 'pg_relpages_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pg_relpages(text) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_relpages(text) TO pg_stat_scan_tables; + +/* New stuff in 1.1 begins here */ + +CREATE OR REPLACE FUNCTION pgstatginindex(IN relname regclass, + OUT version INT4, + OUT pending_pages INT4, + OUT pending_tuples BIGINT) +AS 'MODULE_PATHNAME', 'pgstatginindex_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstatginindex(regclass) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstatginindex(regclass) TO pg_stat_scan_tables; + +/* New stuff in 1.2 begins here */ + +CREATE OR REPLACE FUNCTION pgstattuple(IN reloid regclass, + OUT table_len BIGINT, -- physical table length in bytes + OUT tuple_count BIGINT, -- number of live tuples + OUT tuple_len BIGINT, -- total tuples length in bytes + OUT tuple_percent FLOAT8, -- live tuples in % + OUT dead_tuple_count BIGINT, -- number of dead tuples + OUT dead_tuple_len BIGINT, -- total dead tuples length in bytes + OUT dead_tuple_percent FLOAT8, -- dead tuples in % + OUT free_space BIGINT, -- free space in bytes + OUT free_percent FLOAT8) -- free space in % +AS 'MODULE_PATHNAME', 'pgstattuplebyid_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstattuple(regclass) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstattuple(regclass) TO pg_stat_scan_tables; + +CREATE OR REPLACE FUNCTION pgstatindex(IN relname regclass, + OUT version INT, + OUT tree_level INT, + OUT index_size BIGINT, + OUT root_block_no BIGINT, + OUT internal_pages BIGINT, + OUT leaf_pages BIGINT, + OUT empty_pages BIGINT, + OUT deleted_pages BIGINT, + OUT avg_leaf_density FLOAT8, + OUT leaf_fragmentation FLOAT8) +AS 'MODULE_PATHNAME', 'pgstatindexbyid_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstatindex(regclass) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstatindex(regclass) TO pg_stat_scan_tables; + +CREATE OR REPLACE FUNCTION pg_relpages(IN relname regclass) +RETURNS BIGINT +AS 'MODULE_PATHNAME', 'pg_relpagesbyid_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pg_relpages(regclass) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_relpages(regclass) TO pg_stat_scan_tables; + +/* New stuff in 1.3 begins here */ + +CREATE OR REPLACE FUNCTION pgstattuple_approx(IN reloid regclass, + OUT table_len BIGINT, -- physical table length in bytes + OUT scanned_percent FLOAT8, -- what percentage of the table's pages was scanned + OUT approx_tuple_count BIGINT, -- estimated number of live tuples + OUT approx_tuple_len BIGINT, -- estimated total length in bytes of live tuples + OUT approx_tuple_percent FLOAT8, -- live tuples in % (based on estimate) + OUT dead_tuple_count BIGINT, -- exact number of dead tuples + OUT dead_tuple_len BIGINT, -- exact total length in bytes of dead tuples + OUT dead_tuple_percent FLOAT8, -- dead tuples in % (based on estimate) + OUT approx_free_space BIGINT, -- estimated free space in bytes + OUT approx_free_percent FLOAT8) -- free space in % (based on estimate) +AS 'MODULE_PATHNAME', 'pgstattuple_approx_v1_5' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstattuple_approx(regclass) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstattuple_approx(regclass) TO pg_stat_scan_tables; + +/* New stuff in 1.5 begins here */ + +CREATE OR REPLACE FUNCTION pgstathashindex(IN relname regclass, + OUT version INTEGER, + OUT bucket_pages BIGINT, + OUT overflow_pages BIGINT, + OUT bitmap_pages BIGINT, + OUT unused_pages BIGINT, + OUT live_items BIGINT, + OUT dead_items BIGINT, + OUT free_percent FLOAT8) +AS 'MODULE_PATHNAME', 'pgstathashindex' +LANGUAGE C STRICT PARALLEL SAFE; + +REVOKE EXECUTE ON FUNCTION pgstathashindex(regclass) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pgstathashindex(regclass) TO pg_stat_scan_tables; diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index c1122b496a..eb02ec5b89 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -36,11 +36,14 @@ #include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/tqual.h" +#include "utils/varlena.h" PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(pgstattuple); +PG_FUNCTION_INFO_V1(pgstattuple_v1_5); PG_FUNCTION_INFO_V1(pgstattuplebyid); +PG_FUNCTION_INFO_V1(pgstattuplebyid_v1_5); /* * struct pgstattuple_type @@ -152,13 +155,17 @@ build_pgstattuple_type(pgstattuple_type *stat, FunctionCallInfo fcinfo) * * C FUNCTION definition * pgstattuple(text) returns pgstattuple_type + * + * The superuser() check here must be kept as the library might be upgraded + * without the extension being upgraded, meaning that in pre-1.5 installations + * these functions could be called by any user. * ---------- */ Datum pgstattuple(PG_FUNCTION_ARGS) { - text *relname = PG_GETARG_TEXT_P(0); + text *relname = PG_GETARG_TEXT_PP(0); RangeVar *relrv; Relation rel; @@ -174,6 +181,28 @@ pgstattuple(PG_FUNCTION_ARGS) PG_RETURN_DATUM(pgstat_relation(rel, fcinfo)); } +/* + * As of pgstattuple version 1.5, we no longer need to check if the user + * is a superuser because we REVOKE EXECUTE on the function from PUBLIC. + * Users can then grant access to it based on their policies. + * + * Otherwise identical to pgstattuple (above). + */ +Datum +pgstattuple_v1_5(PG_FUNCTION_ARGS) +{ + text *relname = PG_GETARG_TEXT_PP(0); + RangeVar *relrv; + Relation rel; + + /* open relation */ + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + PG_RETURN_DATUM(pgstat_relation(rel, fcinfo)); +} + +/* Must keep superuser() check, see above. */ Datum pgstattuplebyid(PG_FUNCTION_ARGS) { @@ -191,6 +220,19 @@ pgstattuplebyid(PG_FUNCTION_ARGS) PG_RETURN_DATUM(pgstat_relation(rel, fcinfo)); } +/* Remove superuser() check for 1.5 version, see above */ +Datum +pgstattuplebyid_v1_5(PG_FUNCTION_ARGS) +{ + Oid relid = PG_GETARG_OID(0); + Relation rel; + + /* open relation */ + rel = relation_open(relid, AccessShareLock); + + PG_RETURN_DATUM(pgstat_relation(rel, fcinfo)); +} + /* * pgstat_relation */ @@ -251,6 +293,9 @@ pgstat_relation(Relation rel, FunctionCallInfo fcinfo) case RELKIND_FOREIGN_TABLE: err = "foreign table"; break; + case RELKIND_PARTITIONED_TABLE: + err = "partitioned table"; + break; default: err = "unknown"; break; @@ -311,7 +356,7 @@ pgstat_heap(Relation rel, FunctionCallInfo fcinfo) * heap_getnext may find no tuples on a given page, so we cannot * simply examine the pages returned by the heap scan. */ - tupblock = BlockIdGetBlockNumber(&tuple->t_self.ip_blkid); + tupblock = ItemPointerGetBlockNumber(&tuple->t_self); while (block <= tupblock) { @@ -400,7 +445,6 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno, Buffer buf; Page page; - _hash_getlock(rel, blkno, HASH_SHARE); buf = _hash_getbuf_with_strategy(rel, blkno, HASH_READ, 0, bstrategy); page = BufferGetPage(buf); @@ -409,7 +453,7 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno, HashPageOpaque opaque; opaque = (HashPageOpaque) PageGetSpecialPointer(page); - switch (opaque->hasho_flag) + switch (opaque->hasho_flag & LH_PAGE_TYPE) { case LH_UNUSED_PAGE: stat->free_space += BLCKSZ; @@ -431,7 +475,6 @@ pgstat_hash_page(pgstattuple_type *stat, Relation rel, BlockNumber blkno, } _hash_relbuf(rel, buf); - _hash_droplock(rel, blkno, HASH_SHARE); } /* diff --git a/contrib/pgstattuple/pgstattuple.control b/contrib/pgstattuple/pgstattuple.control index fa328fd664..6af40757b2 100644 --- a/contrib/pgstattuple/pgstattuple.control +++ b/contrib/pgstattuple/pgstattuple.control @@ -1,5 +1,5 @@ # pgstattuple extension comment = 'show tuple-level statistics' -default_version = '1.4' +default_version = '1.5' module_pathname = '$libdir/pgstattuple' relocatable = true diff --git a/contrib/pgstattuple/sql/pgstattuple.sql b/contrib/pgstattuple/sql/pgstattuple.sql index d22c9f1c46..a8e341e351 100644 --- a/contrib/pgstattuple/sql/pgstattuple.sql +++ b/contrib/pgstattuple/sql/pgstattuple.sql @@ -47,3 +47,71 @@ select pg_relpages(relname) from pg_class where relname = 'test_pkey'; create index test_ginidx on test using gin (b); select * from pgstatginindex('test_ginidx'); + +create index test_hashidx on test using hash (b); + +select * from pgstathashindex('test_hashidx'); + +-- these should error with the wrong type +select pgstatginindex('test_pkey'); +select pgstathashindex('test_pkey'); + +select pgstatindex('test_ginidx'); +select pgstathashindex('test_ginidx'); + +select pgstatindex('test_hashidx'); +select pgstatginindex('test_hashidx'); + +-- check that using any of these functions with unsupported relations will fail +create table test_partitioned (a int) partition by range (a); +-- these should all fail +select pgstattuple('test_partitioned'); +select pgstattuple_approx('test_partitioned'); +select pg_relpages('test_partitioned'); +select pgstatindex('test_partitioned'); +select pgstatginindex('test_partitioned'); +select pgstathashindex('test_partitioned'); + +create view test_view as select 1; +-- these should all fail +select pgstattuple('test_view'); +select pgstattuple_approx('test_view'); +select pg_relpages('test_view'); +select pgstatindex('test_view'); +select pgstatginindex('test_view'); +select pgstathashindex('test_view'); + +create foreign data wrapper dummy; +create server dummy_server foreign data wrapper dummy; +create foreign table test_foreign_table () server dummy_server; +-- these should all fail +select pgstattuple('test_foreign_table'); +select pgstattuple_approx('test_foreign_table'); +select pg_relpages('test_foreign_table'); +select pgstatindex('test_foreign_table'); +select pgstatginindex('test_foreign_table'); +select pgstathashindex('test_foreign_table'); + +-- a partition of a partitioned table should work though +create table test_partition partition of test_partitioned for values from (1) to (100); +select pgstattuple('test_partition'); +select pgstattuple_approx('test_partition'); +select pg_relpages('test_partition'); + +-- not for the index calls though, of course +select pgstatindex('test_partition'); +select pgstatginindex('test_partition'); +select pgstathashindex('test_partition'); + +-- an actual index of a partitioned table should work though +create index test_partition_idx on test_partition(a); +create index test_partition_hash_idx on test_partition using hash (a); +-- these should work +select pgstatindex('test_partition_idx'); +select pgstathashindex('test_partition_hash_idx'); + +drop table test_partitioned; +drop view test_view; +drop foreign table test_foreign_table; +drop server dummy_server; +drop foreign data wrapper dummy; diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c index 8ca1c1c898..c6e3d44515 100644 --- a/contrib/postgres_fdw/connection.c +++ b/contrib/postgres_fdw/connection.c @@ -3,7 +3,7 @@ * connection.c * Connection management functions for postgres_fdw * - * Portions Copyright (c) 2012-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2012-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/postgres_fdw/connection.c @@ -17,6 +17,7 @@ #include "access/xact.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "pgstat.h" #include "storage/latch.h" #include "utils/hsearch.h" #include "utils/memutils.h" @@ -225,21 +226,11 @@ connect_pg_server(ForeignServer *server, UserMapping *user) conn = PQconnectdbParams(keywords, values, false); if (!conn || PQstatus(conn) != CONNECTION_OK) - { - char *connmessage; - int msglen; - - /* libpq typically appends a newline, strip that */ - connmessage = pstrdup(PQerrorMessage(conn)); - msglen = strlen(connmessage); - if (msglen > 0 && connmessage[msglen - 1] == '\n') - connmessage[msglen - 1] = '\0'; ereport(ERROR, (errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION), errmsg("could not connect to server \"%s\"", server->servername), - errdetail_internal("%s", connmessage))); - } + errdetail_internal("%s", pchomp(PQerrorMessage(conn))))); /* * Check that non-superuser has used password to establish connection; @@ -496,7 +487,7 @@ pgfdw_get_result(PGconn *conn, const char *query) wc = WaitLatchOrSocket(MyLatch, WL_LATCH_SET | WL_SOCKET_READABLE, PQsocket(conn), - -1L); + -1L, PG_WAIT_EXTENSION); ResetLatch(MyLatch); CHECK_FOR_INTERRUPTS(); @@ -562,12 +553,12 @@ pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, * return NULL, not a PGresult at all. */ if (message_primary == NULL) - message_primary = PQerrorMessage(conn); + message_primary = pchomp(PQerrorMessage(conn)); ereport(elevel, (errcode(sqlstate), message_primary ? errmsg_internal("%s", message_primary) : - errmsg("unknown error"), + errmsg("could not obtain message string for remote error"), message_detail ? errdetail_internal("%s", message_detail) : 0, message_hint ? errhint("%s", message_hint) : 0, message_context ? errcontext("%s", message_context) : 0, diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 6476005f6d..482a3dd301 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -24,7 +24,7 @@ * with collations that match the remote table's columns, which we can * consider to be user error. * - * Portions Copyright (c) 2012-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2012-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/postgres_fdw/deparse.c @@ -38,6 +38,7 @@ #include "access/heapam.h" #include "access/htup_details.h" #include "access/sysattr.h" +#include "catalog/pg_aggregate.h" #include "catalog/pg_collation.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" @@ -56,6 +57,7 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/syscache.h" +#include "utils/typcache.h" /* @@ -65,6 +67,8 @@ typedef struct foreign_glob_cxt { PlannerInfo *root; /* global planner state */ RelOptInfo *foreignrel; /* the foreign relation we are planning for */ + Relids relids; /* relids of base relations in the underlying + * scan */ } foreign_glob_cxt; /* @@ -94,6 +98,9 @@ typedef struct deparse_expr_cxt { PlannerInfo *root; /* global planner state */ RelOptInfo *foreignrel; /* the foreign relation we are planning for */ + RelOptInfo *scanrel; /* the underlying scan relation. Same as + * foreignrel, when that represents a join or + * a base relation. */ StringInfo buf; /* output buffer to append to */ List **params_list; /* exprs that will become remote Params */ } deparse_expr_cxt; @@ -102,6 +109,8 @@ typedef struct deparse_expr_cxt /* Handy macro to add relation name qualification */ #define ADD_REL_QUALIFIER(buf, varno) \ appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno)) +#define SUBQUERY_REL_ALIAS_PREFIX "s" +#define SUBQUERY_COL_ALIAS_PREFIX "c" /* * Functions to determine whether an expression can be evaluated safely on @@ -125,6 +134,7 @@ static void deparseTargetList(StringInfo buf, List **retrieved_attrs); static void deparseExplicitTargetList(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context); +static void deparseSubqueryTargetList(deparse_expr_cxt *context); static void deparseReturningList(StringInfo buf, PlannerInfo *root, Index rtindex, Relation rel, bool trig_after_row, @@ -135,7 +145,7 @@ static void deparseColumnRef(StringInfo buf, int varno, int varattno, static void deparseRelation(StringInfo buf, Relation rel); static void deparseExpr(Expr *expr, deparse_expr_cxt *context); static void deparseVar(Var *node, deparse_expr_cxt *context); -static void deparseConst(Const *node, deparse_expr_cxt *context); +static void deparseConst(Const *node, deparse_expr_cxt *context, int showtype); static void deparseParam(Param *node, deparse_expr_cxt *context); static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context); static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context); @@ -152,13 +162,32 @@ static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); -static void deparseSelectSql(List *tlist, List **retrieved_attrs, +static void deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs, deparse_expr_cxt *context); static void deparseLockingClause(deparse_expr_cxt *context); static void appendOrderByClause(List *pathkeys, deparse_expr_cxt *context); static void appendConditions(List *exprs, deparse_expr_cxt *context); static void deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *joinrel, bool use_alias, List **params_list); +static void deparseFromExpr(List *quals, deparse_expr_cxt *context); +static void deparseRangeTblRef(StringInfo buf, PlannerInfo *root, + RelOptInfo *foreignrel, bool make_subquery, + List **params_list); +static void deparseAggref(Aggref *node, deparse_expr_cxt *context); +static void appendGroupByClause(List *tlist, deparse_expr_cxt *context); +static void appendAggOrderBy(List *orderList, List *targetList, + deparse_expr_cxt *context); +static void appendFunctionName(Oid funcid, deparse_expr_cxt *context); +static Node *deparseSortGroupClause(Index ref, List *tlist, + deparse_expr_cxt *context); + +/* + * Helper functions + */ +static bool is_subquery_var(Var *node, RelOptInfo *foreignrel, + int *relno, int *colno); +static void get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel, + int *relno, int *colno); /* @@ -181,7 +210,7 @@ classifyConditions(PlannerInfo *root, foreach(lc, input_conds) { - RestrictInfo *ri = (RestrictInfo *) lfirst(lc); + RestrictInfo *ri = lfirst_node(RestrictInfo, lc); if (is_foreign_expr(root, baserel, ri->clause)) *remote_conds = lappend(*remote_conds, ri); @@ -200,6 +229,7 @@ is_foreign_expr(PlannerInfo *root, { foreign_glob_cxt glob_cxt; foreign_loc_cxt loc_cxt; + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) (baserel->fdw_private); /* * Check that the expression consists of nodes that are safe to execute @@ -207,6 +237,16 @@ is_foreign_expr(PlannerInfo *root, */ glob_cxt.root = root; glob_cxt.foreignrel = baserel; + + /* + * For an upper relation, use relids from its underneath scan relation, + * because the upperrel's own relids currently aren't set to anything + * meaningful by the core code. For other relation, use their own relids. + */ + if (IS_UPPER_REL(baserel)) + glob_cxt.relids = fpinfo->outerrel->relids; + else + glob_cxt.relids = baserel->relids; loc_cxt.collation = InvalidOid; loc_cxt.state = FDW_COLLATE_NONE; if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt)) @@ -281,19 +321,20 @@ foreign_expr_walker(Node *node, * Param's collation, ie it's not safe for it to have a * non-default collation. */ - if (bms_is_member(var->varno, glob_cxt->foreignrel->relids) && + if (bms_is_member(var->varno, glob_cxt->relids) && var->varlevelsup == 0) { /* Var belongs to foreign table */ /* - * System columns other than ctid should not be sent to - * the remote, since we don't make any effort to ensure - * that local and remote values match (tableoid, in + * System columns other than ctid and oid should not be + * sent to the remote, since we don't make any effort to + * ensure that local and remote values match (tableoid, in * particular, almost certainly doesn't match). */ if (var->varattno < 0 && - var->varattno != SelfItemPointerAttributeNumber) + var->varattno != SelfItemPointerAttributeNumber && + var->varattno != ObjectIdAttributeNumber) return false; /* Else check the collation */ @@ -630,6 +671,106 @@ foreign_expr_walker(Node *node, check_type = false; } break; + case T_Aggref: + { + Aggref *agg = (Aggref *) node; + ListCell *lc; + + /* Not safe to pushdown when not in grouping context */ + if (!IS_UPPER_REL(glob_cxt->foreignrel)) + return false; + + /* Only non-split aggregates are pushable. */ + if (agg->aggsplit != AGGSPLIT_SIMPLE) + return false; + + /* As usual, it must be shippable. */ + if (!is_shippable(agg->aggfnoid, ProcedureRelationId, fpinfo)) + return false; + + /* + * Recurse to input args. aggdirectargs, aggorder and + * aggdistinct are all present in args, so no need to check + * their shippability explicitly. + */ + foreach(lc, agg->args) + { + Node *n = (Node *) lfirst(lc); + + /* If TargetEntry, extract the expression from it */ + if (IsA(n, TargetEntry)) + { + TargetEntry *tle = (TargetEntry *) n; + + n = (Node *) tle->expr; + } + + if (!foreign_expr_walker(n, glob_cxt, &inner_cxt)) + return false; + } + + /* + * For aggorder elements, check whether the sort operator, if + * specified, is shippable or not. + */ + if (agg->aggorder) + { + ListCell *lc; + + foreach(lc, agg->aggorder) + { + SortGroupClause *srt = (SortGroupClause *) lfirst(lc); + Oid sortcoltype; + TypeCacheEntry *typentry; + TargetEntry *tle; + + tle = get_sortgroupref_tle(srt->tleSortGroupRef, + agg->args); + sortcoltype = exprType((Node *) tle->expr); + typentry = lookup_type_cache(sortcoltype, + TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); + /* Check shippability of non-default sort operator. */ + if (srt->sortop != typentry->lt_opr && + srt->sortop != typentry->gt_opr && + !is_shippable(srt->sortop, OperatorRelationId, + fpinfo)) + return false; + } + } + + /* Check aggregate filter */ + if (!foreign_expr_walker((Node *) agg->aggfilter, + glob_cxt, &inner_cxt)) + return false; + + /* + * If aggregate's input collation is not derived from a + * foreign Var, it can't be sent to remote. + */ + if (agg->inputcollid == InvalidOid) + /* OK, inputs are all noncollatable */ ; + else if (inner_cxt.state != FDW_COLLATE_SAFE || + agg->inputcollid != inner_cxt.collation) + return false; + + /* + * Detect whether node is introducing a collation not derived + * from a foreign Var. (If so, we just mark it unsafe for now + * rather than immediately returning false, since the parent + * node might not care.) + */ + collation = agg->aggcollid; + if (collation == InvalidOid) + state = FDW_COLLATE_NONE; + else if (inner_cxt.state == FDW_COLLATE_SAFE && + collation == inner_cxt.collation) + state = FDW_COLLATE_SAFE; + else if (collation == DEFAULT_COLLATION_OID) + state = FDW_COLLATE_NONE; + else + state = FDW_COLLATE_UNSAFE; + } + break; default: /* @@ -719,13 +860,23 @@ deparse_type_name(Oid type_oid, int32 typemod) * Build the targetlist for given relation to be deparsed as SELECT clause. * * The output targetlist contains the columns that need to be fetched from the - * foreign server for the given relation. + * foreign server for the given relation. If foreignrel is an upper relation, + * then the output targetlist can also contain expressions to be evaluated on + * foreign server. */ List * build_tlist_to_deparse(RelOptInfo *foreignrel) { List *tlist = NIL; PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; + ListCell *lc; + + /* + * For an upper relation, we have already built the target list while + * checking shippability, so just return that. + */ + if (IS_UPPER_REL(foreignrel)) + return fpinfo->grouped_tlist; /* * We require columns specified in foreignrel->reltarget->exprs and those @@ -734,9 +885,14 @@ build_tlist_to_deparse(RelOptInfo *foreignrel) tlist = add_to_flat_tlist(tlist, pull_var_clause((Node *) foreignrel->reltarget->exprs, PVC_RECURSE_PLACEHOLDERS)); - tlist = add_to_flat_tlist(tlist, - pull_var_clause((Node *) fpinfo->local_conds, - PVC_RECURSE_PLACEHOLDERS)); + foreach(lc, fpinfo->local_conds) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + + tlist = add_to_flat_tlist(tlist, + pull_var_clause((Node *) rinfo->clause, + PVC_RECURSE_PLACEHOLDERS)); + } return tlist; } @@ -748,7 +904,8 @@ build_tlist_to_deparse(RelOptInfo *foreignrel) * For a base relation fpinfo->attrs_used is used to construct SELECT clause, * hence the tlist is ignored for a base relation. * - * remote_conds is the list of conditions to be deparsed as WHERE clause. + * remote_conds is the list of conditions to be deparsed into the WHERE clause + * (or, in the case of upper relations, into the HAVING clause). * * If params_list is not NULL, it receives a list of Params and other-relation * Vars used in the clauses; these values must be transmitted to the remote @@ -759,36 +916,66 @@ build_tlist_to_deparse(RelOptInfo *foreignrel) * * pathkeys is the list of pathkeys to order the result by. * + * is_subquery is the flag to indicate whether to deparse the specified + * relation as a subquery. + * * List of columns selected is returned in retrieved_attrs. */ extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, List *tlist, List *remote_conds, List *pathkeys, - List **retrieved_attrs, List **params_list) + bool is_subquery, List **retrieved_attrs, + List **params_list) { deparse_expr_cxt context; + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private; + List *quals; - /* We handle relations for foreign tables and joins between those */ - Assert(rel->reloptkind == RELOPT_JOINREL || - rel->reloptkind == RELOPT_BASEREL || - rel->reloptkind == RELOPT_OTHER_MEMBER_REL); + /* + * We handle relations for foreign tables, joins between those and upper + * relations. + */ + Assert(IS_JOIN_REL(rel) || IS_SIMPLE_REL(rel) || IS_UPPER_REL(rel)); - /* Fill portions of context common to join and base relation */ + /* Fill portions of context common to upper, join and base relation */ context.buf = buf; context.root = root; context.foreignrel = rel; + context.scanrel = IS_UPPER_REL(rel) ? fpinfo->outerrel : rel; context.params_list = params_list; - /* Construct SELECT clause and FROM clause */ - deparseSelectSql(tlist, retrieved_attrs, &context); + /* Construct SELECT clause */ + deparseSelectSql(tlist, is_subquery, retrieved_attrs, &context); /* - * Construct WHERE clause + * For upper relations, the WHERE clause is built from the remote + * conditions of the underlying scan relation; otherwise, we can use the + * supplied list of remote conditions directly. */ - if (remote_conds) + if (IS_UPPER_REL(rel)) { - appendStringInfo(buf, " WHERE "); - appendConditions(remote_conds, &context); + PgFdwRelationInfo *ofpinfo; + + ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private; + quals = ofpinfo->remote_conds; + } + else + quals = remote_conds; + + /* Construct FROM and WHERE clauses */ + deparseFromExpr(quals, &context); + + if (IS_UPPER_REL(rel)) + { + /* Append GROUP BY clause */ + appendGroupByClause(tlist, &context); + + /* Append HAVING clause */ + if (remote_conds) + { + appendStringInfo(buf, " HAVING "); + appendConditions(remote_conds, &context); + } } /* Add ORDER BY clause if we found any useful pathkeys */ @@ -802,16 +989,19 @@ deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *rel, /* * Construct a simple SELECT statement that retrieves desired columns * of the specified foreign table, and append it to "buf". The output - * contains just "SELECT ... FROM ....". + * contains just "SELECT ... ". * * We also create an integer List of the columns being retrieved, which is - * returned to *retrieved_attrs. + * returned to *retrieved_attrs, unless we deparse the specified relation + * as a subquery. * - * tlist is the list of desired columns. Read prologue of - * deparseSelectStmtForRel() for details. + * tlist is the list of desired columns. is_subquery is the flag to + * indicate whether to deparse the specified relation as a subquery. + * Read prologue of deparseSelectStmtForRel() for details. */ static void -deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) +deparseSelectSql(List *tlist, bool is_subquery, List **retrieved_attrs, + deparse_expr_cxt *context) { StringInfo buf = context->buf; RelOptInfo *foreignrel = context->foreignrel; @@ -823,9 +1013,21 @@ deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) */ appendStringInfoString(buf, "SELECT "); - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (is_subquery) { - /* For a join relation use the input tlist */ + /* + * For a relation that is deparsed as a subquery, emit expressions + * specified in the relation's reltarget. Note that since this is for + * the subquery, no need to care about *retrieved_attrs. + */ + deparseSubqueryTargetList(context); + } + else if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) + { + /* + * For a join or upper relation the input tlist gives the list of + * columns required to be fetched from the foreign server. + */ deparseExplicitTargetList(tlist, retrieved_attrs, context); } else @@ -846,14 +1048,37 @@ deparseSelectSql(List *tlist, List **retrieved_attrs, deparse_expr_cxt *context) fpinfo->attrs_used, false, retrieved_attrs); heap_close(rel, NoLock); } +} - /* - * Construct FROM clause - */ +/* + * Construct a FROM clause and, if needed, a WHERE clause, and append those to + * "buf". + * + * quals is the list of clauses to be included in the WHERE clause. + * (These may or may not include RestrictInfo decoration.) + */ +static void +deparseFromExpr(List *quals, deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + RelOptInfo *scanrel = context->scanrel; + + /* For upper relations, scanrel must be either a joinrel or a baserel */ + Assert(!IS_UPPER_REL(context->foreignrel) || + IS_JOIN_REL(scanrel) || IS_SIMPLE_REL(scanrel)); + + /* Construct FROM clause */ appendStringInfoString(buf, " FROM "); - deparseFromExprForRel(buf, root, foreignrel, - (foreignrel->reloptkind == RELOPT_JOINREL), + deparseFromExprForRel(buf, context->root, scanrel, + (bms_num_members(scanrel->relids) > 1), context->params_list); + + /* Construct WHERE clause */ + if (quals != NIL) + { + appendStringInfo(buf, " WHERE "); + appendConditions(quals, context); + } } /* @@ -913,8 +1138,8 @@ deparseTargetList(StringInfo buf, } /* - * Add ctid if needed. We currently don't support retrieving any other - * system columns. + * Add ctid and oid if needed. We currently don't support retrieving any + * other system columns. */ if (bms_is_member(SelfItemPointerAttributeNumber - FirstLowInvalidHeapAttributeNumber, attrs_used)) @@ -932,6 +1157,22 @@ deparseTargetList(StringInfo buf, *retrieved_attrs = lappend_int(*retrieved_attrs, SelfItemPointerAttributeNumber); } + if (bms_is_member(ObjectIdAttributeNumber - FirstLowInvalidHeapAttributeNumber, + attrs_used)) + { + if (!first) + appendStringInfoString(buf, ", "); + else if (is_returning) + appendStringInfoString(buf, " RETURNING "); + first = false; + + if (qualify_col) + ADD_REL_QUALIFIER(buf, rtindex); + appendStringInfoString(buf, "oid"); + + *retrieved_attrs = lappend_int(*retrieved_attrs, + ObjectIdAttributeNumber); + } /* Don't generate bad syntax if no undropped columns */ if (first && !is_returning) @@ -939,20 +1180,28 @@ deparseTargetList(StringInfo buf, } /* - * Deparse the appropriate locking clause (FOR SELECT or FOR SHARE) for a - * given relation (context->foreignrel). + * Deparse the appropriate locking clause (FOR UPDATE or FOR SHARE) for a + * given relation (context->scanrel). */ static void deparseLockingClause(deparse_expr_cxt *context) { StringInfo buf = context->buf; PlannerInfo *root = context->root; - RelOptInfo *rel = context->foreignrel; + RelOptInfo *rel = context->scanrel; + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) rel->fdw_private; int relid = -1; while ((relid = bms_next_member(rel->relids, relid)) >= 0) { /* + * Ignore relation if it appears in a lower subquery. Locking clause + * for such a relation is included in the subquery if necessary. + */ + if (bms_is_member(relid, fpinfo->lower_subquery_rels)) + continue; + + /* * Add FOR UPDATE/SHARE if appropriate. We apply locking during the * initial row fetch, rather than later on as is done for local * tables. The extra roundtrips involved in trying to duplicate the @@ -971,7 +1220,7 @@ deparseLockingClause(deparse_expr_cxt *context) appendStringInfoString(buf, " FOR UPDATE"); /* Add the relation alias if we are here for a join relation */ - if (rel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(rel)) appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } else @@ -1007,7 +1256,7 @@ deparseLockingClause(deparse_expr_cxt *context) } /* Add the relation alias if we are here for a join relation */ - if (rel->reloptkind == RELOPT_JOINREL && + if (bms_num_members(rel->relids) > 1 && rc->strength != LCS_NONE) appendStringInfo(buf, " OF %s%d", REL_ALIAS_PREFIX, relid); } @@ -1019,7 +1268,10 @@ deparseLockingClause(deparse_expr_cxt *context) * Deparse conditions from the provided list and append them to buf. * * The conditions in the list are assumed to be ANDed. This function is used to - * deparse both WHERE clauses and JOIN .. ON clauses. + * deparse WHERE clauses, JOIN .. ON clauses and HAVING clauses. + * + * Depending on the caller, the list elements might be either RestrictInfos + * or bare clauses. */ static void appendConditions(List *exprs, deparse_expr_cxt *context) @@ -1036,16 +1288,9 @@ appendConditions(List *exprs, deparse_expr_cxt *context) { Expr *expr = (Expr *) lfirst(lc); - /* - * Extract clause from RestrictInfo, if required. See comments in - * declaration of PgFdwRelationInfo for details. - */ + /* Extract clause from RestrictInfo, if required */ if (IsA(expr, RestrictInfo)) - { - RestrictInfo *ri = (RestrictInfo *) expr; - - expr = ri->clause; - } + expr = ((RestrictInfo *) expr)->clause; /* Connect expressions with "AND" and parenthesize each condition. */ if (!is_first) @@ -1108,23 +1353,13 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs, foreach(lc, tlist) { - TargetEntry *tle = (TargetEntry *) lfirst(lc); - Var *var; - - /* Extract expression if TargetEntry node */ - Assert(IsA(tle, TargetEntry)); - var = (Var *) tle->expr; - - /* We expect only Var nodes here */ - if (!IsA(var, Var)) - elog(ERROR, "non-Var not expected in target list"); + TargetEntry *tle = lfirst_node(TargetEntry, lc); if (i > 0) appendStringInfoString(buf, ", "); - deparseVar(var, context); + deparseExpr((Expr *) tle->expr, context); *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1); - i++; } @@ -1133,6 +1368,39 @@ deparseExplicitTargetList(List *tlist, List **retrieved_attrs, } /* + * Emit expressions specified in the given relation's reltarget. + * + * This is used for deparsing the given relation as a subquery. + */ +static void +deparseSubqueryTargetList(deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + RelOptInfo *foreignrel = context->foreignrel; + bool first; + ListCell *lc; + + /* Should only be called in these cases. */ + Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel)); + + first = true; + foreach(lc, foreignrel->reltarget->exprs) + { + Node *node = (Node *) lfirst(lc); + + if (!first) + appendStringInfoString(buf, ", "); + first = false; + + deparseExpr((Expr *) node, context); + } + + /* Don't generate bad syntax if no expressions */ + if (first) + appendStringInfoString(buf, "NULL"); +} + +/* * Construct FROM clause for given relation * * The function constructs ... JOIN ... ON ... for join relation. For a base @@ -1145,25 +1413,25 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, { PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel)) { - RelOptInfo *rel_o = fpinfo->outerrel; - RelOptInfo *rel_i = fpinfo->innerrel; StringInfoData join_sql_o; StringInfoData join_sql_i; /* Deparse outer relation */ initStringInfo(&join_sql_o); - deparseFromExprForRel(&join_sql_o, root, rel_o, true, params_list); + deparseRangeTblRef(&join_sql_o, root, fpinfo->outerrel, + fpinfo->make_outerrel_subquery, params_list); /* Deparse inner relation */ initStringInfo(&join_sql_i); - deparseFromExprForRel(&join_sql_i, root, rel_i, true, params_list); + deparseRangeTblRef(&join_sql_i, root, fpinfo->innerrel, + fpinfo->make_innerrel_subquery, params_list); /* * For a join relation FROM clause entry is deparsed as * - * ((outer relation) <join type> (inner relation) ON (joinclauses) + * ((outer relation) <join type> (inner relation) ON (joinclauses)) */ appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data, get_jointype_name(fpinfo->jointype), join_sql_i.data); @@ -1175,6 +1443,7 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, context.buf = buf; context.foreignrel = foreignrel; + context.scanrel = foreignrel; context.root = root; context.params_list = params_list; @@ -1213,6 +1482,62 @@ deparseFromExprForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, } /* + * Append FROM clause entry for the given relation into buf. + */ +static void +deparseRangeTblRef(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, + bool make_subquery, List **params_list) +{ + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; + + /* Should only be called in these cases. */ + Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel)); + + Assert(fpinfo->local_conds == NIL); + + /* If make_subquery is true, deparse the relation as a subquery. */ + if (make_subquery) + { + List *retrieved_attrs; + int ncols; + + /* Deparse the subquery representing the relation. */ + appendStringInfoChar(buf, '('); + deparseSelectStmtForRel(buf, root, foreignrel, NIL, + fpinfo->remote_conds, NIL, true, + &retrieved_attrs, params_list); + appendStringInfoChar(buf, ')'); + + /* Append the relation alias. */ + appendStringInfo(buf, " %s%d", SUBQUERY_REL_ALIAS_PREFIX, + fpinfo->relation_index); + + /* + * Append the column aliases if needed. Note that the subquery emits + * expressions specified in the relation's reltarget (see + * deparseSubqueryTargetList). + */ + ncols = list_length(foreignrel->reltarget->exprs); + if (ncols > 0) + { + int i; + + appendStringInfoChar(buf, '('); + for (i = 1; i <= ncols; i++) + { + if (i > 1) + appendStringInfoString(buf, ", "); + + appendStringInfo(buf, "%s%d", SUBQUERY_COL_ALIAS_PREFIX, i); + } + appendStringInfoChar(buf, ')'); + } + } + else + deparseFromExprForRel(buf, root, foreignrel, true, params_list); +} + +/* * deparse remote INSERT statement * * The statement text is appended to buf, and we also create an integer List @@ -1343,6 +1668,7 @@ deparseDirectUpdateSql(StringInfo buf, PlannerInfo *root, /* Set up context struct for recursion */ context.root = root; context.foreignrel = baserel; + context.scanrel = baserel; context.buf = buf; context.params_list = params_list; @@ -1427,6 +1753,7 @@ deparseDirectDeleteSql(StringInfo buf, PlannerInfo *root, /* Set up context struct for recursion */ context.root = root; context.foreignrel = baserel; + context.scanrel = baserel; context.buf = buf; context.params_list = params_list; @@ -1574,13 +1901,19 @@ deparseColumnRef(StringInfo buf, int varno, int varattno, PlannerInfo *root, { RangeTblEntry *rte; + /* We support fetching the remote side's CTID and OID. */ if (varattno == SelfItemPointerAttributeNumber) { - /* We support fetching the remote side's CTID. */ if (qualify_col) ADD_REL_QUALIFIER(buf, varno); appendStringInfoString(buf, "ctid"); } + else if (varattno == ObjectIdAttributeNumber) + { + if (qualify_col) + ADD_REL_QUALIFIER(buf, varno); + appendStringInfoString(buf, "oid"); + } else if (varattno < 0) { /* @@ -1794,7 +2127,7 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) deparseVar((Var *) node, context); break; case T_Const: - deparseConst((Const *) node, context); + deparseConst((Const *) node, context, 0); break; case T_Param: deparseParam((Param *) node, context); @@ -1826,6 +2159,9 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) case T_ArrayExpr: deparseArrayExpr((ArrayExpr *) node, context); break; + case T_Aggref: + deparseAggref((Aggref *) node, context); + break; default: elog(ERROR, "unsupported expression type for deparse: %d", (int) nodeTag(node)); @@ -1844,10 +2180,27 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) static void deparseVar(Var *node, deparse_expr_cxt *context) { - bool qualify_col = (context->foreignrel->reloptkind == RELOPT_JOINREL); + Relids relids = context->scanrel->relids; + int relno; + int colno; + + /* Qualify columns when multiple relations are involved. */ + bool qualify_col = (bms_num_members(relids) > 1); + + /* + * If the Var belongs to the foreign relation that is deparsed as a + * subquery, use the relation and column alias to the Var provided by the + * subquery, instead of the remote name. + */ + if (is_subquery_var(node, context->scanrel, &relno, &colno)) + { + appendStringInfo(context->buf, "%s%d.%s%d", + SUBQUERY_REL_ALIAS_PREFIX, relno, + SUBQUERY_COL_ALIAS_PREFIX, colno); + return; + } - if (bms_is_member(node->varno, context->foreignrel->relids) && - node->varlevelsup == 0) + if (bms_is_member(node->varno, relids) && node->varlevelsup == 0) deparseColumnRef(context->buf, node->varno, node->varattno, context->root, qualify_col); else @@ -1885,9 +2238,12 @@ deparseVar(Var *node, deparse_expr_cxt *context) * Deparse given constant value into context->buf. * * This function has to be kept in sync with ruleutils.c's get_const_expr. + * As for that function, showtype can be -1 to never show "::typename" decoration, + * or +1 to always show it, or 0 to show it only if the constant wouldn't be assumed + * to be the right type by default. */ static void -deparseConst(Const *node, deparse_expr_cxt *context) +deparseConst(Const *node, deparse_expr_cxt *context, int showtype) { StringInfo buf = context->buf; Oid typoutput; @@ -1899,9 +2255,10 @@ deparseConst(Const *node, deparse_expr_cxt *context) if (node->constisnull) { appendStringInfoString(buf, "NULL"); - appendStringInfo(buf, "::%s", - deparse_type_name(node->consttype, - node->consttypmod)); + if (showtype >= 0) + appendStringInfo(buf, "::%s", + deparse_type_name(node->consttype, + node->consttypmod)); return; } @@ -1951,9 +2308,14 @@ deparseConst(Const *node, deparse_expr_cxt *context) break; } + pfree(extval); + + if (showtype < 0) + return; + /* - * Append ::typename unless the constant will be implicitly typed as the - * right type when it is read in. + * For showtype == 0, append ::typename unless the constant will be + * implicitly typed as the right type when it is read in. * * XXX this code has to be kept in sync with the behavior of the parser, * especially make_const. @@ -1972,7 +2334,7 @@ deparseConst(Const *node, deparse_expr_cxt *context) needlabel = true; break; } - if (needlabel) + if (needlabel || showtype > 0) appendStringInfo(buf, "::%s", deparse_type_name(node->consttype, node->consttypmod)); @@ -2069,9 +2431,6 @@ static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context) { StringInfo buf = context->buf; - HeapTuple proctup; - Form_pg_proc procform; - const char *proname; bool use_variadic; bool first; ListCell *arg; @@ -2104,29 +2463,15 @@ deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context) return; } - /* - * Normal function: display as proname(args). - */ - proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(node->funcid)); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup failed for function %u", node->funcid); - procform = (Form_pg_proc) GETSTRUCT(proctup); - /* Check if need to print VARIADIC (cf. ruleutils.c) */ use_variadic = node->funcvariadic; - /* Print schema name only if it's not pg_catalog */ - if (procform->pronamespace != PG_CATALOG_NAMESPACE) - { - const char *schemaname; - - schemaname = get_namespace_name(procform->pronamespace); - appendStringInfo(buf, "%s.", quote_identifier(schemaname)); - } + /* + * Normal function: display as proname(args). + */ + appendFunctionName(node->funcid, context); + appendStringInfoChar(buf, '('); - /* Deparse the function name ... */ - proname = NameStr(procform->proname); - appendStringInfo(buf, "%s(", quote_identifier(proname)); /* ... and all the arguments */ first = true; foreach(arg, node->args) @@ -2139,8 +2484,6 @@ deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context) first = false; } appendStringInfoChar(buf, ')'); - - ReleaseSysCache(proctup); } /* @@ -2397,6 +2740,152 @@ deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context) } /* + * Deparse an Aggref node. + */ +static void +deparseAggref(Aggref *node, deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + bool use_variadic; + + /* Only basic, non-split aggregation accepted. */ + Assert(node->aggsplit == AGGSPLIT_SIMPLE); + + /* Check if need to print VARIADIC (cf. ruleutils.c) */ + use_variadic = node->aggvariadic; + + /* Find aggregate name from aggfnoid which is a pg_proc entry */ + appendFunctionName(node->aggfnoid, context); + appendStringInfoChar(buf, '('); + + /* Add DISTINCT */ + appendStringInfo(buf, "%s", (node->aggdistinct != NIL) ? "DISTINCT " : ""); + + if (AGGKIND_IS_ORDERED_SET(node->aggkind)) + { + /* Add WITHIN GROUP (ORDER BY ..) */ + ListCell *arg; + bool first = true; + + Assert(!node->aggvariadic); + Assert(node->aggorder != NIL); + + foreach(arg, node->aggdirectargs) + { + if (!first) + appendStringInfoString(buf, ", "); + first = false; + + deparseExpr((Expr *) lfirst(arg), context); + } + + appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY "); + appendAggOrderBy(node->aggorder, node->args, context); + } + else + { + /* aggstar can be set only in zero-argument aggregates */ + if (node->aggstar) + appendStringInfoChar(buf, '*'); + else + { + ListCell *arg; + bool first = true; + + /* Add all the arguments */ + foreach(arg, node->args) + { + TargetEntry *tle = (TargetEntry *) lfirst(arg); + Node *n = (Node *) tle->expr; + + if (tle->resjunk) + continue; + + if (!first) + appendStringInfoString(buf, ", "); + first = false; + + /* Add VARIADIC */ + if (use_variadic && lnext(arg) == NULL) + appendStringInfoString(buf, "VARIADIC "); + + deparseExpr((Expr *) n, context); + } + } + + /* Add ORDER BY */ + if (node->aggorder != NIL) + { + appendStringInfoString(buf, " ORDER BY "); + appendAggOrderBy(node->aggorder, node->args, context); + } + } + + /* Add FILTER (WHERE ..) */ + if (node->aggfilter != NULL) + { + appendStringInfoString(buf, ") FILTER (WHERE "); + deparseExpr((Expr *) node->aggfilter, context); + } + + appendStringInfoChar(buf, ')'); +} + +/* + * Append ORDER BY within aggregate function. + */ +static void +appendAggOrderBy(List *orderList, List *targetList, deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + ListCell *lc; + bool first = true; + + foreach(lc, orderList) + { + SortGroupClause *srt = (SortGroupClause *) lfirst(lc); + Node *sortexpr; + Oid sortcoltype; + TypeCacheEntry *typentry; + + if (!first) + appendStringInfoString(buf, ", "); + first = false; + + sortexpr = deparseSortGroupClause(srt->tleSortGroupRef, targetList, + context); + sortcoltype = exprType(sortexpr); + /* See whether operator is default < or > for datatype */ + typentry = lookup_type_cache(sortcoltype, + TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); + if (srt->sortop == typentry->lt_opr) + appendStringInfoString(buf, " ASC"); + else if (srt->sortop == typentry->gt_opr) + appendStringInfoString(buf, " DESC"); + else + { + HeapTuple opertup; + Form_pg_operator operform; + + appendStringInfoString(buf, " USING "); + + /* Append operator name. */ + opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(srt->sortop)); + if (!HeapTupleIsValid(opertup)) + elog(ERROR, "cache lookup failed for operator %u", srt->sortop); + operform = (Form_pg_operator) GETSTRUCT(opertup); + deparseOperatorName(buf, operform); + ReleaseSysCache(opertup); + } + + if (srt->nulls_first) + appendStringInfoString(buf, " NULLS FIRST"); + else + appendStringInfoString(buf, " NULLS LAST"); + } +} + +/* * Print the representation of a parameter to be sent to the remote side. * * Note: we always label the Param's type explicitly rather than relying on @@ -2441,6 +2930,41 @@ printRemotePlaceholder(Oid paramtype, int32 paramtypmod, } /* + * Deparse GROUP BY clause. + */ +static void +appendGroupByClause(List *tlist, deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + Query *query = context->root->parse; + ListCell *lc; + bool first = true; + + /* Nothing to be done, if there's no GROUP BY clause in the query. */ + if (!query->groupClause) + return; + + appendStringInfo(buf, " GROUP BY "); + + /* + * Queries with grouping sets are not pushed down, so we don't expect + * grouping sets here. + */ + Assert(!query->groupingSets); + + foreach(lc, query->groupClause) + { + SortGroupClause *grp = (SortGroupClause *) lfirst(lc); + + if (!first) + appendStringInfoString(buf, ", "); + first = false; + + deparseSortGroupClause(grp->tleSortGroupRef, tlist, context); + } +} + +/* * Deparse ORDER BY clause according to the given pathkeys for given base * relation. From given pathkeys expressions belonging entirely to the given * base relation are obtained and deparsed. @@ -2451,7 +2975,7 @@ appendOrderByClause(List *pathkeys, deparse_expr_cxt *context) ListCell *lcell; int nestlevel; char *delim = " "; - RelOptInfo *baserel = context->foreignrel; + RelOptInfo *baserel = context->scanrel; StringInfo buf = context->buf; /* Make sure any constants in the exprs are printed portably */ @@ -2482,3 +3006,169 @@ appendOrderByClause(List *pathkeys, deparse_expr_cxt *context) } reset_transmission_modes(nestlevel); } + +/* + * appendFunctionName + * Deparses function name from given function oid. + */ +static void +appendFunctionName(Oid funcid, deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + HeapTuple proctup; + Form_pg_proc procform; + const char *proname; + + proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(proctup)) + elog(ERROR, "cache lookup failed for function %u", funcid); + procform = (Form_pg_proc) GETSTRUCT(proctup); + + /* Print schema name only if it's not pg_catalog */ + if (procform->pronamespace != PG_CATALOG_NAMESPACE) + { + const char *schemaname; + + schemaname = get_namespace_name(procform->pronamespace); + appendStringInfo(buf, "%s.", quote_identifier(schemaname)); + } + + /* Always print the function name */ + proname = NameStr(procform->proname); + appendStringInfo(buf, "%s", quote_identifier(proname)); + + ReleaseSysCache(proctup); +} + +/* + * Appends a sort or group clause. + * + * Like get_rule_sortgroupclause(), returns the expression tree, so caller + * need not find it again. + */ +static Node * +deparseSortGroupClause(Index ref, List *tlist, deparse_expr_cxt *context) +{ + StringInfo buf = context->buf; + TargetEntry *tle; + Expr *expr; + + tle = get_sortgroupref_tle(ref, tlist); + expr = tle->expr; + + if (expr && IsA(expr, Const)) + { + /* + * Force a typecast here so that we don't emit something like "GROUP + * BY 2", which will be misconstrued as a column position rather than + * a constant. + */ + deparseConst((Const *) expr, context, 1); + } + else if (!expr || IsA(expr, Var)) + deparseExpr(expr, context); + else + { + /* Always parenthesize the expression. */ + appendStringInfoString(buf, "("); + deparseExpr(expr, context); + appendStringInfoString(buf, ")"); + } + + return (Node *) expr; +} + + +/* + * Returns true if given Var is deparsed as a subquery output column, in + * which case, *relno and *colno are set to the IDs for the relation and + * column alias to the Var provided by the subquery. + */ +static bool +is_subquery_var(Var *node, RelOptInfo *foreignrel, int *relno, int *colno) +{ + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; + RelOptInfo *outerrel = fpinfo->outerrel; + RelOptInfo *innerrel = fpinfo->innerrel; + + /* Should only be called in these cases. */ + Assert(IS_SIMPLE_REL(foreignrel) || IS_JOIN_REL(foreignrel)); + + /* + * If the given relation isn't a join relation, it doesn't have any lower + * subqueries, so the Var isn't a subquery output column. + */ + if (!IS_JOIN_REL(foreignrel)) + return false; + + /* + * If the Var doesn't belong to any lower subqueries, it isn't a subquery + * output column. + */ + if (!bms_is_member(node->varno, fpinfo->lower_subquery_rels)) + return false; + + if (bms_is_member(node->varno, outerrel->relids)) + { + /* + * If outer relation is deparsed as a subquery, the Var is an output + * column of the subquery; get the IDs for the relation/column alias. + */ + if (fpinfo->make_outerrel_subquery) + { + get_relation_column_alias_ids(node, outerrel, relno, colno); + return true; + } + + /* Otherwise, recurse into the outer relation. */ + return is_subquery_var(node, outerrel, relno, colno); + } + else + { + Assert(bms_is_member(node->varno, innerrel->relids)); + + /* + * If inner relation is deparsed as a subquery, the Var is an output + * column of the subquery; get the IDs for the relation/column alias. + */ + if (fpinfo->make_innerrel_subquery) + { + get_relation_column_alias_ids(node, innerrel, relno, colno); + return true; + } + + /* Otherwise, recurse into the inner relation. */ + return is_subquery_var(node, innerrel, relno, colno); + } +} + +/* + * Get the IDs for the relation and column alias to given Var belonging to + * given relation, which are returned into *relno and *colno. + */ +static void +get_relation_column_alias_ids(Var *node, RelOptInfo *foreignrel, + int *relno, int *colno) +{ + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; + int i; + ListCell *lc; + + /* Get the relation alias ID */ + *relno = fpinfo->relation_index; + + /* Get the column alias ID */ + i = 1; + foreach(lc, foreignrel->reltarget->exprs) + { + if (equal(lfirst(lc), (Node *) node)) + { + *colno = i; + return; + } + i++; + } + + /* Shouldn't get here */ + elog(ERROR, "unexpected expression in subquery output"); +} diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index d7747cc665..4d86ab54dd 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -124,6 +124,13 @@ CREATE FOREIGN TABLE ft6 ( c2 int NOT NULL, c3 text ) SERVER loopback2 OPTIONS (schema_name 'S 1', table_name 'T 4'); +-- A table with oids. CREATE FOREIGN TABLE doesn't support the +-- WITH OIDS option, but ALTER does. +CREATE FOREIGN TABLE ft_pg_type ( + typname name, + typlen smallint +) SERVER loopback OPTIONS (schema_name 'pg_catalog', table_name 'pg_type'); +ALTER TABLE ft_pg_type SET WITH OIDS; -- =================================================================== -- tests for validator -- =================================================================== @@ -173,15 +180,16 @@ ALTER FOREIGN TABLE ft2 OPTIONS (schema_name 'S 1', table_name 'T 1'); ALTER FOREIGN TABLE ft1 ALTER COLUMN c1 OPTIONS (column_name 'C 1'); ALTER FOREIGN TABLE ft2 ALTER COLUMN c1 OPTIONS (column_name 'C 1'); \det+ - List of foreign tables - Schema | Table | Server | FDW Options | Description ---------+-------+-----------+---------------------------------------+------------- - public | ft1 | loopback | (schema_name 'S 1', table_name 'T 1') | - public | ft2 | loopback | (schema_name 'S 1', table_name 'T 1') | - public | ft4 | loopback | (schema_name 'S 1', table_name 'T 3') | - public | ft5 | loopback | (schema_name 'S 1', table_name 'T 4') | - public | ft6 | loopback2 | (schema_name 'S 1', table_name 'T 4') | -(5 rows) + List of foreign tables + Schema | Table | Server | FDW Options | Description +--------+------------+-----------+--------------------------------------------------+------------- + public | ft1 | loopback | (schema_name 'S 1', table_name 'T 1') | + public | ft2 | loopback | (schema_name 'S 1', table_name 'T 1') | + public | ft4 | loopback | (schema_name 'S 1', table_name 'T 3') | + public | ft5 | loopback | (schema_name 'S 1', table_name 'T 4') | + public | ft6 | loopback2 | (schema_name 'S 1', table_name 'T 4') | + public | ft_pg_type | loopback | (schema_name 'pg_catalog', table_name 'pg_type') | +(6 rows) -- Now we should be able to run ANALYZE. -- To exercise multiple code paths, we use local stats on ft1 @@ -388,13 +396,14 @@ EXPLAIN (VERBOSE, COSTS OFF) Output: t1.c1, t2."C 1" -> Merge Join Output: t1.c1, t2."C 1" + Inner Unique: true Merge Cond: (t1.c1 = t2."C 1") -> Foreign Scan on public.ft2 t1 Output: t1.c1 Remote SQL: SELECT "C 1" FROM "S 1"."T 1" ORDER BY "C 1" ASC NULLS LAST -> Index Only Scan using t1_pkey on "S 1"."T 1" t2 Output: t2."C 1" -(10 rows) +(11 rows) SELECT t1.c1, t2."C 1" FROM ft2 t1 JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; c1 | C 1 @@ -421,13 +430,14 @@ EXPLAIN (VERBOSE, COSTS OFF) Output: t1.c1, t2."C 1" -> Merge Left Join Output: t1.c1, t2."C 1" + Inner Unique: true Merge Cond: (t1.c1 = t2."C 1") -> Foreign Scan on public.ft2 t1 Output: t1.c1 Remote SQL: SELECT "C 1" FROM "S 1"."T 1" ORDER BY "C 1" ASC NULLS LAST -> Index Only Scan using t1_pkey on "S 1"."T 1" t2 Output: t2."C 1" -(10 rows) +(11 rows) SELECT t1.c1, t2."C 1" FROM ft2 t1 LEFT JOIN "S 1"."T 1" t2 ON (t1.c1 = t2."C 1") OFFSET 100 LIMIT 10; c1 | C 1 @@ -454,6 +464,7 @@ EXPLAIN (VERBOSE, COSTS OFF) Output: t1."C 1" -> Merge Right Join Output: t1."C 1" + Inner Unique: true Merge Cond: (t3.c1 = t1."C 1") -> Foreign Scan Output: t3.c1 @@ -461,7 +472,7 @@ EXPLAIN (VERBOSE, COSTS OFF) Remote SQL: SELECT r3."C 1" FROM ("S 1"."T 1" r2 INNER JOIN "S 1"."T 1" r3 ON (((r2."C 1" = r3."C 1")))) ORDER BY r2."C 1" ASC NULLS LAST -> Index Only Scan using t1_pkey on "S 1"."T 1" t1 Output: t1."C 1" -(11 rows) +(12 rows) SELECT t1."C 1" FROM "S 1"."T 1" t1 left join ft1 t2 join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; C 1 @@ -489,6 +500,7 @@ EXPLAIN (VERBOSE, COSTS OFF) Output: t1."C 1", t2.c1, t3.c1 -> Merge Right Join Output: t1."C 1", t2.c1, t3.c1 + Inner Unique: true Merge Cond: (t3.c1 = t1."C 1") -> Foreign Scan Output: t3.c1, t2.c1 @@ -496,7 +508,7 @@ EXPLAIN (VERBOSE, COSTS OFF) Remote SQL: SELECT r3."C 1", r2."C 1" FROM ("S 1"."T 1" r3 LEFT JOIN "S 1"."T 1" r2 ON (((r2."C 1" = r3."C 1")))) ORDER BY r3."C 1" ASC NULLS LAST -> Index Only Scan using t1_pkey on "S 1"."T 1" t1 Output: t1."C 1" -(11 rows) +(12 rows) SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 left join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; C 1 | c1 | c1 @@ -522,6 +534,7 @@ EXPLAIN (VERBOSE, COSTS OFF) Output: t1."C 1", t2.c1, t3.c1 -> Merge Full Join Output: t1."C 1", t2.c1, t3.c1 + Inner Unique: true Merge Cond: (t3.c1 = t1."C 1") -> Foreign Scan Output: t2.c1, t3.c1 @@ -529,7 +542,7 @@ EXPLAIN (VERBOSE, COSTS OFF) Remote SQL: SELECT r2."C 1", r3."C 1" FROM ("S 1"."T 1" r2 FULL JOIN "S 1"."T 1" r3 ON (((r2."C 1" = r3."C 1")))) ORDER BY r3."C 1" ASC NULLS LAST -> Index Only Scan using t1_pkey on "S 1"."T 1" t1 Output: t1."C 1" -(11 rows) +(12 rows) SELECT t1."C 1", t2.c1, t3.c1 FROM "S 1"."T 1" t1 full join ft1 t2 full join ft2 t3 on (t2.c1 = t3.c1) on (t3.c1 = t1."C 1") OFFSET 100 LIMIT 10; C 1 | c1 | c1 @@ -853,14 +866,13 @@ CREATE OPERATOR === ( -- built-in operators and functions can be shipped for remote execution EXPLAIN (VERBOSE, COSTS OFF) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); - QUERY PLAN --------------------------------------------------------------------------- - Aggregate - Output: count(c3) - -> Foreign Scan on public.ft1 t1 - Output: c3 - Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" = abs(c2))) -(5 rows) + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (count(c3)) + Relations: Aggregate on (public.ft1 t1) + Remote SQL: SELECT count(c3) FROM "S 1"."T 1" WHERE (("C 1" = abs(c2))) +(4 rows) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); count @@ -870,14 +882,13 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = abs(t1.c2); EXPLAIN (VERBOSE, COSTS OFF) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2; - QUERY PLAN ---------------------------------------------------------------------- - Aggregate - Output: count(c3) - -> Foreign Scan on public.ft1 t1 - Output: c3 - Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" = c2)) -(5 rows) + QUERY PLAN +---------------------------------------------------------------------- + Foreign Scan + Output: (count(c3)) + Relations: Aggregate on (public.ft1 t1) + Remote SQL: SELECT count(c3) FROM "S 1"."T 1" WHERE (("C 1" = c2)) +(4 rows) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = t1.c2; count @@ -929,14 +940,13 @@ ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); -- ... now they can be shipped EXPLAIN (VERBOSE, COSTS OFF) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); - QUERY PLAN ----------------------------------------------------------------------------------------------- - Aggregate - Output: count(c3) - -> Foreign Scan on public.ft1 t1 - Output: c3 - Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" = public.postgres_fdw_abs(c2))) -(5 rows) + QUERY PLAN +----------------------------------------------------------------------------------------------- + Foreign Scan + Output: (count(c3)) + Relations: Aggregate on (public.ft1 t1) + Remote SQL: SELECT count(c3) FROM "S 1"."T 1" WHERE (("C 1" = public.postgres_fdw_abs(c2))) +(4 rows) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); count @@ -946,14 +956,13 @@ SELECT count(c3) FROM ft1 t1 WHERE t1.c1 = postgres_fdw_abs(t1.c2); EXPLAIN (VERBOSE, COSTS OFF) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; - QUERY PLAN ----------------------------------------------------------------------------------------- - Aggregate - Output: count(c3) - -> Foreign Scan on public.ft1 t1 - Output: c3 - Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" OPERATOR(public.===) c2)) -(5 rows) + QUERY PLAN +----------------------------------------------------------------------------------------- + Foreign Scan + Output: (count(c3)) + Relations: Aggregate on (public.ft1 t1) + Remote SQL: SELECT count(c3) FROM "S 1"."T 1" WHERE (("C 1" OPERATOR(public.===) c2)) +(4 rows) SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; count @@ -1213,25 +1222,16 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1. (10 rows) -- full outer join with restrictions on the joining relations +-- a. the joining relations are both base relations EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; - QUERY PLAN ------------------------------------------------------------------------------------------------- - Sort + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan Output: ft4.c1, ft5.c1 - Sort Key: ft4.c1, ft5.c1 - -> Hash Full Join - Output: ft4.c1, ft5.c1 - Hash Cond: (ft4.c1 = ft5.c1) - -> Foreign Scan on public.ft4 - Output: ft4.c1, ft4.c2, ft4.c3 - Remote SQL: SELECT c1 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60)) - -> Hash - Output: ft5.c1 - -> Foreign Scan on public.ft5 - Output: ft5.c1 - Remote SQL: SELECT c1 FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60)) -(14 rows) + Relations: (public.ft4) FULL JOIN (public.ft5) + Remote SQL: SELECT s4.c1, s5.c1 FROM ((SELECT c1 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s4(c1) FULL JOIN (SELECT c1 FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s5(c1) ON (((s4.c1 = s5.c1)))) ORDER BY s4.c1 ASC NULLS LAST, s5.c1 ASC NULLS LAST +(4 rows) SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; c1 | c1 @@ -1246,6 +1246,125 @@ SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL | 57 (8 rows) +EXPLAIN (VERBOSE, COSTS OFF) +SELECT 1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (TRUE) OFFSET 10 LIMIT 10; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit + Output: 1 + -> Foreign Scan + Output: 1 + Relations: (public.ft4) FULL JOIN (public.ft5) + Remote SQL: SELECT NULL FROM ((SELECT NULL FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s4 FULL JOIN (SELECT NULL FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s5 ON (TRUE)) +(6 rows) + +SELECT 1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (TRUE) OFFSET 10 LIMIT 10; + ?column? +---------- + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +(10 rows) + +-- b. one of the joining relations is a base relation and the other is a join +-- relation +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM ft4 t2 LEFT JOIN ft5 t3 ON (t2.c1 = t3.c1) WHERE (t2.c1 between 50 and 60)) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: ft4.c1, t2.c1, t3.c1 + Relations: (public.ft4) FULL JOIN ((public.ft4 t2) LEFT JOIN (public.ft5 t3)) + Remote SQL: SELECT s4.c1, s8.c1, s8.c2 FROM ((SELECT c1 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s4(c1) FULL JOIN (SELECT r5.c1, r6.c1 FROM ("S 1"."T 3" r5 LEFT JOIN "S 1"."T 4" r6 ON (((r5.c1 = r6.c1)))) WHERE ((r5.c1 >= 50)) AND ((r5.c1 <= 60))) s8(c1, c2) ON (((s4.c1 = s8.c1)))) ORDER BY s4.c1 ASC NULLS LAST, s8.c1 ASC NULLS LAST, s8.c2 ASC NULLS LAST +(4 rows) + +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM ft4 t2 LEFT JOIN ft5 t3 ON (t2.c1 = t3.c1) WHERE (t2.c1 between 50 and 60)) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; + c1 | a | b +----+----+---- + 50 | 50 | + 52 | 52 | + 54 | 54 | 54 + 56 | 56 | + 58 | 58 | + 60 | 60 | 60 +(6 rows) + +-- c. test deparsing the remote query as nested subqueries +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: ft4.c1, ft4_1.c1, ft5.c1 + Relations: (public.ft4) FULL JOIN ((public.ft4) FULL JOIN (public.ft5)) + Remote SQL: SELECT s4.c1, s10.c1, s10.c2 FROM ((SELECT c1 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s4(c1) FULL JOIN (SELECT s8.c1, s9.c1 FROM ((SELECT c1 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s8(c1) FULL JOIN (SELECT c1 FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s9(c1) ON (((s8.c1 = s9.c1)))) WHERE (((s8.c1 IS NULL) OR (s8.c1 IS NOT NULL)))) s10(c1, c2) ON (((s4.c1 = s10.c1)))) ORDER BY s4.c1 ASC NULLS LAST, s10.c1 ASC NULLS LAST, s10.c2 ASC NULLS LAST +(4 rows) + +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; + c1 | a | b +----+----+---- + 50 | 50 | + 52 | 52 | + 54 | 54 | 54 + 56 | 56 | + 58 | 58 | + 60 | 60 | 60 + | | 51 + | | 57 +(8 rows) + +-- d. test deparsing rowmarked relations as subqueries +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM "S 1"."T 3" WHERE c1 = 50) t1 INNER JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (TRUE) ORDER BY t1.c1, ss.a, ss.b FOR UPDATE OF t1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + LockRows + Output: "T 3".c1, ft4.c1, ft5.c1, "T 3".ctid, ft4.*, ft5.* + -> Nested Loop + Output: "T 3".c1, ft4.c1, ft5.c1, "T 3".ctid, ft4.*, ft5.* + -> Foreign Scan + Output: ft4.c1, ft4.*, ft5.c1, ft5.* + Relations: (public.ft4) FULL JOIN (public.ft5) + Remote SQL: SELECT s8.c1, s8.c2, s9.c1, s9.c2 FROM ((SELECT c1, ROW(c1, c2, c3) FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s8(c1, c2) FULL JOIN (SELECT c1, ROW(c1, c2, c3) FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s9(c1, c2) ON (((s8.c1 = s9.c1)))) WHERE (((s8.c1 IS NULL) OR (s8.c1 IS NOT NULL))) ORDER BY s8.c1 ASC NULLS LAST, s9.c1 ASC NULLS LAST + -> Hash Full Join + Output: ft4.c1, ft4.*, ft5.c1, ft5.* + Hash Cond: (ft4.c1 = ft5.c1) + Filter: ((ft4.c1 IS NULL) OR (ft4.c1 IS NOT NULL)) + -> Foreign Scan on public.ft4 + Output: ft4.c1, ft4.* + Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60)) + -> Hash + Output: ft5.c1, ft5.* + -> Foreign Scan on public.ft5 + Output: ft5.c1, ft5.* + Remote SQL: SELECT c1, c2, c3 FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60)) + -> Materialize + Output: "T 3".c1, "T 3".ctid + -> Seq Scan on "S 1"."T 3" + Output: "T 3".c1, "T 3".ctid + Filter: ("T 3".c1 = 50) +(25 rows) + +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM "S 1"."T 3" WHERE c1 = 50) t1 INNER JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (TRUE) ORDER BY t1.c1, ss.a, ss.b FOR UPDATE OF t1; + c1 | a | b +----+----+---- + 50 | 50 | + 50 | 52 | + 50 | 54 | 54 + 50 | 56 | + 50 | 58 | + 50 | 60 | 60 + 50 | | 51 + 50 | | 57 +(8 rows) + -- full outer join + inner join EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10; @@ -1501,6 +1620,35 @@ SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 | 21 (10 rows) +-- full outer join + WHERE clause with shippable extensions set +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, t2.c2, t1.c3 FROM ft1 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE postgres_fdw_abs(t1.c1) > 0 OFFSET 10 LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Limit + Output: t1.c1, t2.c2, t1.c3 + -> Foreign Scan + Output: t1.c1, t2.c2, t1.c3 + Relations: (public.ft1 t1) FULL JOIN (public.ft2 t2) + Remote SQL: SELECT r1."C 1", r1.c3, r2.c2 FROM ("S 1"."T 1" r1 FULL JOIN "S 1"."T 1" r2 ON (((r1."C 1" = r2."C 1")))) WHERE ((public.postgres_fdw_abs(r1."C 1") > 0)) +(6 rows) + +ALTER SERVER loopback OPTIONS (DROP extensions); +-- full outer join + WHERE clause with shippable extensions not set +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, t2.c2, t1.c3 FROM ft1 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE postgres_fdw_abs(t1.c1) > 0 OFFSET 10 LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------- + Limit + Output: t1.c1, t2.c2, t1.c3 + -> Foreign Scan + Output: t1.c1, t2.c2, t1.c3 + Filter: (postgres_fdw_abs(t1.c1) > 0) + Relations: (public.ft1 t1) FULL JOIN (public.ft2 t2) + Remote SQL: SELECT r1."C 1", r1.c3, r2.c2 FROM ("S 1"."T 1" r1 FULL JOIN "S 1"."T 1" r2 ON (((r1."C 1" = r2."C 1")))) +(7 rows) + +ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); -- join two tables with FOR UPDATE clause -- tests whole-row reference for row marks EXPLAIN (VERBOSE, COSTS OFF) @@ -1730,8 +1878,8 @@ SELECT t1.ctid, t1, t2, t1.c1 FROM ft1 t1 JOIN ft2 t2 ON (t1.c1 = t2.c1) ORDER B -- SEMI JOIN, not pushed down EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10; - QUERY PLAN ---------------------------------------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------------- Limit Output: t1.c1 -> Merge Semi Join @@ -1740,12 +1888,10 @@ SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) -> Foreign Scan on public.ft1 t1 Output: t1.c1 Remote SQL: SELECT "C 1" FROM "S 1"."T 1" ORDER BY "C 1" ASC NULLS LAST - -> Materialize + -> Foreign Scan on public.ft2 t2 Output: t2.c1 - -> Foreign Scan on public.ft2 t2 - Output: t2.c1 - Remote SQL: SELECT "C 1" FROM "S 1"."T 1" ORDER BY "C 1" ASC NULLS LAST -(13 rows) + Remote SQL: SELECT "C 1" FROM "S 1"."T 1" ORDER BY "C 1" ASC NULLS LAST +(11 rows) SELECT t1.c1 FROM ft1 t1 WHERE EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c1) ORDER BY t1.c1 OFFSET 100 LIMIT 10; c1 @@ -1775,12 +1921,10 @@ SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2 -> Foreign Scan on public.ft1 t1 Output: t1.c1 Remote SQL: SELECT "C 1" FROM "S 1"."T 1" ORDER BY "C 1" ASC NULLS LAST - -> Materialize + -> Foreign Scan on public.ft2 t2 Output: t2.c2 - -> Foreign Scan on public.ft2 t2 - Output: t2.c2 - Remote SQL: SELECT c2 FROM "S 1"."T 1" ORDER BY c2 ASC NULLS LAST -(13 rows) + Remote SQL: SELECT c2 FROM "S 1"."T 1" ORDER BY c2 ASC NULLS LAST +(11 rows) SELECT t1.c1 FROM ft1 t1 WHERE NOT EXISTS (SELECT 1 FROM ft2 t2 WHERE t1.c1 = t2.c2) ORDER BY t1.c1 OFFSET 100 LIMIT 10; c1 @@ -2053,7 +2197,7 @@ SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM 1 (10 rows) --- non-Var items in targelist of the nullable rel of a join preventing +-- non-Var items in targetlist of the nullable rel of a join preventing -- push-down in some cases -- unable to push {ft1, ft2} EXPLAIN (VERBOSE, COSTS OFF) @@ -2275,6 +2419,1117 @@ ALTER VIEW v4 OWNER TO regress_view_owner; DROP OWNED BY regress_view_owner; DROP ROLE regress_view_owner; -- =================================================================== +-- Aggregate and grouping queries +-- =================================================================== +-- Simple aggregates +explain (verbose, costs off) +select count(c6), sum(c1), avg(c1), min(c2), max(c1), stddev(c2), sum(c1) * (random() <= 1)::int as sum2 from ft1 where c2 < 5 group by c2 order by 1, 2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------- + Result + Output: (count(c6)), (sum(c1)), (avg(c1)), (min(c2)), (max(c1)), (stddev(c2)), ((sum(c1)) * ((random() <= '1'::double precision))::integer), c2 + -> Sort + Output: (count(c6)), (sum(c1)), (avg(c1)), (min(c2)), (max(c1)), (stddev(c2)), c2 + Sort Key: (count(ft1.c6)), (sum(ft1.c1)) + -> Foreign Scan + Output: (count(c6)), (sum(c1)), (avg(c1)), (min(c2)), (max(c1)), (stddev(c2)), c2 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT count(c6), sum("C 1"), avg("C 1"), min(c2), max("C 1"), stddev(c2), c2 FROM "S 1"."T 1" WHERE ((c2 < 5)) GROUP BY c2 +(9 rows) + +select count(c6), sum(c1), avg(c1), min(c2), max(c1), stddev(c2), sum(c1) * (random() <= 1)::int as sum2 from ft1 where c2 < 5 group by c2 order by 1, 2; + count | sum | avg | min | max | stddev | sum2 +-------+-------+----------------------+-----+------+--------+------- + 100 | 49600 | 496.0000000000000000 | 1 | 991 | 0 | 49600 + 100 | 49700 | 497.0000000000000000 | 2 | 992 | 0 | 49700 + 100 | 49800 | 498.0000000000000000 | 3 | 993 | 0 | 49800 + 100 | 49900 | 499.0000000000000000 | 4 | 994 | 0 | 49900 + 100 | 50500 | 505.0000000000000000 | 0 | 1000 | 0 | 50500 +(5 rows) + +-- Aggregate is not pushed down as aggregation contains random() +explain (verbose, costs off) +select sum(c1 * (random() <= 1)::int) as sum, avg(c1) from ft1; + QUERY PLAN +------------------------------------------------------------------------------- + Aggregate + Output: sum((c1 * ((random() <= '1'::double precision))::integer)), avg(c1) + -> Foreign Scan on public.ft1 + Output: c1 + Remote SQL: SELECT "C 1" FROM "S 1"."T 1" +(5 rows) + +-- Aggregate over join query +explain (verbose, costs off) +select count(*), sum(t1.c1), avg(t2.c1) from ft1 t1 inner join ft1 t2 on (t1.c2 = t2.c2) where t1.c2 = 6; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: (count(*)), (sum(t1.c1)), (avg(t2.c1)) + Relations: Aggregate on ((public.ft1 t1) INNER JOIN (public.ft1 t2)) + Remote SQL: SELECT count(*), sum(r1."C 1"), avg(r2."C 1") FROM ("S 1"."T 1" r1 INNER JOIN "S 1"."T 1" r2 ON (((r2.c2 = 6)) AND ((r1.c2 = 6)))) +(4 rows) + +select count(*), sum(t1.c1), avg(t2.c1) from ft1 t1 inner join ft1 t2 on (t1.c2 = t2.c2) where t1.c2 = 6; + count | sum | avg +-------+---------+---------------------- + 10000 | 5010000 | 501.0000000000000000 +(1 row) + +-- Not pushed down due to local conditions present in underneath input rel +explain (verbose, costs off) +select sum(t1.c1), count(t2.c1) from ft1 t1 inner join ft2 t2 on (t1.c1 = t2.c1) where ((t1.c1 * t2.c1)/(t1.c1 * t2.c1)) * random() <= 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------- + Aggregate + Output: sum(t1.c1), count(t2.c1) + -> Foreign Scan + Output: t1.c1, t2.c1 + Filter: (((((t1.c1 * t2.c1) / (t1.c1 * t2.c1)))::double precision * random()) <= '1'::double precision) + Relations: (public.ft1 t1) INNER JOIN (public.ft2 t2) + Remote SQL: SELECT r1."C 1", r2."C 1" FROM ("S 1"."T 1" r1 INNER JOIN "S 1"."T 1" r2 ON (((r1."C 1" = r2."C 1")))) +(7 rows) + +-- GROUP BY clause having expressions +explain (verbose, costs off) +select c2/2, sum(c2) * (c2/2) from ft1 group by c2/2 order by c2/2; + QUERY PLAN +------------------------------------------------------------------------------------------------ + Sort + Output: ((c2 / 2)), ((sum(c2) * (c2 / 2))) + Sort Key: ((ft1.c2 / 2)) + -> Foreign Scan + Output: ((c2 / 2)), ((sum(c2) * (c2 / 2))) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT (c2 / 2), (sum(c2) * (c2 / 2)) FROM "S 1"."T 1" GROUP BY ((c2 / 2)) +(7 rows) + +select c2/2, sum(c2) * (c2/2) from ft1 group by c2/2 order by c2/2; + ?column? | ?column? +----------+---------- + 0 | 0 + 1 | 500 + 2 | 1800 + 3 | 3900 + 4 | 6800 +(5 rows) + +-- Aggregates in subquery are pushed down. +explain (verbose, costs off) +select count(x.a), sum(x.a) from (select c2 a, sum(c1) b from ft1 group by c2, sqrt(c1) order by 1, 2) x; + QUERY PLAN +---------------------------------------------------------------------------------------------------------- + Aggregate + Output: count(ft1.c2), sum(ft1.c2) + -> Sort + Output: ft1.c2, (sum(ft1.c1)), (sqrt((ft1.c1)::double precision)) + Sort Key: ft1.c2, (sum(ft1.c1)) + -> Foreign Scan + Output: ft1.c2, (sum(ft1.c1)), (sqrt((ft1.c1)::double precision)) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c2, sum("C 1"), sqrt("C 1") FROM "S 1"."T 1" GROUP BY c2, (sqrt("C 1")) +(9 rows) + +select count(x.a), sum(x.a) from (select c2 a, sum(c1) b from ft1 group by c2, sqrt(c1) order by 1, 2) x; + count | sum +-------+------ + 1000 | 4500 +(1 row) + +-- Aggregate is still pushed down by taking unshippable expression out +explain (verbose, costs off) +select c2 * (random() <= 1)::int as sum1, sum(c1) * c2 as sum2 from ft1 group by c2 order by 1, 2; + QUERY PLAN +--------------------------------------------------------------------------------------------------- + Sort + Output: ((c2 * ((random() <= '1'::double precision))::integer)), ((sum(c1) * c2)), c2 + Sort Key: ((ft1.c2 * ((random() <= '1'::double precision))::integer)), ((sum(ft1.c1) * ft1.c2)) + -> Foreign Scan + Output: (c2 * ((random() <= '1'::double precision))::integer), ((sum(c1) * c2)), c2 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT (sum("C 1") * c2), c2 FROM "S 1"."T 1" GROUP BY c2 +(7 rows) + +select c2 * (random() <= 1)::int as sum1, sum(c1) * c2 as sum2 from ft1 group by c2 order by 1, 2; + sum1 | sum2 +------+-------- + 0 | 0 + 1 | 49600 + 2 | 99400 + 3 | 149400 + 4 | 199600 + 5 | 250000 + 6 | 300600 + 7 | 351400 + 8 | 402400 + 9 | 453600 +(10 rows) + +-- Aggregate with unshippable GROUP BY clause are not pushed +explain (verbose, costs off) +select c2 * (random() <= 1)::int as c2 from ft2 group by c2 * (random() <= 1)::int order by 1; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Output: ((c2 * ((random() <= '1'::double precision))::integer)) + Sort Key: ((ft2.c2 * ((random() <= '1'::double precision))::integer)) + -> HashAggregate + Output: ((c2 * ((random() <= '1'::double precision))::integer)) + Group Key: (ft2.c2 * ((random() <= '1'::double precision))::integer) + -> Foreign Scan on public.ft2 + Output: (c2 * ((random() <= '1'::double precision))::integer) + Remote SQL: SELECT c2 FROM "S 1"."T 1" +(9 rows) + +-- GROUP BY clause in various forms, cardinal, alias and constant expression +explain (verbose, costs off) +select count(c2) w, c2 x, 5 y, 7.0 z from ft1 group by 2, y, 9.0::int order by 2; + QUERY PLAN +---------------------------------------------------------------------------------------------------------- + Sort + Output: (count(c2)), c2, 5, 7.0, 9 + Sort Key: ft1.c2 + -> Foreign Scan + Output: (count(c2)), c2, 5, 7.0, 9 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT count(c2), c2, 5, 7.0, 9 FROM "S 1"."T 1" GROUP BY c2, 5::integer, 9::integer +(7 rows) + +select count(c2) w, c2 x, 5 y, 7.0 z from ft1 group by 2, y, 9.0::int order by 2; + w | x | y | z +-----+---+---+----- + 100 | 0 | 5 | 7.0 + 100 | 1 | 5 | 7.0 + 100 | 2 | 5 | 7.0 + 100 | 3 | 5 | 7.0 + 100 | 4 | 5 | 7.0 + 100 | 5 | 5 | 7.0 + 100 | 6 | 5 | 7.0 + 100 | 7 | 5 | 7.0 + 100 | 8 | 5 | 7.0 + 100 | 9 | 5 | 7.0 +(10 rows) + +-- Testing HAVING clause shippability +explain (verbose, costs off) +select c2, sum(c1) from ft2 group by c2 having avg(c1) < 500 and sum(c1) < 49800 order by c2; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: c2, (sum(c1)) + Sort Key: ft2.c2 + -> Foreign Scan + Output: c2, (sum(c1)) + Relations: Aggregate on (public.ft2) + Remote SQL: SELECT c2, sum("C 1") FROM "S 1"."T 1" GROUP BY c2 HAVING ((avg("C 1") < 500::numeric)) AND ((sum("C 1") < 49800)) +(7 rows) + +select c2, sum(c1) from ft2 group by c2 having avg(c1) < 500 and sum(c1) < 49800 order by c2; + c2 | sum +----+------- + 1 | 49600 + 2 | 49700 +(2 rows) + +-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down +explain (verbose, costs off) +select count(*) from (select c5, count(c1) from ft1 group by c5, sqrt(c2) having (avg(c1) / avg(c1)) * random() <= 1 and avg(c1) < 500) x; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------- + Aggregate + Output: count(*) + -> Foreign Scan + Output: ft1.c5, NULL::bigint, (sqrt((ft1.c2)::double precision)) + Filter: (((((avg(ft1.c1)) / (avg(ft1.c1))))::double precision * random()) <= '1'::double precision) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c5, NULL::bigint, sqrt(c2), avg("C 1") FROM "S 1"."T 1" GROUP BY c5, (sqrt(c2)) HAVING ((avg("C 1") < 500::numeric)) +(7 rows) + +select count(*) from (select c5, count(c1) from ft1 group by c5, sqrt(c2) having (avg(c1) / avg(c1)) * random() <= 1 and avg(c1) < 500) x; + count +------- + 49 +(1 row) + +-- Aggregate in HAVING clause is not pushable, and thus aggregation is not pushed down +explain (verbose, costs off) +select sum(c1) from ft1 group by c2 having avg(c1 * (random() <= 1)::int) > 100 order by 1; + QUERY PLAN +--------------------------------------------------------------------------------------------------- + Sort + Output: (sum(c1)), c2 + Sort Key: (sum(ft1.c1)) + -> HashAggregate + Output: sum(c1), c2 + Group Key: ft1.c2 + Filter: (avg((ft1.c1 * ((random() <= '1'::double precision))::integer)) > '100'::numeric) + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" +(10 rows) + +-- Testing ORDER BY, DISTINCT, FILTER, Ordered-sets and VARIADIC within aggregates +-- ORDER BY within aggregate, same column used to order +explain (verbose, costs off) +select array_agg(c1 order by c1) from ft1 where c1 < 100 group by c2 order by 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: (array_agg(c1 ORDER BY c1)), c2 + Sort Key: (array_agg(ft1.c1 ORDER BY ft1.c1)) + -> Foreign Scan + Output: (array_agg(c1 ORDER BY c1)), c2 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT array_agg("C 1" ORDER BY "C 1" ASC NULLS LAST), c2 FROM "S 1"."T 1" WHERE (("C 1" < 100)) GROUP BY c2 +(7 rows) + +select array_agg(c1 order by c1) from ft1 where c1 < 100 group by c2 order by 1; + array_agg +-------------------------------- + {1,11,21,31,41,51,61,71,81,91} + {2,12,22,32,42,52,62,72,82,92} + {3,13,23,33,43,53,63,73,83,93} + {4,14,24,34,44,54,64,74,84,94} + {5,15,25,35,45,55,65,75,85,95} + {6,16,26,36,46,56,66,76,86,96} + {7,17,27,37,47,57,67,77,87,97} + {8,18,28,38,48,58,68,78,88,98} + {9,19,29,39,49,59,69,79,89,99} + {10,20,30,40,50,60,70,80,90} +(10 rows) + +-- ORDER BY within aggregate, different column used to order also using DESC +explain (verbose, costs off) +select array_agg(c5 order by c1 desc) from ft2 where c2 = 6 and c1 < 50; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: (array_agg(c5 ORDER BY c1 DESC)) + Relations: Aggregate on (public.ft2) + Remote SQL: SELECT array_agg(c5 ORDER BY "C 1" DESC NULLS FIRST) FROM "S 1"."T 1" WHERE (("C 1" < 50)) AND ((c2 = 6)) +(4 rows) + +select array_agg(c5 order by c1 desc) from ft2 where c2 = 6 and c1 < 50; + array_agg +------------------------------------------------------------------------------------------------------------------------------------------ + {"Mon Feb 16 00:00:00 1970","Fri Feb 06 00:00:00 1970","Tue Jan 27 00:00:00 1970","Sat Jan 17 00:00:00 1970","Wed Jan 07 00:00:00 1970"} +(1 row) + +-- DISTINCT within aggregate +explain (verbose, costs off) +select array_agg(distinct (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: (array_agg(DISTINCT (t1.c1 % 5))), ((t2.c1 % 3)) + Sort Key: (array_agg(DISTINCT (t1.c1 % 5))) + -> Foreign Scan + Output: (array_agg(DISTINCT (t1.c1 % 5))), ((t2.c1 % 3)) + Relations: Aggregate on ((public.ft4 t1) FULL JOIN (public.ft5 t2)) + Remote SQL: SELECT array_agg(DISTINCT (r1.c1 % 5)), (r2.c1 % 3) FROM ("S 1"."T 3" r1 FULL JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) WHERE (((r1.c1 < 20) OR ((r1.c1 IS NULL) AND (r2.c1 < 5)))) GROUP BY ((r2.c1 % 3)) +(7 rows) + +select array_agg(distinct (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + array_agg +-------------- + {0,1,2,3,4} + {1,2,3,NULL} +(2 rows) + +-- DISTINCT combined with ORDER BY within aggregate +explain (verbose, costs off) +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + Sort + Output: (array_agg(DISTINCT (t1.c1 % 5) ORDER BY (t1.c1 % 5))), ((t2.c1 % 3)) + Sort Key: (array_agg(DISTINCT (t1.c1 % 5) ORDER BY (t1.c1 % 5))) + -> Foreign Scan + Output: (array_agg(DISTINCT (t1.c1 % 5) ORDER BY (t1.c1 % 5))), ((t2.c1 % 3)) + Relations: Aggregate on ((public.ft4 t1) FULL JOIN (public.ft5 t2)) + Remote SQL: SELECT array_agg(DISTINCT (r1.c1 % 5) ORDER BY ((r1.c1 % 5)) ASC NULLS LAST), (r2.c1 % 3) FROM ("S 1"."T 3" r1 FULL JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) WHERE (((r1.c1 < 20) OR ((r1.c1 IS NULL) AND (r2.c1 < 5)))) GROUP BY ((r2.c1 % 3)) +(7 rows) + +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + array_agg +-------------- + {0,1,2,3,4} + {1,2,3,NULL} +(2 rows) + +explain (verbose, costs off) +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5 desc nulls last) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: (array_agg(DISTINCT (t1.c1 % 5) ORDER BY (t1.c1 % 5) DESC NULLS LAST)), ((t2.c1 % 3)) + Sort Key: (array_agg(DISTINCT (t1.c1 % 5) ORDER BY (t1.c1 % 5) DESC NULLS LAST)) + -> Foreign Scan + Output: (array_agg(DISTINCT (t1.c1 % 5) ORDER BY (t1.c1 % 5) DESC NULLS LAST)), ((t2.c1 % 3)) + Relations: Aggregate on ((public.ft4 t1) FULL JOIN (public.ft5 t2)) + Remote SQL: SELECT array_agg(DISTINCT (r1.c1 % 5) ORDER BY ((r1.c1 % 5)) DESC NULLS LAST), (r2.c1 % 3) FROM ("S 1"."T 3" r1 FULL JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) WHERE (((r1.c1 < 20) OR ((r1.c1 IS NULL) AND (r2.c1 < 5)))) GROUP BY ((r2.c1 % 3)) +(7 rows) + +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5 desc nulls last) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + array_agg +-------------- + {3,2,1,NULL} + {4,3,2,1,0} +(2 rows) + +-- FILTER within aggregate +explain (verbose, costs off) +select sum(c1) filter (where c1 < 100 and c2 > 5) from ft1 group by c2 order by 1 nulls last; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(c1) FILTER (WHERE ((c1 < 100) AND (c2 > 5)))), c2 + Sort Key: (sum(ft1.c1) FILTER (WHERE ((ft1.c1 < 100) AND (ft1.c2 > 5)))) + -> Foreign Scan + Output: (sum(c1) FILTER (WHERE ((c1 < 100) AND (c2 > 5)))), c2 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT sum("C 1") FILTER (WHERE (("C 1" < 100) AND (c2 > 5))), c2 FROM "S 1"."T 1" GROUP BY c2 +(7 rows) + +select sum(c1) filter (where c1 < 100 and c2 > 5) from ft1 group by c2 order by 1 nulls last; + sum +----- + 510 + 520 + 530 + 540 + + + + + + +(10 rows) + +-- DISTINCT, ORDER BY and FILTER within aggregate +explain (verbose, costs off) +select sum(c1%3), sum(distinct c1%3 order by c1%3) filter (where c1%3 < 2), c2 from ft1 where c2 = 6 group by c2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: (sum((c1 % 3))), (sum(DISTINCT (c1 % 3) ORDER BY (c1 % 3)) FILTER (WHERE ((c1 % 3) < 2))), c2 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT sum(("C 1" % 3)), sum(DISTINCT ("C 1" % 3) ORDER BY (("C 1" % 3)) ASC NULLS LAST) FILTER (WHERE (("C 1" % 3) < 2)), c2 FROM "S 1"."T 1" WHERE ((c2 = 6)) GROUP BY c2 +(4 rows) + +select sum(c1%3), sum(distinct c1%3 order by c1%3) filter (where c1%3 < 2), c2 from ft1 where c2 = 6 group by c2; + sum | sum | c2 +-----+-----+---- + 99 | 1 | 6 +(1 row) + +-- Outer query is aggregation query +explain (verbose, costs off) +select distinct (select count(*) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------ + Unique + Output: ((SubPlan 1)) + -> Sort + Output: ((SubPlan 1)) + Sort Key: ((SubPlan 1)) + -> Foreign Scan + Output: (SubPlan 1) + Relations: Aggregate on (public.ft2 t2) + Remote SQL: SELECT count(*) FILTER (WHERE ((c2 = 6) AND ("C 1" < 10))) FROM "S 1"."T 1" WHERE (((c2 % 6) = 0)) + SubPlan 1 + -> Foreign Scan on public.ft1 t1 + Output: (count(*) FILTER (WHERE ((t2.c2 = 6) AND (t2.c1 < 10)))) + Remote SQL: SELECT NULL FROM "S 1"."T 1" WHERE (("C 1" = 6)) +(13 rows) + +select distinct (select count(*) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; + count +------- + 1 +(1 row) + +-- Inner query is aggregation query +explain (verbose, costs off) +select distinct (select count(t1.c1) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------ + Unique + Output: ((SubPlan 1)) + -> Sort + Output: ((SubPlan 1)) + Sort Key: ((SubPlan 1)) + -> Foreign Scan on public.ft2 t2 + Output: (SubPlan 1) + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" WHERE (((c2 % 6) = 0)) + SubPlan 1 + -> Foreign Scan + Output: (count(t1.c1) FILTER (WHERE ((t2.c2 = 6) AND (t2.c1 < 10)))) + Relations: Aggregate on (public.ft1 t1) + Remote SQL: SELECT count("C 1") FILTER (WHERE (($1::integer = 6) AND ($2::integer < 10))) FROM "S 1"."T 1" WHERE (("C 1" = 6)) +(13 rows) + +select distinct (select count(t1.c1) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; + count +------- + 0 + 1 +(2 rows) + +-- Aggregate not pushed down as FILTER condition is not pushable +explain (verbose, costs off) +select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: (sum(c1) FILTER (WHERE ((((c1 / c1))::double precision * random()) <= '1'::double precision))), c2 + Sort Key: (sum(ft1.c1) FILTER (WHERE ((((ft1.c1 / ft1.c1))::double precision * random()) <= '1'::double precision))) + -> HashAggregate + Output: sum(c1) FILTER (WHERE ((((c1 / c1))::double precision * random()) <= '1'::double precision)), c2 + Group Key: ft1.c2 + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" +(9 rows) + +explain (verbose, costs off) +select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; + QUERY PLAN +------------------------------------------------------------------- + Aggregate + Output: sum(ft1.c2) FILTER (WHERE (hashed SubPlan 1)) + -> Foreign Scan on public.ft1 + Output: ft1.c2 + Remote SQL: SELECT c2 FROM "S 1"."T 1" + SubPlan 1 + -> Foreign Scan on public.ft1 ft1_1 + Output: ft1_1.c2 + Remote SQL: SELECT c2 FROM "S 1"."T 1" WHERE ((c2 < 5)) +(9 rows) + +-- Ordered-sets within aggregate +explain (verbose, costs off) +select c2, rank('10'::varchar) within group (order by c6), percentile_cont(c2/10::numeric) within group (order by c1) from ft1 where c2 < 10 group by c2 having percentile_cont(c2/10::numeric) within group (order by c1) < 500 order by c2; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: c2, (rank('10'::character varying) WITHIN GROUP (ORDER BY c6)), (percentile_cont((((c2)::numeric / '10'::numeric))::double precision) WITHIN GROUP (ORDER BY ((c1)::double precision))) + Sort Key: ft1.c2 + -> Foreign Scan + Output: c2, (rank('10'::character varying) WITHIN GROUP (ORDER BY c6)), (percentile_cont((((c2)::numeric / '10'::numeric))::double precision) WITHIN GROUP (ORDER BY ((c1)::double precision))) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c2, rank('10'::character varying) WITHIN GROUP (ORDER BY c6 ASC NULLS LAST), percentile_cont((c2 / 10::numeric)) WITHIN GROUP (ORDER BY ("C 1") ASC NULLS LAST) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY c2 HAVING ((percentile_cont((c2 / 10::numeric)) WITHIN GROUP (ORDER BY ("C 1") ASC NULLS LAST) < 500::double precision)) +(7 rows) + +select c2, rank('10'::varchar) within group (order by c6), percentile_cont(c2/10::numeric) within group (order by c1) from ft1 where c2 < 10 group by c2 having percentile_cont(c2/10::numeric) within group (order by c1) < 500 order by c2; + c2 | rank | percentile_cont +----+------+----------------- + 0 | 101 | 10 + 1 | 101 | 100 + 2 | 1 | 200 + 3 | 1 | 300 + 4 | 1 | 400 +(5 rows) + +-- Using multiple arguments within aggregates +explain (verbose, costs off) +select c1, rank(c1, c2) within group (order by c1, c2) from ft1 group by c1, c2 having c1 = 6 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: c1, (rank(c1, c2) WITHIN GROUP (ORDER BY c1, c2)), c2 + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT "C 1", rank("C 1", c2) WITHIN GROUP (ORDER BY "C 1" ASC NULLS LAST, c2 ASC NULLS LAST), c2 FROM "S 1"."T 1" WHERE (("C 1" = 6)) GROUP BY "C 1", c2 +(4 rows) + +select c1, rank(c1, c2) within group (order by c1, c2) from ft1 group by c1, c2 having c1 = 6 order by 1; + c1 | rank +----+------ + 6 | 1 +(1 row) + +-- User defined function for user defined aggregate, VARIADIC +create function least_accum(anyelement, variadic anyarray) +returns anyelement language sql as + 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)'; +create aggregate least_agg(variadic items anyarray) ( + stype = anyelement, sfunc = least_accum +); +-- Disable hash aggregation for plan stability. +set enable_hashagg to false; +-- Not pushed down due to user defined aggregate +explain (verbose, costs off) +select c2, least_agg(c1) from ft1 group by c2 order by c2; + QUERY PLAN +---------------------------------------------------------------------------------- + GroupAggregate + Output: c2, least_agg(VARIADIC ARRAY[c1]) + Group Key: ft1.c2 + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" ORDER BY c2 ASC NULLS LAST +(6 rows) + +-- Add function and aggregate into extension +alter extension postgres_fdw add function least_accum(anyelement, variadic anyarray); +alter extension postgres_fdw add aggregate least_agg(variadic items anyarray); +alter server loopback options (set extensions 'postgres_fdw'); +-- Now aggregate will be pushed. Aggregate will display VARIADIC argument. +explain (verbose, costs off) +select c2, least_agg(c1) from ft1 where c2 < 100 group by c2 order by c2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: c2, (least_agg(VARIADIC ARRAY[c1])) + Sort Key: ft1.c2 + -> Foreign Scan + Output: c2, (least_agg(VARIADIC ARRAY[c1])) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c2, public.least_agg(VARIADIC ARRAY["C 1"]) FROM "S 1"."T 1" WHERE ((c2 < 100)) GROUP BY c2 +(7 rows) + +select c2, least_agg(c1) from ft1 where c2 < 100 group by c2 order by c2; + c2 | least_agg +----+----------- + 0 | 10 + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 + 9 | 9 +(10 rows) + +-- Remove function and aggregate from extension +alter extension postgres_fdw drop function least_accum(anyelement, variadic anyarray); +alter extension postgres_fdw drop aggregate least_agg(variadic items anyarray); +alter server loopback options (set extensions 'postgres_fdw'); +-- Not pushed down as we have dropped objects from extension. +explain (verbose, costs off) +select c2, least_agg(c1) from ft1 group by c2 order by c2; + QUERY PLAN +---------------------------------------------------------------------------------- + GroupAggregate + Output: c2, least_agg(VARIADIC ARRAY[c1]) + Group Key: ft1.c2 + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" ORDER BY c2 ASC NULLS LAST +(6 rows) + +-- Cleanup +reset enable_hashagg; +drop aggregate least_agg(variadic items anyarray); +drop function least_accum(anyelement, variadic anyarray); +-- Testing USING OPERATOR() in ORDER BY within aggregate. +-- For this, we need user defined operators along with operator family and +-- operator class. Create those and then add them in extension. Note that +-- user defined objects are considered unshippable unless they are part of +-- the extension. +create operator public.<^ ( + leftarg = int4, + rightarg = int4, + procedure = int4eq +); +create operator public.=^ ( + leftarg = int4, + rightarg = int4, + procedure = int4lt +); +create operator public.>^ ( + leftarg = int4, + rightarg = int4, + procedure = int4gt +); +create operator family my_op_family using btree; +create function my_op_cmp(a int, b int) returns int as + $$begin return btint4cmp(a, b); end $$ language plpgsql; +create operator class my_op_class for type int using btree family my_op_family as + operator 1 public.<^, + operator 3 public.=^, + operator 5 public.>^, + function 1 my_op_cmp(int, int); +-- This will not be pushed as user defined sort operator is not part of the +-- extension yet. +explain (verbose, costs off) +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + QUERY PLAN +-------------------------------------------------------------------------------------------- + GroupAggregate + Output: array_agg(c1 ORDER BY c1 USING <^ NULLS LAST), c2 + Group Key: ft2.c2 + -> Foreign Scan on public.ft2 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" WHERE (("C 1" < 100)) AND ((c2 = 6)) +(6 rows) + +-- Add into extension +alter extension postgres_fdw add operator class my_op_class using btree; +alter extension postgres_fdw add function my_op_cmp(a int, b int); +alter extension postgres_fdw add operator family my_op_family using btree; +alter extension postgres_fdw add operator public.<^(int, int); +alter extension postgres_fdw add operator public.=^(int, int); +alter extension postgres_fdw add operator public.>^(int, int); +alter server loopback options (set extensions 'postgres_fdw'); +-- Now this will be pushed as sort operator is part of the extension. +explain (verbose, costs off) +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: (array_agg(c1 ORDER BY c1 USING <^ NULLS LAST)), c2 + Relations: Aggregate on (public.ft2) + Remote SQL: SELECT array_agg("C 1" ORDER BY "C 1" USING OPERATOR(public.<^) NULLS LAST), c2 FROM "S 1"."T 1" WHERE (("C 1" < 100)) AND ((c2 = 6)) GROUP BY c2 +(4 rows) + +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + array_agg +-------------------------------- + {6,16,26,36,46,56,66,76,86,96} +(1 row) + +-- Remove from extension +alter extension postgres_fdw drop operator class my_op_class using btree; +alter extension postgres_fdw drop function my_op_cmp(a int, b int); +alter extension postgres_fdw drop operator family my_op_family using btree; +alter extension postgres_fdw drop operator public.<^(int, int); +alter extension postgres_fdw drop operator public.=^(int, int); +alter extension postgres_fdw drop operator public.>^(int, int); +alter server loopback options (set extensions 'postgres_fdw'); +-- This will not be pushed as sort operator is now removed from the extension. +explain (verbose, costs off) +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + QUERY PLAN +-------------------------------------------------------------------------------------------- + GroupAggregate + Output: array_agg(c1 ORDER BY c1 USING <^ NULLS LAST), c2 + Group Key: ft2.c2 + -> Foreign Scan on public.ft2 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" WHERE (("C 1" < 100)) AND ((c2 = 6)) +(6 rows) + +-- Cleanup +drop operator class my_op_class using btree; +drop function my_op_cmp(a int, b int); +drop operator family my_op_family using btree; +drop operator public.>^(int, int); +drop operator public.=^(int, int); +drop operator public.<^(int, int); +-- Input relation to aggregate push down hook is not safe to pushdown and thus +-- the aggregate cannot be pushed down to foreign server. +explain (verbose, costs off) +select count(t1.c3) from ft1 t1, ft1 t2 where t1.c1 = postgres_fdw_abs(t1.c2); + QUERY PLAN +---------------------------------------------------------------------------------------------------------- + Aggregate + Output: count(t1.c3) + -> Nested Loop + Output: t1.c3 + -> Foreign Scan on public.ft1 t2 + Remote SQL: SELECT NULL FROM "S 1"."T 1" + -> Materialize + Output: t1.c3 + -> Foreign Scan on public.ft1 t1 + Output: t1.c3 + Remote SQL: SELECT c3 FROM "S 1"."T 1" WHERE (("C 1" = public.postgres_fdw_abs(c2))) +(11 rows) + +-- Subquery in FROM clause having aggregate +explain (verbose, costs off) +select count(*), x.b from ft1, (select c2 a, sum(c1) b from ft1 group by c2) x where ft1.c2 = x.a group by x.b order by 1, 2; + QUERY PLAN +------------------------------------------------------------------------------------------------ + Sort + Output: (count(*)), x.b + Sort Key: (count(*)), x.b + -> HashAggregate + Output: count(*), x.b + Group Key: x.b + -> Hash Join + Output: x.b + Inner Unique: true + Hash Cond: (ft1.c2 = x.a) + -> Foreign Scan on public.ft1 + Output: ft1.c2 + Remote SQL: SELECT c2 FROM "S 1"."T 1" + -> Hash + Output: x.b, x.a + -> Subquery Scan on x + Output: x.b, x.a + -> Foreign Scan + Output: ft1_1.c2, (sum(ft1_1.c1)) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c2, sum("C 1") FROM "S 1"."T 1" GROUP BY c2 +(21 rows) + +select count(*), x.b from ft1, (select c2 a, sum(c1) b from ft1 group by c2) x where ft1.c2 = x.a group by x.b order by 1, 2; + count | b +-------+------- + 100 | 49600 + 100 | 49700 + 100 | 49800 + 100 | 49900 + 100 | 50000 + 100 | 50100 + 100 | 50200 + 100 | 50300 + 100 | 50400 + 100 | 50500 +(10 rows) + +-- FULL join with IS NULL check in HAVING +explain (verbose, costs off) +select avg(t1.c1), sum(t2.c1) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) group by t2.c1 having (avg(t1.c1) is null and sum(t2.c1) < 10) or sum(t2.c1) is null order by 1 nulls last, 2; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Sort + Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1 + Sort Key: (avg(t1.c1)), (sum(t2.c1)) + -> Foreign Scan + Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1 + Relations: Aggregate on ((public.ft4 t1) FULL JOIN (public.ft5 t2)) + Remote SQL: SELECT avg(r1.c1), sum(r2.c1), r2.c1 FROM ("S 1"."T 3" r1 FULL JOIN "S 1"."T 4" r2 ON (((r1.c1 = r2.c1)))) GROUP BY r2.c1 HAVING ((((avg(r1.c1) IS NULL) AND (sum(r2.c1) < 10)) OR (sum(r2.c1) IS NULL))) +(7 rows) + +select avg(t1.c1), sum(t2.c1) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) group by t2.c1 having (avg(t1.c1) is null and sum(t2.c1) < 10) or sum(t2.c1) is null order by 1 nulls last, 2; + avg | sum +---------------------+----- + 51.0000000000000000 | + | 3 + | 9 +(3 rows) + +-- Aggregate over FULL join needing to deparse the joining relations as +-- subqueries. +explain (verbose, costs off) +select count(*), sum(t1.c1), avg(t2.c1) from (select c1 from ft4 where c1 between 50 and 60) t1 full join (select c1 from ft5 where c1 between 50 and 60) t2 on (t1.c1 = t2.c1); + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan + Output: (count(*)), (sum(ft4.c1)), (avg(ft5.c1)) + Relations: Aggregate on ((public.ft4) FULL JOIN (public.ft5)) + Remote SQL: SELECT count(*), sum(s4.c1), avg(s5.c1) FROM ((SELECT c1 FROM "S 1"."T 3" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s4(c1) FULL JOIN (SELECT c1 FROM "S 1"."T 4" WHERE ((c1 >= 50)) AND ((c1 <= 60))) s5(c1) ON (((s4.c1 = s5.c1)))) +(4 rows) + +select count(*), sum(t1.c1), avg(t2.c1) from (select c1 from ft4 where c1 between 50 and 60) t1 full join (select c1 from ft5 where c1 between 50 and 60) t2 on (t1.c1 = t2.c1); + count | sum | avg +-------+-----+--------------------- + 8 | 330 | 55.5000000000000000 +(1 row) + +-- ORDER BY expression is part of the target list but not pushed down to +-- foreign server. +explain (verbose, costs off) +select sum(c2) * (random() <= 1)::int as sum from ft1 order by 1; + QUERY PLAN +-------------------------------------------------------------------------------- + Sort + Output: (((sum(c2)) * ((random() <= '1'::double precision))::integer)) + Sort Key: (((sum(ft1.c2)) * ((random() <= '1'::double precision))::integer)) + -> Foreign Scan + Output: ((sum(c2)) * ((random() <= '1'::double precision))::integer) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT sum(c2) FROM "S 1"."T 1" +(7 rows) + +select sum(c2) * (random() <= 1)::int as sum from ft1 order by 1; + sum +------ + 4500 +(1 row) + +-- LATERAL join, with parameterization +set enable_hashagg to false; +explain (verbose, costs off) +select c2, sum from "S 1"."T 1" t1, lateral (select sum(t2.c1 + t1."C 1") sum from ft2 t2 group by t2.c1) qry where t1.c2 * 2 = qry.sum and t1.c2 < 3 and t1."C 1" < 100 order by 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------------- + Sort + Output: t1.c2, qry.sum + Sort Key: t1.c2 + -> Nested Loop + Output: t1.c2, qry.sum + -> Index Scan using t1_pkey on "S 1"."T 1" t1 + Output: t1."C 1", t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8 + Index Cond: (t1."C 1" < 100) + Filter: (t1.c2 < 3) + -> Subquery Scan on qry + Output: qry.sum, t2.c1 + Filter: ((t1.c2 * 2) = qry.sum) + -> Foreign Scan + Output: (sum((t2.c1 + t1."C 1"))), t2.c1 + Relations: Aggregate on (public.ft2 t2) + Remote SQL: SELECT sum(("C 1" + $1::integer)), "C 1" FROM "S 1"."T 1" GROUP BY "C 1" +(16 rows) + +select c2, sum from "S 1"."T 1" t1, lateral (select sum(t2.c1 + t1."C 1") sum from ft2 t2 group by t2.c1) qry where t1.c2 * 2 = qry.sum and t1.c2 < 3 and t1."C 1" < 100 order by 1; + c2 | sum +----+----- + 1 | 2 + 2 | 4 +(2 rows) + +reset enable_hashagg; +-- Check with placeHolderVars +explain (verbose, costs off) +select sum(q.a), count(q.b) from ft4 left join (select 13, avg(ft1.c1), sum(ft2.c1) from ft1 right join ft2 on (ft1.c1 = ft2.c1)) q(a, b, c) on (ft4.c1 <= q.b); + QUERY PLAN +---------------------------------------------------------------------------------------------------------------------------------------------------------- + Aggregate + Output: sum(q.a), count(q.b) + -> Nested Loop Left Join + Output: q.a, q.b + Inner Unique: true + Join Filter: ((ft4.c1)::numeric <= q.b) + -> Foreign Scan on public.ft4 + Output: ft4.c1, ft4.c2, ft4.c3 + Remote SQL: SELECT c1 FROM "S 1"."T 3" + -> Materialize + Output: q.a, q.b + -> Subquery Scan on q + Output: q.a, q.b + -> Foreign Scan + Output: 13, (avg(ft1.c1)), NULL::bigint + Relations: Aggregate on ((public.ft2) LEFT JOIN (public.ft1)) + Remote SQL: SELECT 13, avg(r1."C 1"), NULL::bigint FROM ("S 1"."T 1" r2 LEFT JOIN "S 1"."T 1" r1 ON (((r1."C 1" = r2."C 1")))) +(17 rows) + +select sum(q.a), count(q.b) from ft4 left join (select 13, avg(ft1.c1), sum(ft2.c1) from ft1 right join ft2 on (ft1.c1 = ft2.c1)) q(a, b, c) on (ft4.c1 <= q.b); + sum | count +-----+------- + 650 | 50 +(1 row) + +-- Not supported cases +-- Grouping sets +explain (verbose, costs off) +select c2, sum(c1) from ft1 where c2 < 3 group by rollup(c2) order by 1 nulls last; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Output: c2, (sum(c1)) + Sort Key: ft1.c2 + -> MixedAggregate + Output: c2, sum(c1) + Hash Key: ft1.c2 + Group Key: () + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" WHERE ((c2 < 3)) +(10 rows) + +select c2, sum(c1) from ft1 where c2 < 3 group by rollup(c2) order by 1 nulls last; + c2 | sum +----+-------- + 0 | 50500 + 1 | 49600 + 2 | 49700 + | 149800 +(4 rows) + +explain (verbose, costs off) +select c2, sum(c1) from ft1 where c2 < 3 group by cube(c2) order by 1 nulls last; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Output: c2, (sum(c1)) + Sort Key: ft1.c2 + -> MixedAggregate + Output: c2, sum(c1) + Hash Key: ft1.c2 + Group Key: () + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" WHERE ((c2 < 3)) +(10 rows) + +select c2, sum(c1) from ft1 where c2 < 3 group by cube(c2) order by 1 nulls last; + c2 | sum +----+-------- + 0 | 50500 + 1 | 49600 + 2 | 49700 + | 149800 +(4 rows) + +explain (verbose, costs off) +select c2, c6, sum(c1) from ft1 where c2 < 3 group by grouping sets(c2, c6) order by 1 nulls last, 2 nulls last; + QUERY PLAN +---------------------------------------------------------------------------------- + Sort + Output: c2, c6, (sum(c1)) + Sort Key: ft1.c2, ft1.c6 + -> HashAggregate + Output: c2, c6, sum(c1) + Hash Key: ft1.c2 + Hash Key: ft1.c6 + -> Foreign Scan on public.ft1 + Output: c2, c6, c1 + Remote SQL: SELECT "C 1", c2, c6 FROM "S 1"."T 1" WHERE ((c2 < 3)) +(10 rows) + +select c2, c6, sum(c1) from ft1 where c2 < 3 group by grouping sets(c2, c6) order by 1 nulls last, 2 nulls last; + c2 | c6 | sum +----+----+------- + 0 | | 50500 + 1 | | 49600 + 2 | | 49700 + | 0 | 50500 + | 1 | 49600 + | 2 | 49700 +(6 rows) + +explain (verbose, costs off) +select c2, sum(c1), grouping(c2) from ft1 where c2 < 3 group by c2 order by 1 nulls last; + QUERY PLAN +------------------------------------------------------------------------------ + Sort + Output: c2, (sum(c1)), (GROUPING(c2)) + Sort Key: ft1.c2 + -> HashAggregate + Output: c2, sum(c1), GROUPING(c2) + Group Key: ft1.c2 + -> Foreign Scan on public.ft1 + Output: c2, c1 + Remote SQL: SELECT "C 1", c2 FROM "S 1"."T 1" WHERE ((c2 < 3)) +(9 rows) + +select c2, sum(c1), grouping(c2) from ft1 where c2 < 3 group by c2 order by 1 nulls last; + c2 | sum | grouping +----+-------+---------- + 0 | 50500 | 0 + 1 | 49600 | 0 + 2 | 49700 | 0 +(3 rows) + +-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed +explain (verbose, costs off) +select distinct sum(c1)/1000 s from ft2 where c2 < 6 group by c2 order by 1; + QUERY PLAN +-------------------------------------------------------------------------------------------------------- + Unique + Output: ((sum(c1) / 1000)), c2 + -> Sort + Output: ((sum(c1) / 1000)), c2 + Sort Key: ((sum(ft2.c1) / 1000)) + -> Foreign Scan + Output: ((sum(c1) / 1000)), c2 + Relations: Aggregate on (public.ft2) + Remote SQL: SELECT (sum("C 1") / 1000), c2 FROM "S 1"."T 1" WHERE ((c2 < 6)) GROUP BY c2 +(9 rows) + +select distinct sum(c1)/1000 s from ft2 where c2 < 6 group by c2 order by 1; + s +---- + 49 + 50 +(2 rows) + +-- WindowAgg +explain (verbose, costs off) +select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------- + Sort + Output: c2, (sum(c2)), (count(c2) OVER (?)), ((c2 % 2)) + Sort Key: ft2.c2 + -> WindowAgg + Output: c2, (sum(c2)), count(c2) OVER (?), ((c2 % 2)) + -> Sort + Output: c2, ((c2 % 2)), (sum(c2)) + Sort Key: ((ft2.c2 % 2)) + -> Foreign Scan + Output: c2, ((c2 % 2)), (sum(c2)) + Relations: Aggregate on (public.ft2) + Remote SQL: SELECT c2, (c2 % 2), sum(c2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY c2 +(12 rows) + +select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1; + c2 | sum | count +----+-----+------- + 0 | 0 | 5 + 1 | 100 | 5 + 2 | 200 | 5 + 3 | 300 | 5 + 4 | 400 | 5 + 5 | 500 | 5 + 6 | 600 | 5 + 7 | 700 | 5 + 8 | 800 | 5 + 9 | 900 | 5 +(10 rows) + +explain (verbose, costs off) +select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------- + Sort + Output: c2, (array_agg(c2) OVER (?)), ((c2 % 2)) + Sort Key: ft1.c2 + -> WindowAgg + Output: c2, array_agg(c2) OVER (?), ((c2 % 2)) + -> Sort + Output: c2, ((c2 % 2)) + Sort Key: ((ft1.c2 % 2)), ft1.c2 DESC + -> Foreign Scan + Output: c2, ((c2 % 2)) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c2, (c2 % 2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY c2 +(12 rows) + +select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1; + c2 | array_agg +----+------------- + 0 | {8,6,4,2,0} + 1 | {9,7,5,3,1} + 2 | {8,6,4,2} + 3 | {9,7,5,3} + 4 | {8,6,4} + 5 | {9,7,5} + 6 | {8,6} + 7 | {9,7} + 8 | {8} + 9 | {9} +(10 rows) + +explain (verbose, costs off) +select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1; + QUERY PLAN +---------------------------------------------------------------------------------------------------- + Sort + Output: c2, (array_agg(c2) OVER (?)), ((c2 % 2)) + Sort Key: ft1.c2 + -> WindowAgg + Output: c2, array_agg(c2) OVER (?), ((c2 % 2)) + -> Sort + Output: c2, ((c2 % 2)) + Sort Key: ((ft1.c2 % 2)), ft1.c2 + -> Foreign Scan + Output: c2, ((c2 % 2)) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT c2, (c2 % 2) FROM "S 1"."T 1" WHERE ((c2 < 10)) GROUP BY c2 +(12 rows) + +select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1; + c2 | array_agg +----+------------- + 0 | {0,2,4,6,8} + 1 | {1,3,5,7,9} + 2 | {2,4,6,8} + 3 | {3,5,7,9} + 4 | {4,6,8} + 5 | {5,7,9} + 6 | {6,8} + 7 | {7,9} + 8 | {8} + 9 | {9} +(10 rows) + +-- =================================================================== -- parameterized queries -- =================================================================== -- simple join @@ -2479,13 +3734,100 @@ EXECUTE st5('foo', 1); 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo (1 row) +-- altering FDW options requires replanning +PREPARE st6 AS SELECT * FROM ft1 t1 WHERE t1.c1 = t1.c2; +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6; + QUERY PLAN +---------------------------------------------------------------------------------------------- + Foreign Scan on public.ft1 t1 + Output: c1, c2, c3, c4, c5, c6, c7, c8 + Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE (("C 1" = c2)) +(3 rows) + +PREPARE st7 AS INSERT INTO ft1 (c1,c2,c3) VALUES (1001,101,'foo'); +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Insert on public.ft1 + Remote SQL: INSERT INTO "S 1"."T 1"("C 1", c2, c3, c4, c5, c6, c7, c8) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + -> Result + Output: NULL::integer, 1001, 101, 'foo'::text, NULL::timestamp with time zone, NULL::timestamp without time zone, NULL::character varying, 'ft1 '::character(10), NULL::user_enum +(4 rows) + +ALTER TABLE "S 1"."T 1" RENAME TO "T 0"; +ALTER FOREIGN TABLE ft1 OPTIONS (SET table_name 'T 0'); +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6; + QUERY PLAN +---------------------------------------------------------------------------------------------- + Foreign Scan on public.ft1 t1 + Output: c1, c2, c3, c4, c5, c6, c7, c8 + Remote SQL: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 0" WHERE (("C 1" = c2)) +(3 rows) + +EXECUTE st6; + c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 +----+----+-------+------------------------------+--------------------------+----+------------+----- + 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo + 2 | 2 | 00002 | Sat Jan 03 00:00:00 1970 PST | Sat Jan 03 00:00:00 1970 | 2 | 2 | foo + 3 | 3 | 00003 | Sun Jan 04 00:00:00 1970 PST | Sun Jan 04 00:00:00 1970 | 3 | 3 | foo + 4 | 4 | 00004 | Mon Jan 05 00:00:00 1970 PST | Mon Jan 05 00:00:00 1970 | 4 | 4 | foo + 5 | 5 | 00005 | Tue Jan 06 00:00:00 1970 PST | Tue Jan 06 00:00:00 1970 | 5 | 5 | foo + 6 | 6 | 00006 | Wed Jan 07 00:00:00 1970 PST | Wed Jan 07 00:00:00 1970 | 6 | 6 | foo + 7 | 7 | 00007 | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7 | 7 | foo + 8 | 8 | 00008 | Fri Jan 09 00:00:00 1970 PST | Fri Jan 09 00:00:00 1970 | 8 | 8 | foo + 9 | 9 | 00009 | Sat Jan 10 00:00:00 1970 PST | Sat Jan 10 00:00:00 1970 | 9 | 9 | foo +(9 rows) + +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + Insert on public.ft1 + Remote SQL: INSERT INTO "S 1"."T 0"("C 1", c2, c3, c4, c5, c6, c7, c8) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + -> Result + Output: NULL::integer, 1001, 101, 'foo'::text, NULL::timestamp with time zone, NULL::timestamp without time zone, NULL::character varying, 'ft1 '::character(10), NULL::user_enum +(4 rows) + +ALTER TABLE "S 1"."T 0" RENAME TO "T 1"; +ALTER FOREIGN TABLE ft1 OPTIONS (SET table_name 'T 1'); +PREPARE st8 AS SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8; + QUERY PLAN +----------------------------------------------------------------------------------------- + Foreign Scan + Output: (count(c3)) + Relations: Aggregate on (public.ft1 t1) + Remote SQL: SELECT count(c3) FROM "S 1"."T 1" WHERE (("C 1" OPERATOR(public.===) c2)) +(4 rows) + +ALTER SERVER loopback OPTIONS (DROP extensions); +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8; + QUERY PLAN +----------------------------------------------------------- + Aggregate + Output: count(c3) + -> Foreign Scan on public.ft1 t1 + Output: c3 + Filter: (t1.c1 === t1.c2) + Remote SQL: SELECT "C 1", c2, c3 FROM "S 1"."T 1" +(6 rows) + +EXECUTE st8; + count +------- + 9 +(1 row) + +ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); -- cleanup DEALLOCATE st1; DEALLOCATE st2; DEALLOCATE st3; DEALLOCATE st4; DEALLOCATE st5; --- System columns, except ctid, should not be sent to remote +DEALLOCATE st6; +DEALLOCATE st7; +DEALLOCATE st8; +-- System columns, except ctid and oid, should not be sent to remote EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1; QUERY PLAN @@ -2553,8 +3895,23 @@ SELECT ctid, * FROM ft1 t1 LIMIT 1; (0,1) | 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo (1 row) +EXPLAIN (VERBOSE, COSTS OFF) +SELECT oid, * FROM ft_pg_type WHERE typname = 'int4'; + QUERY PLAN +---------------------------------------------------------------------------------------------------- + Foreign Scan on public.ft_pg_type + Output: oid, typname, typlen + Remote SQL: SELECT typname, typlen, oid FROM pg_catalog.pg_type WHERE ((typname = 'int4'::name)) +(3 rows) + +SELECT oid, * FROM ft_pg_type WHERE typname = 'int4'; + oid | typname | typlen +-----+---------+-------- + 23 | int4 | 4 +(1 row) + -- =================================================================== --- used in pl/pgsql function +-- used in PL/pgSQL function -- =================================================================== CREATE OR REPLACE FUNCTION f_test(p_c1 int) RETURNS int AS $$ DECLARE @@ -2585,6 +3942,9 @@ CONTEXT: column "c8" of foreign table "ft1" SELECT ft1.c1, ft2.c2, ft1 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR ERROR: invalid input syntax for integer: "foo" CONTEXT: whole-row reference to foreign table "ft1" +SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR +ERROR: invalid input syntax for integer: "foo" +CONTEXT: processing expression at position 2 in select list ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum; -- =================================================================== -- subtransaction @@ -2720,20 +4080,18 @@ explain (verbose, costs off) select * from ft3 where f2 = 'foo' COLLATE "C"; explain (verbose, costs off) select * from ft3 f, loct3 l where f.f3 = l.f3 COLLATE "POSIX" and l.f1 = 'foo'; - QUERY PLAN -------------------------------------------------------------- - Hash Join + QUERY PLAN +--------------------------------------------------------- + Nested Loop Output: f.f1, f.f2, f.f3, l.f1, l.f2, l.f3 - Hash Cond: ((f.f3)::text = (l.f3)::text) + Join Filter: ((f.f3)::text = (l.f3)::text) + -> Index Scan using loct3_f1_key on public.loct3 l + Output: l.f1, l.f2, l.f3 + Index Cond: (l.f1 = 'foo'::text) -> Foreign Scan on public.ft3 f Output: f.f1, f.f2, f.f3 Remote SQL: SELECT f1, f2, f3 FROM public.loct3 - -> Hash - Output: l.f1, l.f2, l.f3 - -> Index Scan using loct3_f1_key on public.loct3 l - Output: l.f1, l.f2, l.f3 - Index Cond: (l.f1 = 'foo'::text) -(11 rows) +(9 rows) -- =================================================================== -- test writable foreign table stuff @@ -4418,12 +5776,12 @@ SELECT * FROM ft1 ORDER BY c6 ASC NULLS FIRST, c1 OFFSET 15 LIMIT 10; -- Consistent check constraints provide consistent results ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2positive CHECK (c2 >= 0); EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 < 0; - QUERY PLAN -------------------------------------------------------------------- - Aggregate - Output: count(*) - -> Foreign Scan on public.ft1 - Remote SQL: SELECT NULL FROM "S 1"."T 1" WHERE ((c2 < 0)) + QUERY PLAN +----------------------------------------------------------------- + Foreign Scan + Output: (count(*)) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT count(*) FROM "S 1"."T 1" WHERE ((c2 < 0)) (4 rows) SELECT count(*) FROM ft1 WHERE c2 < 0; @@ -4462,12 +5820,12 @@ ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c2positive; -- But inconsistent check constraints provide inconsistent results ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c2negative CHECK (c2 < 0); EXPLAIN (VERBOSE, COSTS OFF) SELECT count(*) FROM ft1 WHERE c2 >= 0; - QUERY PLAN --------------------------------------------------------------------- - Aggregate - Output: count(*) - -> Foreign Scan on public.ft1 - Remote SQL: SELECT NULL FROM "S 1"."T 1" WHERE ((c2 >= 0)) + QUERY PLAN +------------------------------------------------------------------ + Foreign Scan + Output: (count(*)) + Relations: Aggregate on (public.ft1) + Remote SQL: SELECT count(*) FROM "S 1"."T 1" WHERE ((c2 >= 0)) (4 rows) SELECT count(*) FROM ft1 WHERE c2 >= 0; @@ -5213,6 +6571,7 @@ select * from bar where f1 in (select f1 from foo) for update; Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid -> Hash Join Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid + Inner Unique: true Hash Cond: (bar.f1 = foo.f1) -> Append -> Seq Scan on public.bar @@ -5231,7 +6590,7 @@ select * from bar where f1 in (select f1 from foo) for update; -> Foreign Scan on public.foo2 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 -(22 rows) +(23 rows) select * from bar where f1 in (select f1 from foo) for update; f1 | f2 @@ -5250,6 +6609,7 @@ select * from bar where f1 in (select f1 from foo) for share; Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid -> Hash Join Output: bar.f1, bar.f2, bar.ctid, bar.*, bar.tableoid, foo.ctid, foo.*, foo.tableoid + Inner Unique: true Hash Cond: (bar.f1 = foo.f1) -> Append -> Seq Scan on public.bar @@ -5268,7 +6628,7 @@ select * from bar where f1 in (select f1 from foo) for share; -> Foreign Scan on public.foo2 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 -(22 rows) +(23 rows) select * from bar where f1 in (select f1 from foo) for share; f1 | f2 @@ -5290,6 +6650,7 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo); Remote SQL: UPDATE public.loct2 SET f2 = $2 WHERE ctid = $1 -> Hash Join Output: bar.f1, (bar.f2 + 100), bar.ctid, foo.ctid, foo.*, foo.tableoid + Inner Unique: true Hash Cond: (bar.f1 = foo.f1) -> Seq Scan on public.bar Output: bar.f1, bar.f2, bar.ctid @@ -5306,6 +6667,7 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo); Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 -> Hash Join Output: bar2.f1, (bar2.f2 + 100), bar2.f3, bar2.ctid, foo.ctid, foo.*, foo.tableoid + Inner Unique: true Hash Cond: (bar2.f1 = foo.f1) -> Foreign Scan on public.bar2 Output: bar2.f1, bar2.f2, bar2.f3, bar2.ctid @@ -5321,7 +6683,7 @@ update bar set f2 = f2 + 100 where f1 in (select f1 from foo); -> Foreign Scan on public.foo2 Output: foo2.ctid, foo2.*, foo2.tableoid, foo2.f1 Remote SQL: SELECT f1, f2, f3, ctid FROM public.loct1 -(37 rows) +(39 rows) update bar set f2 = f2 + 100 where f1 in (select f1 from foo); select tableoid::regclass, * from bar order by 1,2; @@ -5579,6 +6941,9 @@ CREATE TABLE import_source.t3 (c1 timestamptz default now(), c2 typ1); CREATE TABLE import_source."x 4" (c1 float8, "C 2" text, c3 varchar(42)); CREATE TABLE import_source."x 5" (c1 float8); ALTER TABLE import_source."x 5" DROP COLUMN c1; +CREATE TABLE import_source.t4 (c1 int) PARTITION BY RANGE (c1); +CREATE TABLE import_source.t4_part PARTITION OF import_source.t4 + FOR VALUES FROM (1) TO (100); CREATE SCHEMA import_dest1; IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest1; \det+ import_dest1.* @@ -5588,48 +6953,56 @@ IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest1; import_dest1 | t1 | loopback | (schema_name 'import_source', table_name 't1') | import_dest1 | t2 | loopback | (schema_name 'import_source', table_name 't2') | import_dest1 | t3 | loopback | (schema_name 'import_source', table_name 't3') | + import_dest1 | t4 | loopback | (schema_name 'import_source', table_name 't4') | import_dest1 | x 4 | loopback | (schema_name 'import_source', table_name 'x 4') | import_dest1 | x 5 | loopback | (schema_name 'import_source', table_name 'x 5') | -(5 rows) +(6 rows) \d import_dest1.* - Foreign table "import_dest1.t1" - Column | Type | Modifiers | FDW Options ---------+-------------------+-----------+-------------------- - c1 | integer | | (column_name 'c1') - c2 | character varying | not null | (column_name 'c2') + Foreign table "import_dest1.t1" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-------------------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') + c2 | character varying | | not null | | (column_name 'c2') Server: loopback FDW Options: (schema_name 'import_source', table_name 't1') - Foreign table "import_dest1.t2" - Column | Type | Modifiers | FDW Options ---------+-------------------+---------------+-------------------- - c1 | integer | | (column_name 'c1') - c2 | character varying | | (column_name 'c2') - c3 | text | collate POSIX | (column_name 'c3') + Foreign table "import_dest1.t2" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-------------------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') + c2 | character varying | | | | (column_name 'c2') + c3 | text | POSIX | | | (column_name 'c3') Server: loopback FDW Options: (schema_name 'import_source', table_name 't2') - Foreign table "import_dest1.t3" - Column | Type | Modifiers | FDW Options ---------+--------------------------+-----------+-------------------- - c1 | timestamp with time zone | | (column_name 'c1') - c2 | typ1 | | (column_name 'c2') + Foreign table "import_dest1.t3" + Column | Type | Collation | Nullable | Default | FDW Options +--------+--------------------------+-----------+----------+---------+-------------------- + c1 | timestamp with time zone | | | | (column_name 'c1') + c2 | typ1 | | | | (column_name 'c2') Server: loopback FDW Options: (schema_name 'import_source', table_name 't3') - Foreign table "import_dest1.x 4" - Column | Type | Modifiers | FDW Options ---------+-----------------------+-----------+--------------------- - c1 | double precision | | (column_name 'c1') - C 2 | text | | (column_name 'C 2') - c3 | character varying(42) | | (column_name 'c3') + Foreign table "import_dest1.t4" + Column | Type | Collation | Nullable | Default | FDW Options +--------+---------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') +Server: loopback +FDW Options: (schema_name 'import_source', table_name 't4') + + Foreign table "import_dest1.x 4" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-----------------------+-----------+----------+---------+--------------------- + c1 | double precision | | | | (column_name 'c1') + C 2 | text | | | | (column_name 'C 2') + c3 | character varying(42) | | | | (column_name 'c3') Server: loopback FDW Options: (schema_name 'import_source', table_name 'x 4') - Foreign table "import_dest1.x 5" - Column | Type | Modifiers | FDW Options ---------+------+-----------+------------- + Foreign table "import_dest1.x 5" + Column | Type | Collation | Nullable | Default | FDW Options +--------+------+-----------+----------+---------+------------- Server: loopback FDW Options: (schema_name 'import_source', table_name 'x 5') @@ -5644,48 +7017,56 @@ IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest2 import_dest2 | t1 | loopback | (schema_name 'import_source', table_name 't1') | import_dest2 | t2 | loopback | (schema_name 'import_source', table_name 't2') | import_dest2 | t3 | loopback | (schema_name 'import_source', table_name 't3') | + import_dest2 | t4 | loopback | (schema_name 'import_source', table_name 't4') | import_dest2 | x 4 | loopback | (schema_name 'import_source', table_name 'x 4') | import_dest2 | x 5 | loopback | (schema_name 'import_source', table_name 'x 5') | -(5 rows) +(6 rows) \d import_dest2.* - Foreign table "import_dest2.t1" - Column | Type | Modifiers | FDW Options ---------+-------------------+-----------+-------------------- - c1 | integer | | (column_name 'c1') - c2 | character varying | not null | (column_name 'c2') + Foreign table "import_dest2.t1" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-------------------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') + c2 | character varying | | not null | | (column_name 'c2') Server: loopback FDW Options: (schema_name 'import_source', table_name 't1') - Foreign table "import_dest2.t2" - Column | Type | Modifiers | FDW Options ---------+-------------------+---------------+-------------------- - c1 | integer | default 42 | (column_name 'c1') - c2 | character varying | | (column_name 'c2') - c3 | text | collate POSIX | (column_name 'c3') + Foreign table "import_dest2.t2" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-------------------+-----------+----------+---------+-------------------- + c1 | integer | | | 42 | (column_name 'c1') + c2 | character varying | | | | (column_name 'c2') + c3 | text | POSIX | | | (column_name 'c3') Server: loopback FDW Options: (schema_name 'import_source', table_name 't2') - Foreign table "import_dest2.t3" - Column | Type | Modifiers | FDW Options ---------+--------------------------+---------------+-------------------- - c1 | timestamp with time zone | default now() | (column_name 'c1') - c2 | typ1 | | (column_name 'c2') + Foreign table "import_dest2.t3" + Column | Type | Collation | Nullable | Default | FDW Options +--------+--------------------------+-----------+----------+---------+-------------------- + c1 | timestamp with time zone | | | now() | (column_name 'c1') + c2 | typ1 | | | | (column_name 'c2') Server: loopback FDW Options: (schema_name 'import_source', table_name 't3') - Foreign table "import_dest2.x 4" - Column | Type | Modifiers | FDW Options ---------+-----------------------+-----------+--------------------- - c1 | double precision | | (column_name 'c1') - C 2 | text | | (column_name 'C 2') - c3 | character varying(42) | | (column_name 'c3') + Foreign table "import_dest2.t4" + Column | Type | Collation | Nullable | Default | FDW Options +--------+---------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') +Server: loopback +FDW Options: (schema_name 'import_source', table_name 't4') + + Foreign table "import_dest2.x 4" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-----------------------+-----------+----------+---------+--------------------- + c1 | double precision | | | | (column_name 'c1') + C 2 | text | | | | (column_name 'C 2') + c3 | character varying(42) | | | | (column_name 'c3') Server: loopback FDW Options: (schema_name 'import_source', table_name 'x 4') - Foreign table "import_dest2.x 5" - Column | Type | Modifiers | FDW Options ---------+------+-----------+------------- + Foreign table "import_dest2.x 5" + Column | Type | Collation | Nullable | Default | FDW Options +--------+------+-----------+----------+---------+------------- Server: loopback FDW Options: (schema_name 'import_source', table_name 'x 5') @@ -5699,48 +7080,56 @@ IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest3 import_dest3 | t1 | loopback | (schema_name 'import_source', table_name 't1') | import_dest3 | t2 | loopback | (schema_name 'import_source', table_name 't2') | import_dest3 | t3 | loopback | (schema_name 'import_source', table_name 't3') | + import_dest3 | t4 | loopback | (schema_name 'import_source', table_name 't4') | import_dest3 | x 4 | loopback | (schema_name 'import_source', table_name 'x 4') | import_dest3 | x 5 | loopback | (schema_name 'import_source', table_name 'x 5') | -(5 rows) +(6 rows) \d import_dest3.* - Foreign table "import_dest3.t1" - Column | Type | Modifiers | FDW Options ---------+-------------------+-----------+-------------------- - c1 | integer | | (column_name 'c1') - c2 | character varying | | (column_name 'c2') + Foreign table "import_dest3.t1" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-------------------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') + c2 | character varying | | | | (column_name 'c2') Server: loopback FDW Options: (schema_name 'import_source', table_name 't1') - Foreign table "import_dest3.t2" - Column | Type | Modifiers | FDW Options ---------+-------------------+-----------+-------------------- - c1 | integer | | (column_name 'c1') - c2 | character varying | | (column_name 'c2') - c3 | text | | (column_name 'c3') + Foreign table "import_dest3.t2" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-------------------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') + c2 | character varying | | | | (column_name 'c2') + c3 | text | | | | (column_name 'c3') Server: loopback FDW Options: (schema_name 'import_source', table_name 't2') - Foreign table "import_dest3.t3" - Column | Type | Modifiers | FDW Options ---------+--------------------------+-----------+-------------------- - c1 | timestamp with time zone | | (column_name 'c1') - c2 | typ1 | | (column_name 'c2') + Foreign table "import_dest3.t3" + Column | Type | Collation | Nullable | Default | FDW Options +--------+--------------------------+-----------+----------+---------+-------------------- + c1 | timestamp with time zone | | | | (column_name 'c1') + c2 | typ1 | | | | (column_name 'c2') Server: loopback FDW Options: (schema_name 'import_source', table_name 't3') - Foreign table "import_dest3.x 4" - Column | Type | Modifiers | FDW Options ---------+-----------------------+-----------+--------------------- - c1 | double precision | | (column_name 'c1') - C 2 | text | | (column_name 'C 2') - c3 | character varying(42) | | (column_name 'c3') + Foreign table "import_dest3.t4" + Column | Type | Collation | Nullable | Default | FDW Options +--------+---------+-----------+----------+---------+-------------------- + c1 | integer | | | | (column_name 'c1') +Server: loopback +FDW Options: (schema_name 'import_source', table_name 't4') + + Foreign table "import_dest3.x 4" + Column | Type | Collation | Nullable | Default | FDW Options +--------+-----------------------+-----------+----------+---------+--------------------- + c1 | double precision | | | | (column_name 'c1') + C 2 | text | | | | (column_name 'C 2') + c3 | character varying(42) | | | | (column_name 'c3') Server: loopback FDW Options: (schema_name 'import_source', table_name 'x 4') - Foreign table "import_dest3.x 5" - Column | Type | Modifiers | FDW Options ---------+------+-----------+------------- + Foreign table "import_dest3.x 5" + Column | Type | Collation | Nullable | Default | FDW Options +--------+------+-----------+----------+---------+------------- Server: loopback FDW Options: (schema_name 'import_source', table_name 'x 5') @@ -5764,8 +7153,9 @@ IMPORT FOREIGN SCHEMA import_source EXCEPT (t1, "x 4", nonesuch) import_dest4 | t1 | loopback | (schema_name 'import_source', table_name 't1') | import_dest4 | t2 | loopback | (schema_name 'import_source', table_name 't2') | import_dest4 | t3 | loopback | (schema_name 'import_source', table_name 't3') | + import_dest4 | t4 | loopback | (schema_name 'import_source', table_name 't4') | import_dest4 | x 5 | loopback | (schema_name 'import_source', table_name 'x 5') | -(4 rows) +(5 rows) -- Assorted error cases IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest4; diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 224aed948e..e24db569ea 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -3,7 +3,7 @@ * option.c * FDW option handling for postgres_fdw * - * Portions Copyright (c) 2012-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2012-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/postgres_fdw/option.c @@ -21,6 +21,7 @@ #include "commands/defrem.h" #include "commands/extension.h" #include "utils/builtins.h" +#include "utils/varlena.h" /* diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 931bcfd37d..080cb0a074 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -3,7 +3,7 @@ * postgres_fdw.c * Foreign-data wrapper for remote PostgreSQL servers * - * Portions Copyright (c) 2012-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2012-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/postgres_fdw/postgres_fdw.c @@ -16,6 +16,7 @@ #include "access/htup_details.h" #include "access/sysattr.h" +#include "catalog/pg_class.h" #include "commands/defrem.h" #include "commands/explain.h" #include "commands/vacuum.h" @@ -25,6 +26,7 @@ #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/cost.h" +#include "optimizer/clauses.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/planmain.h" @@ -38,6 +40,7 @@ #include "utils/memutils.h" #include "utils/rel.h" #include "utils/sampling.h" +#include "utils/selfuncs.h" PG_MODULE_MAGIC; @@ -61,8 +64,6 @@ enum FdwScanPrivateIndex { /* SQL statement to execute remotely (as a String node) */ FdwScanPrivateSelectSql, - /* List of restriction clauses that can be executed remotely */ - FdwScanPrivateRemoteConds, /* Integer list of attribute numbers retrieved by the SELECT */ FdwScanPrivateRetrievedAttrs, /* Integer representing the desired fetch_size */ @@ -343,6 +344,10 @@ static void postgresGetForeignJoinPaths(PlannerInfo *root, JoinPathExtraData *extra); static bool postgresRecheckForeignScan(ForeignScanState *node, TupleTableSlot *slot); +static void postgresGetForeignUpperPaths(PlannerInfo *root, + UpperRelationKind stage, + RelOptInfo *input_rel, + RelOptInfo *output_rel); /* * Helper functions @@ -400,11 +405,20 @@ static void conversion_error_callback(void *arg); static bool foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinPathExtraData *extra); +static bool foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel); static List *get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel); static List *get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel); static void add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel, Path *epq_path); +static void add_foreign_grouping_paths(PlannerInfo *root, + RelOptInfo *input_rel, + RelOptInfo *grouped_rel); +static void apply_server_options(PgFdwRelationInfo *fpinfo); +static void apply_table_options(PgFdwRelationInfo *fpinfo); +static void merge_fdw_options(PgFdwRelationInfo *fpinfo, + const PgFdwRelationInfo *fpinfo_o, + const PgFdwRelationInfo *fpinfo_i); /* @@ -455,6 +469,9 @@ postgres_fdw_handler(PG_FUNCTION_ARGS) /* Support functions for join push-down */ routine->GetForeignJoinPaths = postgresGetForeignJoinPaths; + /* Support functions for upper relation push-down */ + routine->GetForeignUpperPaths = postgresGetForeignUpperPaths; + PG_RETURN_POINTER(routine); } @@ -484,7 +501,7 @@ postgresGetForeignRelSize(PlannerInfo *root, fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo)); baserel->fdw_private = (void *) fpinfo; - /* Base foreign tables need to be push down always. */ + /* Base foreign tables need to be pushed down always. */ fpinfo->pushdown_safe = true; /* Look up foreign-table catalog info. */ @@ -501,31 +518,8 @@ postgresGetForeignRelSize(PlannerInfo *root, fpinfo->shippable_extensions = NIL; fpinfo->fetch_size = 100; - foreach(lc, fpinfo->server->options) - { - DefElem *def = (DefElem *) lfirst(lc); - - if (strcmp(def->defname, "use_remote_estimate") == 0) - fpinfo->use_remote_estimate = defGetBoolean(def); - else if (strcmp(def->defname, "fdw_startup_cost") == 0) - fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL); - else if (strcmp(def->defname, "fdw_tuple_cost") == 0) - fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL); - else if (strcmp(def->defname, "extensions") == 0) - fpinfo->shippable_extensions = - ExtractExtensionList(defGetString(def), false); - else if (strcmp(def->defname, "fetch_size") == 0) - fpinfo->fetch_size = strtol(defGetString(def), NULL, 10); - } - foreach(lc, fpinfo->table->options) - { - DefElem *def = (DefElem *) lfirst(lc); - - if (strcmp(def->defname, "use_remote_estimate") == 0) - fpinfo->use_remote_estimate = defGetBoolean(def); - else if (strcmp(def->defname, "fetch_size") == 0) - fpinfo->fetch_size = strtol(defGetString(def), NULL, 10); - } + apply_server_options(fpinfo); + apply_table_options(fpinfo); /* * If the table or the server is configured to use remote estimates, @@ -562,7 +556,7 @@ postgresGetForeignRelSize(PlannerInfo *root, &fpinfo->attrs_used); foreach(lc, fpinfo->local_conds) { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); pull_varattnos((Node *) rinfo->clause, baserel->relid, &fpinfo->attrs_used); @@ -655,6 +649,13 @@ postgresGetForeignRelSize(PlannerInfo *root, if (*refname && strcmp(refname, relname) != 0) appendStringInfo(fpinfo->relation_name, " %s", quote_identifier(rte->eref->aliasname)); + + /* No outer and inner relations. */ + fpinfo->make_outerrel_subquery = false; + fpinfo->make_innerrel_subquery = false; + fpinfo->lower_subquery_rels = NULL; + /* Set the relation index. */ + fpinfo->relation_index = baserel->relid; } /* @@ -708,8 +709,11 @@ get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel) return useful_eclass_list; /* If this is a child rel, we must use the topmost parent rel to search. */ - if (rel->reloptkind == RELOPT_OTHER_MEMBER_REL) - relids = find_childrel_top_parent(root, rel)->relids; + if (IS_OTHER_REL(rel)) + { + Assert(!bms_is_empty(rel->top_parent_relids)); + relids = rel->top_parent_relids; + } else relids = rel->relids; @@ -1095,86 +1099,96 @@ postgresGetForeignPlan(PlannerInfo *root, PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; Index scan_relid; List *fdw_private; - List *remote_conds = NIL; List *remote_exprs = NIL; List *local_exprs = NIL; List *params_list = NIL; + List *fdw_scan_tlist = NIL; + List *fdw_recheck_quals = NIL; List *retrieved_attrs; StringInfoData sql; ListCell *lc; - List *fdw_scan_tlist = NIL; - /* - * For base relations, set scan_relid as the relid of the relation. For - * other kinds of relations set it to 0. - */ - if (foreignrel->reloptkind == RELOPT_BASEREL || - foreignrel->reloptkind == RELOPT_OTHER_MEMBER_REL) + if (IS_SIMPLE_REL(foreignrel)) + { + /* + * For base relations, set scan_relid as the relid of the relation. + */ scan_relid = foreignrel->relid; + + /* + * In a base-relation scan, we must apply the given scan_clauses. + * + * Separate the scan_clauses into those that can be executed remotely + * and those that can't. baserestrictinfo clauses that were + * previously determined to be safe or unsafe by classifyConditions + * are found in fpinfo->remote_conds and fpinfo->local_conds. Anything + * else in the scan_clauses list will be a join clause, which we have + * to check for remote-safety. + * + * Note: the join clauses we see here should be the exact same ones + * previously examined by postgresGetForeignPaths. Possibly it'd be + * worth passing forward the classification work done then, rather + * than repeating it here. + * + * This code must match "extract_actual_clauses(scan_clauses, false)" + * except for the additional decision about remote versus local + * execution. + */ + foreach(lc, scan_clauses) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + + /* Ignore any pseudoconstants, they're dealt with elsewhere */ + if (rinfo->pseudoconstant) + continue; + + if (list_member_ptr(fpinfo->remote_conds, rinfo)) + remote_exprs = lappend(remote_exprs, rinfo->clause); + else if (list_member_ptr(fpinfo->local_conds, rinfo)) + local_exprs = lappend(local_exprs, rinfo->clause); + else if (is_foreign_expr(root, foreignrel, rinfo->clause)) + remote_exprs = lappend(remote_exprs, rinfo->clause); + else + local_exprs = lappend(local_exprs, rinfo->clause); + } + + /* + * For a base-relation scan, we have to support EPQ recheck, which + * should recheck all the remote quals. + */ + fdw_recheck_quals = remote_exprs; + } else { + /* + * Join relation or upper relation - set scan_relid to 0. + */ scan_relid = 0; /* - * create_scan_plan() and create_foreignscan_plan() pass - * rel->baserestrictinfo + parameterization clauses through - * scan_clauses. For a join rel->baserestrictinfo is NIL and we are - * not considering parameterization right now, so there should be no - * scan_clauses for a joinrel. + * For a join rel, baserestrictinfo is NIL and we are not considering + * parameterization right now, so there should be no scan_clauses for + * a joinrel or an upper rel either. */ Assert(!scan_clauses); - } - /* - * Separate the scan_clauses into those that can be executed remotely and - * those that can't. baserestrictinfo clauses that were previously - * determined to be safe or unsafe by classifyConditions are shown in - * fpinfo->remote_conds and fpinfo->local_conds. Anything else in the - * scan_clauses list will be a join clause, which we have to check for - * remote-safety. - * - * Note: the join clauses we see here should be the exact same ones - * previously examined by postgresGetForeignPaths. Possibly it'd be worth - * passing forward the classification work done then, rather than - * repeating it here. - * - * This code must match "extract_actual_clauses(scan_clauses, false)" - * except for the additional decision about remote versus local execution. - * Note however that we don't strip the RestrictInfo nodes from the - * remote_conds list, since appendWhereClause expects a list of - * RestrictInfos. - */ - foreach(lc, scan_clauses) - { - RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - - Assert(IsA(rinfo, RestrictInfo)); - - /* Ignore any pseudoconstants, they're dealt with elsewhere */ - if (rinfo->pseudoconstant) - continue; - - if (list_member_ptr(fpinfo->remote_conds, rinfo)) - { - remote_conds = lappend(remote_conds, rinfo); - remote_exprs = lappend(remote_exprs, rinfo->clause); - } - else if (list_member_ptr(fpinfo->local_conds, rinfo)) - local_exprs = lappend(local_exprs, rinfo->clause); - else if (is_foreign_expr(root, foreignrel, rinfo->clause)) - { - remote_conds = lappend(remote_conds, rinfo); - remote_exprs = lappend(remote_exprs, rinfo->clause); - } - else - local_exprs = lappend(local_exprs, rinfo->clause); - } + /* + * Instead we get the conditions to apply from the fdw_private + * structure. + */ + remote_exprs = extract_actual_clauses(fpinfo->remote_conds, false); + local_exprs = extract_actual_clauses(fpinfo->local_conds, false); - if (foreignrel->reloptkind == RELOPT_JOINREL) - { - /* For a join relation, get the conditions from fdw_private structure */ - remote_conds = fpinfo->remote_conds; - local_exprs = fpinfo->local_conds; + /* + * We leave fdw_recheck_quals empty in this case, since we never need + * to apply EPQ recheck clauses. In the case of a joinrel, EPQ + * recheck is handled elsewhere --- see postgresGetForeignJoinPaths(). + * If we're planning an upperrel (ie, remote grouping or aggregation) + * then there's no EPQ to do because SELECT FOR UPDATE wouldn't be + * allowed, and indeed we *can't* put the remote clauses into + * fdw_recheck_quals because the unaggregated Vars won't be available + * locally. + */ /* Build the list of columns to be fetched from the foreign server. */ fdw_scan_tlist = build_tlist_to_deparse(foreignrel); @@ -1191,6 +1205,13 @@ postgresGetForeignPlan(PlannerInfo *root, { ListCell *lc; + /* + * Right now, we only consider grouping and aggregation beyond + * joins. Queries involving aggregates or grouping do not require + * EPQ mechanism, hence should not have an outer plan here. + */ + Assert(!IS_UPPER_REL(foreignrel)); + outer_plan->targetlist = fdw_scan_tlist; foreach(lc, local_exprs) @@ -1217,18 +1238,20 @@ postgresGetForeignPlan(PlannerInfo *root, */ initStringInfo(&sql); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, - remote_conds, best_path->path.pathkeys, - &retrieved_attrs, ¶ms_list); + remote_exprs, best_path->path.pathkeys, + false, &retrieved_attrs, ¶ms_list); + + /* Remember remote_exprs for possible use by postgresPlanDirectModify */ + fpinfo->final_remote_exprs = remote_exprs; /* * Build the fdw_private list that will be available to the executor. * Items in the list must match order in enum FdwScanPrivateIndex. */ - fdw_private = list_make4(makeString(sql.data), - remote_conds, + fdw_private = list_make3(makeString(sql.data), retrieved_attrs, makeInteger(fpinfo->fetch_size)); - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) fdw_private = lappend(fdw_private, makeString(fpinfo->relation_name->data)); @@ -1245,7 +1268,7 @@ postgresGetForeignPlan(PlannerInfo *root, params_list, fdw_private, fdw_scan_tlist, - remote_exprs, + fdw_recheck_quals, outer_plan); } @@ -1280,8 +1303,9 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. In case of a join, use the lowest-numbered - * member RTE as a representative; we would get the same result from any. + * ExecCheckRTEPerms() does. In case of a join or aggregate, use the + * lowest-numbered member RTE as a representative; we would get the same + * result from any. */ if (fsplan->scan.scanrelid > 0) rtindex = fsplan->scan.scanrelid; @@ -1315,14 +1339,10 @@ postgresBeginForeignScan(ForeignScanState *node, int eflags) /* Create contexts for batches of tuples and per-tuple temp workspace. */ fsstate->batch_cxt = AllocSetContextCreate(estate->es_query_cxt, "postgres_fdw tuple data", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); fsstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt, "postgres_fdw temporary data", - ALLOCSET_SMALL_MINSIZE, - ALLOCSET_SMALL_INITSIZE, - ALLOCSET_SMALL_MAXSIZE); + ALLOCSET_SMALL_SIZES); /* * Get info we'll need for converting data fetched from the foreign server @@ -1695,9 +1715,7 @@ postgresBeginForeignModify(ModifyTableState *mtstate, /* Create context for per-tuple temp workspace. */ fmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt, "postgres_fdw temporary data", - ALLOCSET_SMALL_MINSIZE, - ALLOCSET_SMALL_INITSIZE, - ALLOCSET_SMALL_MAXSIZE); + ALLOCSET_SMALL_SIZES); /* Prepare for input conversion of RETURNING results. */ if (fmstate->has_returning) @@ -2086,13 +2104,15 @@ postgresPlanDirectModify(PlannerInfo *root, int subplan_index) { CmdType operation = plan->operation; - Plan *subplan = (Plan *) list_nth(plan->plans, subplan_index); - RangeTblEntry *rte = planner_rt_fetch(resultRelation, root); + Plan *subplan; + RelOptInfo *foreignrel; + RangeTblEntry *rte; + PgFdwRelationInfo *fpinfo; Relation rel; StringInfoData sql; ForeignScan *fscan; List *targetAttrs = NIL; - List *remote_conds; + List *remote_exprs; List *params_list = NIL; List *returningList = NIL; List *retrieved_attrs = NIL; @@ -2111,8 +2131,10 @@ postgresPlanDirectModify(PlannerInfo *root, * It's unsafe to modify a foreign table directly if there are any local * joins needed. */ + subplan = (Plan *) list_nth(plan->plans, subplan_index); if (!IsA(subplan, ForeignScan)) return false; + fscan = (ForeignScan *) subplan; /* * It's unsafe to modify a foreign table directly if there are any quals @@ -2124,17 +2146,20 @@ postgresPlanDirectModify(PlannerInfo *root, /* * We can't handle an UPDATE or DELETE on a foreign join for now. */ - fscan = (ForeignScan *) subplan; if (fscan->scan.scanrelid == 0) return false; + /* Safe to fetch data about the target foreign rel */ + foreignrel = root->simple_rel_array[resultRelation]; + rte = root->simple_rte_array[resultRelation]; + fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private; + /* * It's unsafe to update a foreign table directly, if any expressions to * assign to the target columns are unsafe to evaluate remotely. */ if (operation == CMD_UPDATE) { - RelOptInfo *baserel = root->simple_rel_array[resultRelation]; int col; /* @@ -2157,7 +2182,7 @@ postgresPlanDirectModify(PlannerInfo *root, elog(ERROR, "attribute number %d not found in subplan targetlist", attno); - if (!is_foreign_expr(root, baserel, (Expr *) tle->expr)) + if (!is_foreign_expr(root, foreignrel, (Expr *) tle->expr)) return false; targetAttrs = lappend_int(targetAttrs, attno); @@ -2176,10 +2201,11 @@ postgresPlanDirectModify(PlannerInfo *root, rel = heap_open(rte->relid, NoLock); /* - * Extract the baserestrictinfo clauses that can be evaluated remotely. + * Recall the qual clauses that must be evaluated remotely. (These are + * bare clauses not RestrictInfos, but deparse.c's appendConditions() + * doesn't care.) */ - remote_conds = (List *) list_nth(fscan->fdw_private, - FdwScanPrivateRemoteConds); + remote_exprs = fpinfo->final_remote_exprs; /* * Extract the relevant RETURNING list if any. @@ -2196,12 +2222,12 @@ postgresPlanDirectModify(PlannerInfo *root, deparseDirectUpdateSql(&sql, root, resultRelation, rel, ((Plan *) fscan)->targetlist, targetAttrs, - remote_conds, ¶ms_list, + remote_exprs, ¶ms_list, returningList, &retrieved_attrs); break; case CMD_DELETE: deparseDirectDeleteSql(&sql, root, resultRelation, rel, - remote_conds, ¶ms_list, + remote_exprs, ¶ms_list, returningList, &retrieved_attrs); break; default: @@ -2294,9 +2320,7 @@ postgresBeginDirectModify(ForeignScanState *node, int eflags) /* Create context for per-tuple temp workspace. */ dmstate->temp_cxt = AllocSetContextCreate(estate->es_query_cxt, "postgres_fdw temporary data", - ALLOCSET_SMALL_MINSIZE, - ALLOCSET_SMALL_INITSIZE, - ALLOCSET_SMALL_MAXSIZE); + ALLOCSET_SMALL_SIZES); /* Prepare for input conversion of RETURNING results. */ if (dmstate->has_returning) @@ -2460,7 +2484,8 @@ postgresExplainDirectModify(ForeignScanState *node, ExplainState *es) /* * estimate_path_cost_size * Get cost and size estimates for a foreign scan on given foreign relation - * either a base relation or a join between foreign relations. + * either a base relation or a join between foreign relations or an upper + * relation containing foreign relations. * * param_join_conds are the parameterization clauses with outer relations. * pathkeys specify the expected sort order if any for given path being costed. @@ -2513,7 +2538,7 @@ estimate_path_cost_size(PlannerInfo *root, &remote_param_join_conds, &local_param_join_conds); /* Build the list of columns to be fetched from the foreign server. */ - if (foreignrel->reloptkind == RELOPT_JOINREL) + if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) fdw_scan_tlist = build_tlist_to_deparse(foreignrel); else fdw_scan_tlist = NIL; @@ -2534,8 +2559,8 @@ estimate_path_cost_size(PlannerInfo *root, initStringInfo(&sql); appendStringInfoString(&sql, "EXPLAIN "); deparseSelectStmtForRel(&sql, root, foreignrel, fdw_scan_tlist, - remote_conds, pathkeys, &retrieved_attrs, - NULL); + remote_conds, pathkeys, false, + &retrieved_attrs, NULL); /* Get the remote estimate */ conn = GetConnection(fpinfo->user, false); @@ -2594,25 +2619,7 @@ estimate_path_cost_size(PlannerInfo *root, startup_cost = fpinfo->rel_startup_cost; run_cost = fpinfo->rel_total_cost - fpinfo->rel_startup_cost; } - else if (foreignrel->reloptkind != RELOPT_JOINREL) - { - /* Clamp retrieved rows estimates to at most foreignrel->tuples. */ - retrieved_rows = Min(retrieved_rows, foreignrel->tuples); - - /* - * Cost as though this were a seqscan, which is pessimistic. We - * effectively imagine the local_conds are being evaluated - * remotely, too. - */ - startup_cost = 0; - run_cost = 0; - run_cost += seq_page_cost * foreignrel->pages; - - startup_cost += foreignrel->baserestrictcost.startup; - cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple; - run_cost += cpu_per_tuple * foreignrel->tuples; - } - else + else if (IS_JOIN_REL(foreignrel)) { PgFdwRelationInfo *fpinfo_i; PgFdwRelationInfo *fpinfo_o; @@ -2637,7 +2644,9 @@ estimate_path_cost_size(PlannerInfo *root, * rows. */ - /* Calculate the cost of clauses pushed down the foreign server */ + /* + * Calculate the cost of clauses pushed down to the foreign server + */ cost_qual_eval(&remote_conds_cost, fpinfo->remote_conds, root); /* Calculate the cost of applying join clauses */ cost_qual_eval(&join_cost, fpinfo->joinclauses, root); @@ -2676,6 +2685,99 @@ estimate_path_cost_size(PlannerInfo *root, run_cost += nrows * remote_conds_cost.per_tuple; run_cost += fpinfo->local_conds_cost.per_tuple * retrieved_rows; } + else if (IS_UPPER_REL(foreignrel)) + { + PgFdwRelationInfo *ofpinfo; + PathTarget *ptarget = root->upper_targets[UPPERREL_GROUP_AGG]; + AggClauseCosts aggcosts; + double input_rows; + int numGroupCols; + double numGroups = 1; + + /* + * This cost model is mixture of costing done for sorted and + * hashed aggregates in cost_agg(). We are not sure which + * strategy will be considered at remote side, thus for + * simplicity, we put all startup related costs in startup_cost + * and all finalization and run cost are added in total_cost. + * + * Also, core does not care about costing HAVING expressions and + * adding that to the costs. So similarly, here too we are not + * considering remote and local conditions for costing. + */ + + ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private; + + /* Get rows and width from input rel */ + input_rows = ofpinfo->rows; + width = ofpinfo->width; + + /* Collect statistics about aggregates for estimating costs. */ + MemSet(&aggcosts, 0, sizeof(AggClauseCosts)); + if (root->parse->hasAggs) + { + get_agg_clause_costs(root, (Node *) fpinfo->grouped_tlist, + AGGSPLIT_SIMPLE, &aggcosts); + get_agg_clause_costs(root, (Node *) root->parse->havingQual, + AGGSPLIT_SIMPLE, &aggcosts); + } + + /* Get number of grouping columns and possible number of groups */ + numGroupCols = list_length(root->parse->groupClause); + numGroups = estimate_num_groups(root, + get_sortgrouplist_exprs(root->parse->groupClause, + fpinfo->grouped_tlist), + input_rows, NULL); + + /* + * Number of rows expected from foreign server will be same as + * that of number of groups. + */ + rows = retrieved_rows = numGroups; + + /*----- + * Startup cost includes: + * 1. Startup cost for underneath input * relation + * 2. Cost of performing aggregation, per cost_agg() + * 3. Startup cost for PathTarget eval + *----- + */ + startup_cost = ofpinfo->rel_startup_cost; + startup_cost += aggcosts.transCost.startup; + startup_cost += aggcosts.transCost.per_tuple * input_rows; + startup_cost += (cpu_operator_cost * numGroupCols) * input_rows; + startup_cost += ptarget->cost.startup; + + /*----- + * Run time cost includes: + * 1. Run time cost of underneath input relation + * 2. Run time cost of performing aggregation, per cost_agg() + * 3. PathTarget eval cost for each output row + *----- + */ + run_cost = ofpinfo->rel_total_cost - ofpinfo->rel_startup_cost; + run_cost += aggcosts.finalCost * numGroups; + run_cost += cpu_tuple_cost * numGroups; + run_cost += ptarget->cost.per_tuple * numGroups; + } + else + { + /* Clamp retrieved rows estimates to at most foreignrel->tuples. */ + retrieved_rows = Min(retrieved_rows, foreignrel->tuples); + + /* + * Cost as though this were a seqscan, which is pessimistic. We + * effectively imagine the local_conds are being evaluated + * remotely, too. + */ + startup_cost = 0; + run_cost = 0; + run_cost += seq_page_cost * foreignrel->pages; + + startup_cost += foreignrel->baserestrictcost.startup; + cpu_per_tuple = cpu_tuple_cost + foreignrel->baserestrictcost.per_tuple; + run_cost += cpu_per_tuple * foreignrel->tuples; + } /* * Without remote estimates, we have no real way to estimate the cost @@ -3321,7 +3423,7 @@ prepare_query_params(PlanState *node, * benefit, and it'd require postgres_fdw to know more than is desirable * about Param evaluation.) */ - *param_exprs = (List *) ExecInitExpr((Expr *) fdw_exprs, node); + *param_exprs = ExecInitExprList(fdw_exprs, node); /* Allocate buffer for text form of query parameters. */ *param_values = (const char **) palloc0(numParams * sizeof(char *)); @@ -3350,7 +3452,7 @@ process_query_params(ExprContext *econtext, bool isNull; /* Evaluate the parameter expression */ - expr_value = ExecEvalExpr(expr_state, econtext, &isNull, NULL); + expr_value = ExecEvalExpr(expr_state, econtext, &isNull); /* * Get string representation of each parameter value by invoking @@ -3479,9 +3581,7 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel, astate.anl_cxt = CurrentMemoryContext; astate.temp_cxt = AllocSetContextCreate(CurrentMemoryContext, "postgres_fdw temporary data", - ALLOCSET_SMALL_MINSIZE, - ALLOCSET_SMALL_INITSIZE, - ALLOCSET_SMALL_MAXSIZE); + ALLOCSET_SMALL_SIZES); /* * Get the connection to use. We do the remote access as the table's @@ -3751,6 +3851,10 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid) * should save a few cycles to not process excluded tables in the * first place.) * + * Ignore table data for partitions and only include the definitions + * of the root partitioned tables to allow access to the complete + * remote data set locally in the schema imported. + * * Note: because we run the connection with search_path restricted to * pg_catalog, the format_type() and pg_get_expr() outputs will always * include a schema name for types/functions in other schemas, which @@ -3795,10 +3899,19 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid) " adrelid = c.oid AND adnum = attnum "); appendStringInfoString(&buf, - "WHERE c.relkind IN ('r', 'v', 'f', 'm') " + "WHERE c.relkind IN (" + CppAsString2(RELKIND_RELATION) "," + CppAsString2(RELKIND_VIEW) "," + CppAsString2(RELKIND_FOREIGN_TABLE) "," + CppAsString2(RELKIND_MATVIEW) "," + CppAsString2(RELKIND_PARTITIONED_TABLE) ") " " AND n.nspname = "); deparseStringLiteral(&buf, stmt->remote_schema); + /* Partitions are supported since Postgres 10 */ + if (PQserverVersion(conn) >= 100000) + appendStringInfoString(&buf, " AND NOT c.relispartition "); + /* Apply restrictions for LIMIT TO and EXCEPT */ if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO || stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT) @@ -3954,7 +4067,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, PgFdwRelationInfo *fpinfo_i; ListCell *lc; List *joinclauses; - List *otherclauses; /* * We support pushing down INNER, LEFT, RIGHT and FULL OUTER joins. @@ -3984,29 +4096,50 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, if (fpinfo_o->local_conds || fpinfo_i->local_conds) return false; - /* Separate restrict list into join quals and quals on join relation */ - if (IS_OUTER_JOIN(jointype)) - extract_actual_join_clauses(extra->restrictlist, &joinclauses, &otherclauses); - else - { - /* - * Unlike an outer join, for inner join, the join result contains only - * the rows which satisfy join clauses, similar to the other clause. - * Hence all clauses can be treated as other quals. This helps to push - * a join down to the foreign server even if some of its join quals - * are not safe to pushdown. - */ - otherclauses = extract_actual_clauses(extra->restrictlist, false); - joinclauses = NIL; - } + /* + * Merge FDW options. We might be tempted to do this after we have deemed + * the foreign join to be OK. But we must do this beforehand so that we + * know which quals can be evaluated on the foreign server, which might + * depend on shippable_extensions. + */ + fpinfo->server = fpinfo_o->server; + merge_fdw_options(fpinfo, fpinfo_o, fpinfo_i); - /* Join quals must be safe to push down. */ - foreach(lc, joinclauses) + /* + * Separate restrict list into join quals and pushed-down (other) quals. + * + * Join quals belonging to an outer join must all be shippable, else we + * cannot execute the join remotely. Add such quals to 'joinclauses'. + * + * Add other quals to fpinfo->remote_conds if they are shippable, else to + * fpinfo->local_conds. In an inner join it's okay to execute conditions + * either locally or remotely; the same is true for pushed-down conditions + * at an outer join. + * + * Note we might return failure after having already scribbled on + * fpinfo->remote_conds and fpinfo->local_conds. That's okay because we + * won't consult those lists again if we deem the join unshippable. + */ + joinclauses = NIL; + foreach(lc, extra->restrictlist) { - Expr *expr = (Expr *) lfirst(lc); + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + bool is_remote_clause = is_foreign_expr(root, joinrel, + rinfo->clause); - if (!is_foreign_expr(root, joinrel, expr)) - return false; + if (IS_OUTER_JOIN(jointype) && !rinfo->is_pushed_down) + { + if (!is_remote_clause) + return false; + joinclauses = lappend(joinclauses, rinfo); + } + else + { + if (is_remote_clause) + fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo); + else + fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo); + } } /* @@ -4032,45 +4165,38 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, /* Save the join clauses, for later use. */ fpinfo->joinclauses = joinclauses; - /* - * Other clauses are applied after the join has been performed and thus - * need not be all pushable. We will push those which can be pushed to - * reduce the number of rows fetched from the foreign server. Rest of them - * will be applied locally after fetching join result. Add them to fpinfo - * so that other joins involving this joinrel will know that this joinrel - * has local clauses. - */ - foreach(lc, otherclauses) - { - Expr *expr = (Expr *) lfirst(lc); - - if (!is_foreign_expr(root, joinrel, expr)) - fpinfo->local_conds = lappend(fpinfo->local_conds, expr); - else - fpinfo->remote_conds = lappend(fpinfo->remote_conds, expr); - } - fpinfo->outerrel = outerrel; fpinfo->innerrel = innerrel; fpinfo->jointype = jointype; /* + * By default, both the input relations are not required to be deparsed as + * subqueries, but there might be some relations covered by the input + * relations that are required to be deparsed as subqueries, so save the + * relids of those relations for later use by the deparser. + */ + fpinfo->make_outerrel_subquery = false; + fpinfo->make_innerrel_subquery = false; + Assert(bms_is_subset(fpinfo_o->lower_subquery_rels, outerrel->relids)); + Assert(bms_is_subset(fpinfo_i->lower_subquery_rels, innerrel->relids)); + fpinfo->lower_subquery_rels = bms_union(fpinfo_o->lower_subquery_rels, + fpinfo_i->lower_subquery_rels); + + /* * Pull the other remote conditions from the joining relations into join * clauses or other remote clauses (remote_conds) of this relation - * wherever possible. This avoids building subqueries at every join step, - * which is not currently supported by the deparser logic. + * wherever possible. This avoids building subqueries at every join step. * * For an inner join, clauses from both the relations are added to the * other remote clauses. For LEFT and RIGHT OUTER join, the clauses from * the outer side are added to remote_conds since those can be evaluated * after the join is evaluated. The clauses from inner side are added to - * the joinclauses, since they need to evaluated while constructing the + * the joinclauses, since they need to be evaluated while constructing the * join. * * For a FULL OUTER JOIN, the other clauses from either relation can not * be added to the joinclauses or remote_conds, since each relation acts - * as an outer relation for the other. Consider such full outer join as - * unshippable because of the reasons mentioned above in this comment. + * as an outer relation for the other. * * The joining sides can not have local conditions, thus no need to test * shippability of the clauses being pulled up. @@ -4099,8 +4225,29 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, break; case JOIN_FULL: - if (fpinfo_i->remote_conds || fpinfo_o->remote_conds) - return false; + + /* + * In this case, if any of the input relations has conditions, we + * need to deparse that relation as a subquery so that the + * conditions can be evaluated before the join. Remember it in + * the fpinfo of this relation so that the deparser can take + * appropriate action. Also, save the relids of base relations + * covered by that relation for later use by the deparser. + */ + if (fpinfo_o->remote_conds) + { + fpinfo->make_outerrel_subquery = true; + fpinfo->lower_subquery_rels = + bms_add_members(fpinfo->lower_subquery_rels, + outerrel->relids); + } + if (fpinfo_i->remote_conds) + { + fpinfo->make_innerrel_subquery = true; + fpinfo->lower_subquery_rels = + bms_add_members(fpinfo->lower_subquery_rels, + innerrel->relids); + } break; default: @@ -4123,15 +4270,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, /* Mark that this join can be pushed down safely */ fpinfo->pushdown_safe = true; - /* - * If user is willing to estimate cost for a scan of either of the joining - * relations using EXPLAIN, he intends to estimate scans on that relation - * more accurately. Then, it makes sense to estimate the cost the join - * with that relation more accurately using EXPLAIN. - */ - fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate || - fpinfo_i->use_remote_estimate; - /* Get user mapping */ if (fpinfo->use_remote_estimate) { @@ -4143,17 +4281,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, else fpinfo->user = NULL; - /* Get foreign server */ - fpinfo->server = fpinfo_o->server; - - /* - * Since both the joining relations come from the same server, the server - * level options should have same value for both the relations. Pick from - * any side. - */ - fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost; - fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost; - /* * Set cached relation costs to some negative value, so that we can detect * when they are set to some sensible costs, during one (usually the @@ -4163,15 +4290,6 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, fpinfo->rel_total_cost = -1; /* - * Set fetch size to maximum of the joining sides, since we are expecting - * the rows returned by the join to be proportional to the relation sizes. - */ - if (fpinfo_o->fetch_size > fpinfo_i->fetch_size) - fpinfo->fetch_size = fpinfo_o->fetch_size; - else - fpinfo->fetch_size = fpinfo_i->fetch_size; - - /* * Set the string describing this join relation to be used in EXPLAIN * output of corresponding ForeignScan. */ @@ -4181,6 +4299,16 @@ foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, get_jointype_name(fpinfo->jointype), fpinfo_i->relation_name->data); + /* + * Set the relation index. This is defined as the position of this + * joinrel in the join_rel_list list plus the length of the rtable list. + * Note that since this joinrel is at the end of the join_rel_list list + * when we are called, we can get the position by list_length. + */ + Assert(fpinfo->relation_index == 0); /* shouldn't be set yet */ + fpinfo->relation_index = + list_length(root->parse->rtable) + list_length(root->join_rel_list); + return true; } @@ -4219,6 +4347,110 @@ add_paths_with_pathkeys_for_rel(PlannerInfo *root, RelOptInfo *rel, } /* + * Parse options from foreign server and apply them to fpinfo. + * + * New options might also require tweaking merge_fdw_options(). + */ +static void +apply_server_options(PgFdwRelationInfo *fpinfo) +{ + ListCell *lc; + + foreach(lc, fpinfo->server->options) + { + DefElem *def = (DefElem *) lfirst(lc); + + if (strcmp(def->defname, "use_remote_estimate") == 0) + fpinfo->use_remote_estimate = defGetBoolean(def); + else if (strcmp(def->defname, "fdw_startup_cost") == 0) + fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL); + else if (strcmp(def->defname, "fdw_tuple_cost") == 0) + fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL); + else if (strcmp(def->defname, "extensions") == 0) + fpinfo->shippable_extensions = + ExtractExtensionList(defGetString(def), false); + else if (strcmp(def->defname, "fetch_size") == 0) + fpinfo->fetch_size = strtol(defGetString(def), NULL, 10); + } +} + +/* + * Parse options from foreign table and apply them to fpinfo. + * + * New options might also require tweaking merge_fdw_options(). + */ +static void +apply_table_options(PgFdwRelationInfo *fpinfo) +{ + ListCell *lc; + + foreach(lc, fpinfo->table->options) + { + DefElem *def = (DefElem *) lfirst(lc); + + if (strcmp(def->defname, "use_remote_estimate") == 0) + fpinfo->use_remote_estimate = defGetBoolean(def); + else if (strcmp(def->defname, "fetch_size") == 0) + fpinfo->fetch_size = strtol(defGetString(def), NULL, 10); + } +} + +/* + * Merge FDW options from input relations into a new set of options for a join + * or an upper rel. + * + * For a join relation, FDW-specific information about the inner and outer + * relations is provided using fpinfo_i and fpinfo_o. For an upper relation, + * fpinfo_o provides the information for the input relation; fpinfo_i is + * expected to NULL. + */ +static void +merge_fdw_options(PgFdwRelationInfo *fpinfo, + const PgFdwRelationInfo *fpinfo_o, + const PgFdwRelationInfo *fpinfo_i) +{ + /* We must always have fpinfo_o. */ + Assert(fpinfo_o); + + /* fpinfo_i may be NULL, but if present the servers must both match. */ + Assert(!fpinfo_i || + fpinfo_i->server->serverid == fpinfo_o->server->serverid); + + /* + * Copy the server specific FDW options. (For a join, both relations come + * from the same server, so the server options should have the same value + * for both relations.) + */ + fpinfo->fdw_startup_cost = fpinfo_o->fdw_startup_cost; + fpinfo->fdw_tuple_cost = fpinfo_o->fdw_tuple_cost; + fpinfo->shippable_extensions = fpinfo_o->shippable_extensions; + fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate; + fpinfo->fetch_size = fpinfo_o->fetch_size; + + /* Merge the table level options from either side of the join. */ + if (fpinfo_i) + { + /* + * We'll prefer to use remote estimates for this join if any table + * from either side of the join is using remote estimates. This is + * most likely going to be preferred since they're already willing to + * pay the price of a round trip to get the remote EXPLAIN. In any + * case it's not entirely clear how we might otherwise handle this + * best. + */ + fpinfo->use_remote_estimate = fpinfo_o->use_remote_estimate || + fpinfo_i->use_remote_estimate; + + /* + * Set fetch size to maximum of the joining sides, since we are + * expecting the rows returned by the join to be proportional to the + * relation sizes. + */ + fpinfo->fetch_size = Max(fpinfo_o->fetch_size, fpinfo_i->fetch_size); + } +} + +/* * postgresGetForeignJoinPaths * Add possible ForeignPath to joinrel, if join is safe to push down. */ @@ -4339,7 +4571,7 @@ postgresGetForeignJoinPaths(PlannerInfo *root, NIL, /* no pathkeys */ NULL, /* no required_outer */ epq_path, - NULL); /* no fdw_private */ + NIL); /* no fdw_private */ /* Add generated path into joinrel by add_path(). */ add_path(joinrel, (Path *) joinpath); @@ -4351,6 +4583,325 @@ postgresGetForeignJoinPaths(PlannerInfo *root, } /* + * Assess whether the aggregation, grouping and having operations can be pushed + * down to the foreign server. As a side effect, save information we obtain in + * this function to PgFdwRelationInfo of the input relation. + */ +static bool +foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) +{ + Query *query = root->parse; + PathTarget *grouping_target; + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) grouped_rel->fdw_private; + PgFdwRelationInfo *ofpinfo; + List *aggvars; + ListCell *lc; + int i; + List *tlist = NIL; + + /* Grouping Sets are not pushable */ + if (query->groupingSets) + return false; + + /* Get the fpinfo of the underlying scan relation. */ + ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private; + + /* + * If underneath input relation has any local conditions, those conditions + * are required to be applied before performing aggregation. Hence the + * aggregate cannot be pushed down. + */ + if (ofpinfo->local_conds) + return false; + + /* + * The targetlist expected from this node and the targetlist pushed down + * to the foreign server may be different. The latter requires + * sortgrouprefs to be set to push down GROUP BY clause, but should not + * have those arising from ORDER BY clause. These sortgrouprefs may be + * different from those in the plan's targetlist. Use a copy of path + * target to record the new sortgrouprefs. + */ + grouping_target = copy_pathtarget(root->upper_targets[UPPERREL_GROUP_AGG]); + + /* + * Evaluate grouping targets and check whether they are safe to push down + * to the foreign side. All GROUP BY expressions will be part of the + * grouping target and thus there is no need to evaluate it separately. + * While doing so, add required expressions into target list which can + * then be used to pass to foreign server. + */ + i = 0; + foreach(lc, grouping_target->exprs) + { + Expr *expr = (Expr *) lfirst(lc); + Index sgref = get_pathtarget_sortgroupref(grouping_target, i); + ListCell *l; + + /* Check whether this expression is part of GROUP BY clause */ + if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause)) + { + /* + * If any of the GROUP BY expression is not shippable we can not + * push down aggregation to the foreign server. + */ + if (!is_foreign_expr(root, grouped_rel, expr)) + return false; + + /* Pushable, add to tlist */ + tlist = add_to_flat_tlist(tlist, list_make1(expr)); + } + else + { + /* Check entire expression whether it is pushable or not */ + if (is_foreign_expr(root, grouped_rel, expr)) + { + /* Pushable, add to tlist */ + tlist = add_to_flat_tlist(tlist, list_make1(expr)); + } + else + { + /* + * If we have sortgroupref set, then it means that we have an + * ORDER BY entry pointing to this expression. Since we are + * not pushing ORDER BY with GROUP BY, clear it. + */ + if (sgref) + grouping_target->sortgrouprefs[i] = 0; + + /* Not matched exactly, pull the var with aggregates then */ + aggvars = pull_var_clause((Node *) expr, + PVC_INCLUDE_AGGREGATES); + + if (!is_foreign_expr(root, grouped_rel, (Expr *) aggvars)) + return false; + + /* + * Add aggregates, if any, into the targetlist. Plain var + * nodes should be either same as some GROUP BY expression or + * part of some GROUP BY expression. In later case, the query + * cannot refer plain var nodes without the surrounding + * expression. In both the cases, they are already part of + * the targetlist and thus no need to add them again. In fact + * adding pulled plain var nodes in SELECT clause will cause + * an error on the foreign server if they are not same as some + * GROUP BY expression. + */ + foreach(l, aggvars) + { + Expr *expr = (Expr *) lfirst(l); + + if (IsA(expr, Aggref)) + tlist = add_to_flat_tlist(tlist, list_make1(expr)); + } + } + } + + i++; + } + + /* + * Classify the pushable and non-pushable having clauses and save them in + * remote_conds and local_conds of the grouped rel's fpinfo. + */ + if (root->hasHavingQual && query->havingQual) + { + ListCell *lc; + + foreach(lc, (List *) query->havingQual) + { + Expr *expr = (Expr *) lfirst(lc); + RestrictInfo *rinfo; + + /* + * Currently, the core code doesn't wrap havingQuals in + * RestrictInfos, so we must make our own. + */ + Assert(!IsA(expr, RestrictInfo)); + rinfo = make_restrictinfo(expr, + true, + false, + false, + root->qual_security_level, + grouped_rel->relids, + NULL, + NULL); + if (is_foreign_expr(root, grouped_rel, expr)) + fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo); + else + fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo); + } + } + + /* + * If there are any local conditions, pull Vars and aggregates from it and + * check whether they are safe to pushdown or not. + */ + if (fpinfo->local_conds) + { + List *aggvars = NIL; + ListCell *lc; + + foreach(lc, fpinfo->local_conds) + { + RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc); + + aggvars = list_concat(aggvars, + pull_var_clause((Node *) rinfo->clause, + PVC_INCLUDE_AGGREGATES)); + } + + foreach(lc, aggvars) + { + Expr *expr = (Expr *) lfirst(lc); + + /* + * If aggregates within local conditions are not safe to push + * down, then we cannot push down the query. Vars are already + * part of GROUP BY clause which are checked above, so no need to + * access them again here. + */ + if (IsA(expr, Aggref)) + { + if (!is_foreign_expr(root, grouped_rel, expr)) + return false; + + tlist = add_to_flat_tlist(tlist, list_make1(expr)); + } + } + } + + /* Transfer any sortgroupref data to the replacement tlist */ + apply_pathtarget_labeling_to_tlist(tlist, grouping_target); + + /* Store generated targetlist */ + fpinfo->grouped_tlist = tlist; + + /* Safe to pushdown */ + fpinfo->pushdown_safe = true; + + /* + * Set cached relation costs to some negative value, so that we can detect + * when they are set to some sensible costs, during one (usually the + * first) of the calls to estimate_path_cost_size(). + */ + fpinfo->rel_startup_cost = -1; + fpinfo->rel_total_cost = -1; + + /* + * Set the string describing this grouped relation to be used in EXPLAIN + * output of corresponding ForeignScan. + */ + fpinfo->relation_name = makeStringInfo(); + appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)", + ofpinfo->relation_name->data); + + return true; +} + +/* + * postgresGetForeignUpperPaths + * Add paths for post-join operations like aggregation, grouping etc. if + * corresponding operations are safe to push down. + * + * Right now, we only support aggregate, grouping and having clause pushdown. + */ +static void +postgresGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, + RelOptInfo *input_rel, RelOptInfo *output_rel) +{ + PgFdwRelationInfo *fpinfo; + + /* + * If input rel is not safe to pushdown, then simply return as we cannot + * perform any post-join operations on the foreign server. + */ + if (!input_rel->fdw_private || + !((PgFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe) + return; + + /* Ignore stages we don't support; and skip any duplicate calls. */ + if (stage != UPPERREL_GROUP_AGG || output_rel->fdw_private) + return; + + fpinfo = (PgFdwRelationInfo *) palloc0(sizeof(PgFdwRelationInfo)); + fpinfo->pushdown_safe = false; + output_rel->fdw_private = fpinfo; + + add_foreign_grouping_paths(root, input_rel, output_rel); +} + +/* + * add_foreign_grouping_paths + * Add foreign path for grouping and/or aggregation. + * + * Given input_rel represents the underlying scan. The paths are added to the + * given grouped_rel. + */ +static void +add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, + RelOptInfo *grouped_rel) +{ + Query *parse = root->parse; + PgFdwRelationInfo *ifpinfo = input_rel->fdw_private; + PgFdwRelationInfo *fpinfo = grouped_rel->fdw_private; + ForeignPath *grouppath; + PathTarget *grouping_target; + double rows; + int width; + Cost startup_cost; + Cost total_cost; + + /* Nothing to be done, if there is no grouping or aggregation required. */ + if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs && + !root->hasHavingQual) + return; + + grouping_target = root->upper_targets[UPPERREL_GROUP_AGG]; + + /* save the input_rel as outerrel in fpinfo */ + fpinfo->outerrel = input_rel; + + /* + * Copy foreign table, foreign server, user mapping, FDW options etc. + * details from the input relation's fpinfo. + */ + fpinfo->table = ifpinfo->table; + fpinfo->server = ifpinfo->server; + fpinfo->user = ifpinfo->user; + merge_fdw_options(fpinfo, ifpinfo, NULL); + + /* Assess if it is safe to push down aggregation and grouping. */ + if (!foreign_grouping_ok(root, grouped_rel)) + return; + + /* Estimate the cost of push down */ + estimate_path_cost_size(root, grouped_rel, NIL, NIL, &rows, + &width, &startup_cost, &total_cost); + + /* Now update this information in the fpinfo */ + fpinfo->rows = rows; + fpinfo->width = width; + fpinfo->startup_cost = startup_cost; + fpinfo->total_cost = total_cost; + + /* Create and add foreign path to the grouping relation. */ + grouppath = create_foreignscan_path(root, + grouped_rel, + grouping_target, + rows, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + NULL, /* no required_outer */ + NULL, + NIL); /* no fdw_private */ + + /* Add generated path into grouped_rel by add_path(). */ + add_path(grouped_rel, (Path *) grouppath); +} + +/* * Create a tuple from the specified row of the PGresult. * * rel is the local representation of the foreign table, attinmeta is @@ -4372,6 +4923,7 @@ make_tuple_from_result_row(PGresult *res, Datum *values; bool *nulls; ItemPointer ctid = NULL; + Oid oid = InvalidOid; ConversionLocation errpos; ErrorContextCallback errcallback; MemoryContext oldcontext; @@ -4429,7 +4981,11 @@ make_tuple_from_result_row(PGresult *res, else valstr = PQgetvalue(res, row, j); - /* convert value to internal representation */ + /* + * convert value to internal representation + * + * Note: we ignore system columns other than ctid and oid in result + */ errpos.cur_attno = i; if (i > 0) { @@ -4444,7 +5000,7 @@ make_tuple_from_result_row(PGresult *res, } else if (i == SelfItemPointerAttributeNumber) { - /* ctid --- note we ignore any other system column in result */ + /* ctid */ if (valstr != NULL) { Datum datum; @@ -4453,6 +5009,17 @@ make_tuple_from_result_row(PGresult *res, ctid = (ItemPointer) DatumGetPointer(datum); } } + else if (i == ObjectIdAttributeNumber) + { + /* oid */ + if (valstr != NULL) + { + Datum datum; + + datum = DirectFunctionCall1(oidin, CStringGetDatum(valstr)); + oid = DatumGetObjectId(datum); + } + } errpos.cur_attno = 0; j++; @@ -4496,6 +5063,12 @@ make_tuple_from_result_row(PGresult *res, HeapTupleHeaderSetXmin(tuple->t_data, InvalidTransactionId); HeapTupleHeaderSetCmin(tuple->t_data, InvalidTransactionId); + /* + * If we have an OID to return, install it. + */ + if (OidIsValid(oid)) + HeapTupleSetOid(tuple, oid); + /* Clean up */ MemoryContextReset(temp_context); @@ -4523,6 +5096,8 @@ conversion_error_callback(void *arg) attname = NameStr(tupdesc->attrs[errpos->cur_attno - 1]->attname); else if (errpos->cur_attno == SelfItemPointerAttributeNumber) attname = "ctid"; + else if (errpos->cur_attno == ObjectIdAttributeNumber) + attname = "oid"; relname = RelationGetRelationName(errpos->rel); } @@ -4530,27 +5105,35 @@ conversion_error_callback(void *arg) { /* error occurred in a scan against a foreign join */ ForeignScanState *fsstate = errpos->fsstate; - ForeignScan *fsplan = (ForeignScan *) fsstate->ss.ps.plan; + ForeignScan *fsplan = castNode(ForeignScan, fsstate->ss.ps.plan); EState *estate = fsstate->ss.ps.state; TargetEntry *tle; - Var *var; - RangeTblEntry *rte; - Assert(IsA(fsplan, ForeignScan)); - tle = (TargetEntry *) list_nth(fsplan->fdw_scan_tlist, - errpos->cur_attno - 1); - Assert(IsA(tle, TargetEntry)); - var = (Var *) tle->expr; - Assert(IsA(var, Var)); + tle = list_nth_node(TargetEntry, fsplan->fdw_scan_tlist, + errpos->cur_attno - 1); - rte = rt_fetch(var->varno, estate->es_range_table); + /* + * Target list can have Vars and expressions. For Vars, we can get + * it's relation, however for expressions we can't. Thus for + * expressions, just show generic context message. + */ + if (IsA(tle->expr, Var)) + { + RangeTblEntry *rte; + Var *var = (Var *) tle->expr; - if (var->varattno == 0) - is_wholerow = true; - else - attname = get_relid_attribute_name(rte->relid, var->varattno); + rte = rt_fetch(var->varno, estate->es_range_table); + + if (var->varattno == 0) + is_wholerow = true; + else + attname = get_relid_attribute_name(rte->relid, var->varattno); - relname = get_rel_name(rte->relid); + relname = get_rel_name(rte->relid); + } + else + errcontext("processing expression at position %d in select list", + errpos->cur_attno); } if (relname) diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 67126bc421..25c950dd76 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -3,7 +3,7 @@ * postgres_fdw.h * Foreign-data wrapper for remote PostgreSQL servers * - * Portions Copyright (c) 2012-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2012-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/postgres_fdw/postgres_fdw.h @@ -22,7 +22,10 @@ /* * FDW-specific planner information kept in RelOptInfo.fdw_private for a - * foreign table. This information is collected by postgresGetForeignRelSize. + * postgres_fdw foreign table. For a baserel, this struct is created by + * postgresGetForeignRelSize, although some fields are not filled till later. + * postgresGetForeignJoinPaths creates it for a joinrel, and + * postgresGetForeignUpperPaths creates it for an upperrel. */ typedef struct PgFdwRelationInfo { @@ -34,20 +37,15 @@ typedef struct PgFdwRelationInfo /* * Restriction clauses, divided into safe and unsafe to pushdown subsets. - * - * For a base foreign relation this is a list of clauses along-with - * RestrictInfo wrapper. Keeping RestrictInfo wrapper helps while dividing - * scan_clauses in postgresGetForeignPlan into safe and unsafe subsets. - * Also it helps in estimating costs since RestrictInfo caches the - * selectivity and qual cost for the clause in it. - * - * For a join relation, however, they are part of otherclause list - * obtained from extract_actual_join_clauses, which strips RestrictInfo - * construct. So, for a join relation they are list of bare clauses. + * All entries in these lists should have RestrictInfo wrappers; that + * improves efficiency of selectivity and cost estimation. */ List *remote_conds; List *local_conds; + /* Actual remote restriction clauses for scan (sans RestrictInfos) */ + List *final_remote_exprs; + /* Bitmap of attr numbers we need to fetch from the remote server. */ Bitmapset *attrs_used; @@ -91,7 +89,25 @@ typedef struct PgFdwRelationInfo RelOptInfo *outerrel; RelOptInfo *innerrel; JoinType jointype; - List *joinclauses; + /* joinclauses contains only JOIN/ON conditions for an outer join */ + List *joinclauses; /* List of RestrictInfo */ + + /* Grouping information */ + List *grouped_tlist; + + /* Subquery information */ + bool make_outerrel_subquery; /* do we deparse outerrel as a + * subquery? */ + bool make_innerrel_subquery; /* do we deparse innerrel as a + * subquery? */ + Relids lower_subquery_rels; /* all relids appearing in lower + * subqueries */ + + /* + * Index of the relation. It is used to create an alias to a subquery + * representing the relation. + */ + int relation_index; } PgFdwRelationInfo; /* in postgres_fdw.c */ @@ -155,10 +171,10 @@ extern void deparseAnalyzeSql(StringInfo buf, Relation rel, List **retrieved_attrs); extern void deparseStringLiteral(StringInfo buf, const char *val); extern Expr *find_em_expr_for_rel(EquivalenceClass *ec, RelOptInfo *rel); -extern List *build_tlist_to_deparse(RelOptInfo *foreign_rel); +extern List *build_tlist_to_deparse(RelOptInfo *foreignrel); extern void deparseSelectStmtForRel(StringInfo buf, PlannerInfo *root, RelOptInfo *foreignrel, List *tlist, - List *remote_conds, List *pathkeys, + List *remote_conds, List *pathkeys, bool is_subquery, List **retrieved_attrs, List **params_list); /* in shippable.c */ diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c index 92c952589e..2ac0873caa 100644 --- a/contrib/postgres_fdw/shippable.c +++ b/contrib/postgres_fdw/shippable.c @@ -13,7 +13,7 @@ * functions or functions using nonportable collations. Those considerations * need not be accounted for here. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/postgres_fdw/shippable.c diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 6f684a1b0c..509bb547c6 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -136,6 +136,14 @@ CREATE FOREIGN TABLE ft6 ( c3 text ) SERVER loopback2 OPTIONS (schema_name 'S 1', table_name 'T 4'); +-- A table with oids. CREATE FOREIGN TABLE doesn't support the +-- WITH OIDS option, but ALTER does. +CREATE FOREIGN TABLE ft_pg_type ( + typname name, + typlen smallint +) SERVER loopback OPTIONS (schema_name 'pg_catalog', table_name 'pg_type'); +ALTER TABLE ft_pg_type SET WITH OIDS; + -- =================================================================== -- tests for validator -- =================================================================== @@ -383,9 +391,26 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1 OFFSET 45 LIMIT 10; -- full outer join with restrictions on the joining relations +-- a. the joining relations are both base relations EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; SELECT t1.c1, t2.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (t1.c1 = t2.c1) ORDER BY t1.c1, t2.c1; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT 1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (TRUE) OFFSET 10 LIMIT 10; +SELECT 1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t2 ON (TRUE) OFFSET 10 LIMIT 10; +-- b. one of the joining relations is a base relation and the other is a join +-- relation +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM ft4 t2 LEFT JOIN ft5 t3 ON (t2.c1 = t3.c1) WHERE (t2.c1 between 50 and 60)) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM ft4 t2 LEFT JOIN ft5 t3 ON (t2.c1 = t3.c1) WHERE (t2.c1 between 50 and 60)) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; +-- c. test deparsing the remote query as nested subqueries +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t1 FULL JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (t1.c1 = ss.a) ORDER BY t1.c1, ss.a, ss.b; +-- d. test deparsing rowmarked relations as subqueries +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM "S 1"."T 3" WHERE c1 = 50) t1 INNER JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (TRUE) ORDER BY t1.c1, ss.a, ss.b FOR UPDATE OF t1; +SELECT t1.c1, ss.a, ss.b FROM (SELECT c1 FROM "S 1"."T 3" WHERE c1 = 50) t1 INNER JOIN (SELECT t2.c1, t3.c1 FROM (SELECT c1 FROM ft4 WHERE c1 between 50 and 60) t2 FULL JOIN (SELECT c1 FROM ft5 WHERE c1 between 50 and 60) t3 ON (t2.c1 = t3.c1) WHERE t2.c1 IS NULL OR t2.c1 IS NOT NULL) ss(a, b) ON (TRUE) ORDER BY t1.c1, ss.a, ss.b FOR UPDATE OF t1; -- full outer join + inner join EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1, t3.c1 FROM ft4 t1 INNER JOIN ft5 t2 ON (t1.c1 = t2.c1 + 1 and t1.c1 between 50 and 60) FULL JOIN ft4 t3 ON (t2.c1 = t3.c1) ORDER BY t1.c1, t2.c1, t3.c1 LIMIT 10; @@ -422,6 +447,14 @@ SELECT t1.c1, t2.c2, t3.c3 FROM ft2 t1 LEFT JOIN ft2 t2 ON (t1.c1 = t2.c1) RIGHT EXPLAIN (VERBOSE, COSTS OFF) SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; SELECT t1.c1, t2.c1 FROM ft4 t1 FULL JOIN ft5 t2 ON (t1.c1 = t2.c1) WHERE (t1.c1 = t2.c1 OR t1.c1 IS NULL) ORDER BY t1.c1, t2.c1 OFFSET 10 LIMIT 10; +-- full outer join + WHERE clause with shippable extensions set +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, t2.c2, t1.c3 FROM ft1 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE postgres_fdw_abs(t1.c1) > 0 OFFSET 10 LIMIT 10; +ALTER SERVER loopback OPTIONS (DROP extensions); +-- full outer join + WHERE clause with shippable extensions not set +EXPLAIN (VERBOSE, COSTS OFF) +SELECT t1.c1, t2.c2, t1.c3 FROM ft1 t1 FULL JOIN ft2 t2 ON (t1.c1 = t2.c1) WHERE postgres_fdw_abs(t1.c1) > 0 OFFSET 10 LIMIT 10; +ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); -- join two tables with FOR UPDATE clause -- tests whole-row reference for row marks EXPLAIN (VERBOSE, COSTS OFF) @@ -485,7 +518,7 @@ EXPLAIN (VERBOSE, COSTS OFF) SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; SELECT t1."C 1" FROM "S 1"."T 1" t1, LATERAL (SELECT DISTINCT t2.c1, t3.c1 FROM ft1 t2, ft2 t3 WHERE t2.c1 = t3.c1 AND t2.c2 = t1.c2) q ORDER BY t1."C 1" OFFSET 10 LIMIT 10; --- non-Var items in targelist of the nullable rel of a join preventing +-- non-Var items in targetlist of the nullable rel of a join preventing -- push-down in some cases -- unable to push {ft1, ft2} EXPLAIN (VERBOSE, COSTS OFF) @@ -533,6 +566,315 @@ ALTER VIEW v4 OWNER TO regress_view_owner; DROP OWNED BY regress_view_owner; DROP ROLE regress_view_owner; + +-- =================================================================== +-- Aggregate and grouping queries +-- =================================================================== + +-- Simple aggregates +explain (verbose, costs off) +select count(c6), sum(c1), avg(c1), min(c2), max(c1), stddev(c2), sum(c1) * (random() <= 1)::int as sum2 from ft1 where c2 < 5 group by c2 order by 1, 2; +select count(c6), sum(c1), avg(c1), min(c2), max(c1), stddev(c2), sum(c1) * (random() <= 1)::int as sum2 from ft1 where c2 < 5 group by c2 order by 1, 2; + +-- Aggregate is not pushed down as aggregation contains random() +explain (verbose, costs off) +select sum(c1 * (random() <= 1)::int) as sum, avg(c1) from ft1; + +-- Aggregate over join query +explain (verbose, costs off) +select count(*), sum(t1.c1), avg(t2.c1) from ft1 t1 inner join ft1 t2 on (t1.c2 = t2.c2) where t1.c2 = 6; +select count(*), sum(t1.c1), avg(t2.c1) from ft1 t1 inner join ft1 t2 on (t1.c2 = t2.c2) where t1.c2 = 6; + +-- Not pushed down due to local conditions present in underneath input rel +explain (verbose, costs off) +select sum(t1.c1), count(t2.c1) from ft1 t1 inner join ft2 t2 on (t1.c1 = t2.c1) where ((t1.c1 * t2.c1)/(t1.c1 * t2.c1)) * random() <= 1; + +-- GROUP BY clause having expressions +explain (verbose, costs off) +select c2/2, sum(c2) * (c2/2) from ft1 group by c2/2 order by c2/2; +select c2/2, sum(c2) * (c2/2) from ft1 group by c2/2 order by c2/2; + +-- Aggregates in subquery are pushed down. +explain (verbose, costs off) +select count(x.a), sum(x.a) from (select c2 a, sum(c1) b from ft1 group by c2, sqrt(c1) order by 1, 2) x; +select count(x.a), sum(x.a) from (select c2 a, sum(c1) b from ft1 group by c2, sqrt(c1) order by 1, 2) x; + +-- Aggregate is still pushed down by taking unshippable expression out +explain (verbose, costs off) +select c2 * (random() <= 1)::int as sum1, sum(c1) * c2 as sum2 from ft1 group by c2 order by 1, 2; +select c2 * (random() <= 1)::int as sum1, sum(c1) * c2 as sum2 from ft1 group by c2 order by 1, 2; + +-- Aggregate with unshippable GROUP BY clause are not pushed +explain (verbose, costs off) +select c2 * (random() <= 1)::int as c2 from ft2 group by c2 * (random() <= 1)::int order by 1; + +-- GROUP BY clause in various forms, cardinal, alias and constant expression +explain (verbose, costs off) +select count(c2) w, c2 x, 5 y, 7.0 z from ft1 group by 2, y, 9.0::int order by 2; +select count(c2) w, c2 x, 5 y, 7.0 z from ft1 group by 2, y, 9.0::int order by 2; + +-- Testing HAVING clause shippability +explain (verbose, costs off) +select c2, sum(c1) from ft2 group by c2 having avg(c1) < 500 and sum(c1) < 49800 order by c2; +select c2, sum(c1) from ft2 group by c2 having avg(c1) < 500 and sum(c1) < 49800 order by c2; + +-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down +explain (verbose, costs off) +select count(*) from (select c5, count(c1) from ft1 group by c5, sqrt(c2) having (avg(c1) / avg(c1)) * random() <= 1 and avg(c1) < 500) x; +select count(*) from (select c5, count(c1) from ft1 group by c5, sqrt(c2) having (avg(c1) / avg(c1)) * random() <= 1 and avg(c1) < 500) x; + +-- Aggregate in HAVING clause is not pushable, and thus aggregation is not pushed down +explain (verbose, costs off) +select sum(c1) from ft1 group by c2 having avg(c1 * (random() <= 1)::int) > 100 order by 1; + + +-- Testing ORDER BY, DISTINCT, FILTER, Ordered-sets and VARIADIC within aggregates + +-- ORDER BY within aggregate, same column used to order +explain (verbose, costs off) +select array_agg(c1 order by c1) from ft1 where c1 < 100 group by c2 order by 1; +select array_agg(c1 order by c1) from ft1 where c1 < 100 group by c2 order by 1; + +-- ORDER BY within aggregate, different column used to order also using DESC +explain (verbose, costs off) +select array_agg(c5 order by c1 desc) from ft2 where c2 = 6 and c1 < 50; +select array_agg(c5 order by c1 desc) from ft2 where c2 = 6 and c1 < 50; + +-- DISTINCT within aggregate +explain (verbose, costs off) +select array_agg(distinct (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; +select array_agg(distinct (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + +-- DISTINCT combined with ORDER BY within aggregate +explain (verbose, costs off) +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + +explain (verbose, costs off) +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5 desc nulls last) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; +select array_agg(distinct (t1.c1)%5 order by (t1.c1)%5 desc nulls last) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) where t1.c1 < 20 or (t1.c1 is null and t2.c1 < 5) group by (t2.c1)%3 order by 1; + +-- FILTER within aggregate +explain (verbose, costs off) +select sum(c1) filter (where c1 < 100 and c2 > 5) from ft1 group by c2 order by 1 nulls last; +select sum(c1) filter (where c1 < 100 and c2 > 5) from ft1 group by c2 order by 1 nulls last; + +-- DISTINCT, ORDER BY and FILTER within aggregate +explain (verbose, costs off) +select sum(c1%3), sum(distinct c1%3 order by c1%3) filter (where c1%3 < 2), c2 from ft1 where c2 = 6 group by c2; +select sum(c1%3), sum(distinct c1%3 order by c1%3) filter (where c1%3 < 2), c2 from ft1 where c2 = 6 group by c2; + +-- Outer query is aggregation query +explain (verbose, costs off) +select distinct (select count(*) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; +select distinct (select count(*) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; +-- Inner query is aggregation query +explain (verbose, costs off) +select distinct (select count(t1.c1) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; +select distinct (select count(t1.c1) filter (where t2.c2 = 6 and t2.c1 < 10) from ft1 t1 where t1.c1 = 6) from ft2 t2 where t2.c2 % 6 = 0 order by 1; + +-- Aggregate not pushed down as FILTER condition is not pushable +explain (verbose, costs off) +select sum(c1) filter (where (c1 / c1) * random() <= 1) from ft1 group by c2 order by 1; +explain (verbose, costs off) +select sum(c2) filter (where c2 in (select c2 from ft1 where c2 < 5)) from ft1; + +-- Ordered-sets within aggregate +explain (verbose, costs off) +select c2, rank('10'::varchar) within group (order by c6), percentile_cont(c2/10::numeric) within group (order by c1) from ft1 where c2 < 10 group by c2 having percentile_cont(c2/10::numeric) within group (order by c1) < 500 order by c2; +select c2, rank('10'::varchar) within group (order by c6), percentile_cont(c2/10::numeric) within group (order by c1) from ft1 where c2 < 10 group by c2 having percentile_cont(c2/10::numeric) within group (order by c1) < 500 order by c2; + +-- Using multiple arguments within aggregates +explain (verbose, costs off) +select c1, rank(c1, c2) within group (order by c1, c2) from ft1 group by c1, c2 having c1 = 6 order by 1; +select c1, rank(c1, c2) within group (order by c1, c2) from ft1 group by c1, c2 having c1 = 6 order by 1; + +-- User defined function for user defined aggregate, VARIADIC +create function least_accum(anyelement, variadic anyarray) +returns anyelement language sql as + 'select least($1, min($2[i])) from generate_subscripts($2,1) g(i)'; +create aggregate least_agg(variadic items anyarray) ( + stype = anyelement, sfunc = least_accum +); + +-- Disable hash aggregation for plan stability. +set enable_hashagg to false; + +-- Not pushed down due to user defined aggregate +explain (verbose, costs off) +select c2, least_agg(c1) from ft1 group by c2 order by c2; + +-- Add function and aggregate into extension +alter extension postgres_fdw add function least_accum(anyelement, variadic anyarray); +alter extension postgres_fdw add aggregate least_agg(variadic items anyarray); +alter server loopback options (set extensions 'postgres_fdw'); + +-- Now aggregate will be pushed. Aggregate will display VARIADIC argument. +explain (verbose, costs off) +select c2, least_agg(c1) from ft1 where c2 < 100 group by c2 order by c2; +select c2, least_agg(c1) from ft1 where c2 < 100 group by c2 order by c2; + +-- Remove function and aggregate from extension +alter extension postgres_fdw drop function least_accum(anyelement, variadic anyarray); +alter extension postgres_fdw drop aggregate least_agg(variadic items anyarray); +alter server loopback options (set extensions 'postgres_fdw'); + +-- Not pushed down as we have dropped objects from extension. +explain (verbose, costs off) +select c2, least_agg(c1) from ft1 group by c2 order by c2; + +-- Cleanup +reset enable_hashagg; +drop aggregate least_agg(variadic items anyarray); +drop function least_accum(anyelement, variadic anyarray); + + +-- Testing USING OPERATOR() in ORDER BY within aggregate. +-- For this, we need user defined operators along with operator family and +-- operator class. Create those and then add them in extension. Note that +-- user defined objects are considered unshippable unless they are part of +-- the extension. +create operator public.<^ ( + leftarg = int4, + rightarg = int4, + procedure = int4eq +); + +create operator public.=^ ( + leftarg = int4, + rightarg = int4, + procedure = int4lt +); + +create operator public.>^ ( + leftarg = int4, + rightarg = int4, + procedure = int4gt +); + +create operator family my_op_family using btree; + +create function my_op_cmp(a int, b int) returns int as + $$begin return btint4cmp(a, b); end $$ language plpgsql; + +create operator class my_op_class for type int using btree family my_op_family as + operator 1 public.<^, + operator 3 public.=^, + operator 5 public.>^, + function 1 my_op_cmp(int, int); + +-- This will not be pushed as user defined sort operator is not part of the +-- extension yet. +explain (verbose, costs off) +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + +-- Add into extension +alter extension postgres_fdw add operator class my_op_class using btree; +alter extension postgres_fdw add function my_op_cmp(a int, b int); +alter extension postgres_fdw add operator family my_op_family using btree; +alter extension postgres_fdw add operator public.<^(int, int); +alter extension postgres_fdw add operator public.=^(int, int); +alter extension postgres_fdw add operator public.>^(int, int); +alter server loopback options (set extensions 'postgres_fdw'); + +-- Now this will be pushed as sort operator is part of the extension. +explain (verbose, costs off) +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + +-- Remove from extension +alter extension postgres_fdw drop operator class my_op_class using btree; +alter extension postgres_fdw drop function my_op_cmp(a int, b int); +alter extension postgres_fdw drop operator family my_op_family using btree; +alter extension postgres_fdw drop operator public.<^(int, int); +alter extension postgres_fdw drop operator public.=^(int, int); +alter extension postgres_fdw drop operator public.>^(int, int); +alter server loopback options (set extensions 'postgres_fdw'); + +-- This will not be pushed as sort operator is now removed from the extension. +explain (verbose, costs off) +select array_agg(c1 order by c1 using operator(public.<^)) from ft2 where c2 = 6 and c1 < 100 group by c2; + +-- Cleanup +drop operator class my_op_class using btree; +drop function my_op_cmp(a int, b int); +drop operator family my_op_family using btree; +drop operator public.>^(int, int); +drop operator public.=^(int, int); +drop operator public.<^(int, int); + +-- Input relation to aggregate push down hook is not safe to pushdown and thus +-- the aggregate cannot be pushed down to foreign server. +explain (verbose, costs off) +select count(t1.c3) from ft1 t1, ft1 t2 where t1.c1 = postgres_fdw_abs(t1.c2); + +-- Subquery in FROM clause having aggregate +explain (verbose, costs off) +select count(*), x.b from ft1, (select c2 a, sum(c1) b from ft1 group by c2) x where ft1.c2 = x.a group by x.b order by 1, 2; +select count(*), x.b from ft1, (select c2 a, sum(c1) b from ft1 group by c2) x where ft1.c2 = x.a group by x.b order by 1, 2; + +-- FULL join with IS NULL check in HAVING +explain (verbose, costs off) +select avg(t1.c1), sum(t2.c1) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) group by t2.c1 having (avg(t1.c1) is null and sum(t2.c1) < 10) or sum(t2.c1) is null order by 1 nulls last, 2; +select avg(t1.c1), sum(t2.c1) from ft4 t1 full join ft5 t2 on (t1.c1 = t2.c1) group by t2.c1 having (avg(t1.c1) is null and sum(t2.c1) < 10) or sum(t2.c1) is null order by 1 nulls last, 2; + +-- Aggregate over FULL join needing to deparse the joining relations as +-- subqueries. +explain (verbose, costs off) +select count(*), sum(t1.c1), avg(t2.c1) from (select c1 from ft4 where c1 between 50 and 60) t1 full join (select c1 from ft5 where c1 between 50 and 60) t2 on (t1.c1 = t2.c1); +select count(*), sum(t1.c1), avg(t2.c1) from (select c1 from ft4 where c1 between 50 and 60) t1 full join (select c1 from ft5 where c1 between 50 and 60) t2 on (t1.c1 = t2.c1); + +-- ORDER BY expression is part of the target list but not pushed down to +-- foreign server. +explain (verbose, costs off) +select sum(c2) * (random() <= 1)::int as sum from ft1 order by 1; +select sum(c2) * (random() <= 1)::int as sum from ft1 order by 1; + +-- LATERAL join, with parameterization +set enable_hashagg to false; +explain (verbose, costs off) +select c2, sum from "S 1"."T 1" t1, lateral (select sum(t2.c1 + t1."C 1") sum from ft2 t2 group by t2.c1) qry where t1.c2 * 2 = qry.sum and t1.c2 < 3 and t1."C 1" < 100 order by 1; +select c2, sum from "S 1"."T 1" t1, lateral (select sum(t2.c1 + t1."C 1") sum from ft2 t2 group by t2.c1) qry where t1.c2 * 2 = qry.sum and t1.c2 < 3 and t1."C 1" < 100 order by 1; +reset enable_hashagg; + +-- Check with placeHolderVars +explain (verbose, costs off) +select sum(q.a), count(q.b) from ft4 left join (select 13, avg(ft1.c1), sum(ft2.c1) from ft1 right join ft2 on (ft1.c1 = ft2.c1)) q(a, b, c) on (ft4.c1 <= q.b); +select sum(q.a), count(q.b) from ft4 left join (select 13, avg(ft1.c1), sum(ft2.c1) from ft1 right join ft2 on (ft1.c1 = ft2.c1)) q(a, b, c) on (ft4.c1 <= q.b); + + +-- Not supported cases +-- Grouping sets +explain (verbose, costs off) +select c2, sum(c1) from ft1 where c2 < 3 group by rollup(c2) order by 1 nulls last; +select c2, sum(c1) from ft1 where c2 < 3 group by rollup(c2) order by 1 nulls last; +explain (verbose, costs off) +select c2, sum(c1) from ft1 where c2 < 3 group by cube(c2) order by 1 nulls last; +select c2, sum(c1) from ft1 where c2 < 3 group by cube(c2) order by 1 nulls last; +explain (verbose, costs off) +select c2, c6, sum(c1) from ft1 where c2 < 3 group by grouping sets(c2, c6) order by 1 nulls last, 2 nulls last; +select c2, c6, sum(c1) from ft1 where c2 < 3 group by grouping sets(c2, c6) order by 1 nulls last, 2 nulls last; +explain (verbose, costs off) +select c2, sum(c1), grouping(c2) from ft1 where c2 < 3 group by c2 order by 1 nulls last; +select c2, sum(c1), grouping(c2) from ft1 where c2 < 3 group by c2 order by 1 nulls last; + +-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed +explain (verbose, costs off) +select distinct sum(c1)/1000 s from ft2 where c2 < 6 group by c2 order by 1; +select distinct sum(c1)/1000 s from ft2 where c2 < 6 group by c2 order by 1; + +-- WindowAgg +explain (verbose, costs off) +select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1; +select c2, sum(c2), count(c2) over (partition by c2%2) from ft2 where c2 < 10 group by c2 order by 1; +explain (verbose, costs off) +select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1; +select c2, array_agg(c2) over (partition by c2%2 order by c2 desc) from ft1 where c2 < 10 group by c2 order by 1; +explain (verbose, costs off) +select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1; +select c2, array_agg(c2) over (partition by c2%2 order by c2 range between current row and unbounded following) from ft1 where c2 < 10 group by c2 order by 1; + + -- =================================================================== -- parameterized queries -- =================================================================== @@ -570,14 +912,37 @@ EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1); EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st5('foo', 1); EXECUTE st5('foo', 1); +-- altering FDW options requires replanning +PREPARE st6 AS SELECT * FROM ft1 t1 WHERE t1.c1 = t1.c2; +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6; +PREPARE st7 AS INSERT INTO ft1 (c1,c2,c3) VALUES (1001,101,'foo'); +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7; +ALTER TABLE "S 1"."T 1" RENAME TO "T 0"; +ALTER FOREIGN TABLE ft1 OPTIONS (SET table_name 'T 0'); +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st6; +EXECUTE st6; +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st7; +ALTER TABLE "S 1"."T 0" RENAME TO "T 1"; +ALTER FOREIGN TABLE ft1 OPTIONS (SET table_name 'T 1'); + +PREPARE st8 AS SELECT count(c3) FROM ft1 t1 WHERE t1.c1 === t1.c2; +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8; +ALTER SERVER loopback OPTIONS (DROP extensions); +EXPLAIN (VERBOSE, COSTS OFF) EXECUTE st8; +EXECUTE st8; +ALTER SERVER loopback OPTIONS (ADD extensions 'postgres_fdw'); + -- cleanup DEALLOCATE st1; DEALLOCATE st2; DEALLOCATE st3; DEALLOCATE st4; DEALLOCATE st5; +DEALLOCATE st6; +DEALLOCATE st7; +DEALLOCATE st8; --- System columns, except ctid, should not be sent to remote +-- System columns, except ctid and oid, should not be sent to remote EXPLAIN (VERBOSE, COSTS OFF) SELECT * FROM ft1 t1 WHERE t1.tableoid = 'pg_class'::regclass LIMIT 1; SELECT * FROM ft1 t1 WHERE t1.tableoid = 'ft1'::regclass LIMIT 1; @@ -590,9 +955,12 @@ SELECT * FROM ft1 t1 WHERE t1.ctid = '(0,2)'; EXPLAIN (VERBOSE, COSTS OFF) SELECT ctid, * FROM ft1 t1 LIMIT 1; SELECT ctid, * FROM ft1 t1 LIMIT 1; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT oid, * FROM ft_pg_type WHERE typname = 'int4'; +SELECT oid, * FROM ft_pg_type WHERE typname = 'int4'; -- =================================================================== --- used in pl/pgsql function +-- used in PL/pgSQL function -- =================================================================== CREATE OR REPLACE FUNCTION f_test(p_c1 int) RETURNS int AS $$ DECLARE @@ -613,6 +981,7 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE int; SELECT * FROM ft1 WHERE c1 = 1; -- ERROR SELECT ft1.c1, ft2.c2, ft1.c8 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR SELECT ft1.c1, ft2.c2, ft1 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; -- ERROR +SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum; -- =================================================================== @@ -1257,6 +1626,9 @@ CREATE TABLE import_source.t3 (c1 timestamptz default now(), c2 typ1); CREATE TABLE import_source."x 4" (c1 float8, "C 2" text, c3 varchar(42)); CREATE TABLE import_source."x 5" (c1 float8); ALTER TABLE import_source."x 5" DROP COLUMN c1; +CREATE TABLE import_source.t4 (c1 int) PARTITION BY RANGE (c1); +CREATE TABLE import_source.t4_part PARTITION OF import_source.t4 + FOR VALUES FROM (1) TO (100); CREATE SCHEMA import_dest1; IMPORT FOREIGN SCHEMA import_source FROM SERVER loopback INTO import_dest1; diff --git a/contrib/seg/expected/seg.out b/contrib/seg/expected/seg.out index 1f82a4abb8..18010c4d5c 100644 --- a/contrib/seg/expected/seg.out +++ b/contrib/seg/expected/seg.out @@ -2,6 +2,14 @@ -- Test seg datatype -- CREATE EXTENSION seg; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + -- -- testing the input and output functions -- diff --git a/contrib/seg/expected/seg_1.out b/contrib/seg/expected/seg_1.out index 563c744b2d..566ce394ed 100644 --- a/contrib/seg/expected/seg_1.out +++ b/contrib/seg/expected/seg_1.out @@ -2,6 +2,14 @@ -- Test seg datatype -- CREATE EXTENSION seg; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + amname | opcname +--------+--------- +(0 rows) + -- -- testing the input and output functions -- diff --git a/contrib/seg/seg-validate.pl b/contrib/seg/seg-validate.pl index cb3fb9a099..b8957ed984 100755 --- a/contrib/seg/seg-validate.pl +++ b/contrib/seg/seg-validate.pl @@ -1,20 +1,23 @@ #!/usr/bin/perl -$integer = '[+-]?[0-9]+'; -$real = '[+-]?[0-9]+\.[0-9]+'; - -$RANGE = '(\.\.)(\.)?'; -$PLUMIN = q(\'\+\-\'); -$FLOAT = "(($integer)|($real))([eE]($integer))?"; -$EXTENSION = '<|>|~'; - -$boundary = "($EXTENSION)?$FLOAT"; -$deviation = $FLOAT; - -$rule_1 = $boundary . $PLUMIN . $deviation; -$rule_2 = $boundary . $RANGE . $boundary; -$rule_3 = $boundary . $RANGE; -$rule_4 = $RANGE . $boundary; -$rule_5 = $boundary; + +use strict; + +my $integer = '[+-]?[0-9]+'; +my $real = '[+-]?[0-9]+\.[0-9]+'; + +my $RANGE = '(\.\.)(\.)?'; +my $PLUMIN = q(\'\+\-\'); +my $FLOAT = "(($integer)|($real))([eE]($integer))?"; +my $EXTENSION = '<|>|~'; + +my $boundary = "($EXTENSION)?$FLOAT"; +my $deviation = $FLOAT; + +my $rule_1 = $boundary . $PLUMIN . $deviation; +my $rule_2 = $boundary . $RANGE . $boundary; +my $rule_3 = $boundary . $RANGE; +my $rule_4 = $RANGE . $boundary; +my $rule_5 = $boundary; print "$rule_5\n"; diff --git a/contrib/seg/seg.c b/contrib/seg/seg.c index c6c082b8ea..61e72937ee 100644 --- a/contrib/seg/seg.c +++ b/contrib/seg/seg.c @@ -17,6 +17,11 @@ #include "segdata.h" + +#define DatumGetSegP(X) ((SEG *) DatumGetPointer(X)) +#define PG_GETARG_SEG_P(n) DatumGetSegP(PG_GETARG_POINTER(n)) + + /* #define GIST_DEBUG #define GIST_QUERY_DEBUG @@ -47,52 +52,45 @@ PG_FUNCTION_INFO_V1(seg_center); /* ** GiST support methods */ -bool gseg_consistent(GISTENTRY *entry, - SEG *query, - StrategyNumber strategy, - Oid subtype, - bool *recheck); -GISTENTRY *gseg_compress(GISTENTRY *entry); -GISTENTRY *gseg_decompress(GISTENTRY *entry); -float *gseg_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result); -GIST_SPLITVEC *gseg_picksplit(GistEntryVector *entryvec, GIST_SPLITVEC *v); -bool gseg_leaf_consistent(SEG *key, SEG *query, StrategyNumber strategy); -bool gseg_internal_consistent(SEG *key, SEG *query, StrategyNumber strategy); -SEG *gseg_union(GistEntryVector *entryvec, int *sizep); -SEG *gseg_binary_union(SEG *r1, SEG *r2, int *sizep); -bool *gseg_same(SEG *b1, SEG *b2, bool *result); +PG_FUNCTION_INFO_V1(gseg_consistent); +PG_FUNCTION_INFO_V1(gseg_compress); +PG_FUNCTION_INFO_V1(gseg_decompress); +PG_FUNCTION_INFO_V1(gseg_picksplit); +PG_FUNCTION_INFO_V1(gseg_penalty); +PG_FUNCTION_INFO_V1(gseg_union); +PG_FUNCTION_INFO_V1(gseg_same); +static Datum gseg_leaf_consistent(Datum key, Datum query, StrategyNumber strategy); +static Datum gseg_internal_consistent(Datum key, Datum query, StrategyNumber strategy); +static Datum gseg_binary_union(Datum r1, Datum r2, int *sizep); /* ** R-tree support functions */ -bool seg_same(SEG *a, SEG *b); -bool seg_contains_int(SEG *a, int *b); -bool seg_contains_float4(SEG *a, float4 *b); -bool seg_contains_float8(SEG *a, float8 *b); -bool seg_contains(SEG *a, SEG *b); -bool seg_contained(SEG *a, SEG *b); -bool seg_overlap(SEG *a, SEG *b); -bool seg_left(SEG *a, SEG *b); -bool seg_over_left(SEG *a, SEG *b); -bool seg_right(SEG *a, SEG *b); -bool seg_over_right(SEG *a, SEG *b); -SEG *seg_union(SEG *a, SEG *b); -SEG *seg_inter(SEG *a, SEG *b); -void rt_seg_size(SEG *a, float *sz); +PG_FUNCTION_INFO_V1(seg_same); +PG_FUNCTION_INFO_V1(seg_contains); +PG_FUNCTION_INFO_V1(seg_contained); +PG_FUNCTION_INFO_V1(seg_overlap); +PG_FUNCTION_INFO_V1(seg_left); +PG_FUNCTION_INFO_V1(seg_over_left); +PG_FUNCTION_INFO_V1(seg_right); +PG_FUNCTION_INFO_V1(seg_over_right); +PG_FUNCTION_INFO_V1(seg_union); +PG_FUNCTION_INFO_V1(seg_inter); +static void rt_seg_size(SEG *a, float *size); /* ** Various operators */ -int32 seg_cmp(SEG *a, SEG *b); -bool seg_lt(SEG *a, SEG *b); -bool seg_le(SEG *a, SEG *b); -bool seg_gt(SEG *a, SEG *b); -bool seg_ge(SEG *a, SEG *b); -bool seg_different(SEG *a, SEG *b); +PG_FUNCTION_INFO_V1(seg_cmp); +PG_FUNCTION_INFO_V1(seg_lt); +PG_FUNCTION_INFO_V1(seg_le); +PG_FUNCTION_INFO_V1(seg_gt); +PG_FUNCTION_INFO_V1(seg_ge); +PG_FUNCTION_INFO_V1(seg_different); /* -** Auxiliary funxtions +** Auxiliary functions */ static int restore(char *s, float val, int n); @@ -120,7 +118,7 @@ seg_in(PG_FUNCTION_ARGS) Datum seg_out(PG_FUNCTION_ARGS) { - SEG *seg = (SEG *) PG_GETARG_POINTER(0); + SEG *seg = PG_GETARG_SEG_P(0); char *result; char *p; @@ -161,7 +159,7 @@ seg_out(PG_FUNCTION_ARGS) Datum seg_center(PG_FUNCTION_ARGS) { - SEG *seg = (SEG *) PG_GETARG_POINTER(0); + SEG *seg = PG_GETARG_SEG_P(0); PG_RETURN_FLOAT4(((float) seg->lower + (float) seg->upper) / 2.0); } @@ -169,7 +167,7 @@ seg_center(PG_FUNCTION_ARGS) Datum seg_lower(PG_FUNCTION_ARGS) { - SEG *seg = (SEG *) PG_GETARG_POINTER(0); + SEG *seg = PG_GETARG_SEG_P(0); PG_RETURN_FLOAT4(seg->lower); } @@ -177,7 +175,7 @@ seg_lower(PG_FUNCTION_ARGS) Datum seg_upper(PG_FUNCTION_ARGS) { - SEG *seg = (SEG *) PG_GETARG_POINTER(0); + SEG *seg = PG_GETARG_SEG_P(0); PG_RETURN_FLOAT4(seg->upper); } @@ -193,13 +191,16 @@ seg_upper(PG_FUNCTION_ARGS) ** the predicate x op query == FALSE, where op is the oper ** corresponding to strategy in the pg_amop table. */ -bool -gseg_consistent(GISTENTRY *entry, - SEG *query, - StrategyNumber strategy, - Oid subtype, - bool *recheck) +Datum +gseg_consistent(PG_FUNCTION_ARGS) { + GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0); + Datum query = PG_GETARG_DATUM(1); + StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2); + + /* Oid subtype = PG_GETARG_OID(3); */ + bool *recheck = (bool *) PG_GETARG_POINTER(4); + /* All cases served by this function are exact */ *recheck = false; @@ -208,73 +209,77 @@ gseg_consistent(GISTENTRY *entry, * gseg_leaf_consistent */ if (GIST_LEAF(entry)) - return (gseg_leaf_consistent((SEG *) DatumGetPointer(entry->key), query, strategy)); + return gseg_leaf_consistent(entry->key, query, strategy); else - return (gseg_internal_consistent((SEG *) DatumGetPointer(entry->key), query, strategy)); + return gseg_internal_consistent(entry->key, query, strategy); } /* ** The GiST Union method for segments ** returns the minimal bounding seg that encloses all the entries in entryvec */ -SEG * -gseg_union(GistEntryVector *entryvec, int *sizep) +Datum +gseg_union(PG_FUNCTION_ARGS) { + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + int *sizep = (int *) PG_GETARG_POINTER(1); int numranges, i; - SEG *out = (SEG *) NULL; - SEG *tmp; + Datum out = 0; + Datum tmp; #ifdef GIST_DEBUG fprintf(stderr, "union\n"); #endif numranges = entryvec->n; - tmp = (SEG *) DatumGetPointer(entryvec->vector[0].key); + tmp = entryvec->vector[0].key; *sizep = sizeof(SEG); for (i = 1; i < numranges; i++) { - out = gseg_binary_union(tmp, (SEG *) - DatumGetPointer(entryvec->vector[i].key), - sizep); + out = gseg_binary_union(tmp, entryvec->vector[i].key, sizep); tmp = out; } - return (out); + PG_RETURN_DATUM(out); } /* ** GiST Compress and Decompress methods for segments ** do not do anything. */ -GISTENTRY * -gseg_compress(GISTENTRY *entry) +Datum +gseg_compress(PG_FUNCTION_ARGS) { - return (entry); + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); } -GISTENTRY * -gseg_decompress(GISTENTRY *entry) +Datum +gseg_decompress(PG_FUNCTION_ARGS) { - return (entry); + PG_RETURN_POINTER(PG_GETARG_POINTER(0)); } /* ** The GiST Penalty method for segments ** As in the R-tree paper, we use change in area as our penalty metric */ -float * -gseg_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result) +Datum +gseg_penalty(PG_FUNCTION_ARGS) { + GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0); + GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1); + float *result = (float *) PG_GETARG_POINTER(2); SEG *ud; float tmp1, tmp2; - ud = seg_union((SEG *) DatumGetPointer(origentry->key), - (SEG *) DatumGetPointer(newentry->key)); + ud = DatumGetSegP(DirectFunctionCall2(seg_union, + origentry->key, + newentry->key)); rt_seg_size(ud, &tmp1); - rt_seg_size((SEG *) DatumGetPointer(origentry->key), &tmp2); + rt_seg_size(DatumGetSegP(origentry->key), &tmp2); *result = tmp1 - tmp2; #ifdef GIST_DEBUG @@ -282,7 +287,7 @@ gseg_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result) fprintf(stderr, "\t%g\n", *result); #endif - return (result); + PG_RETURN_POINTER(result); } /* @@ -309,14 +314,15 @@ gseg_picksplit_item_cmp(const void *a, const void *b) * it's easier and more robust to just sort the segments by center-point and * split at the middle. */ -GIST_SPLITVEC * -gseg_picksplit(GistEntryVector *entryvec, - GIST_SPLITVEC *v) +Datum +gseg_picksplit(PG_FUNCTION_ARGS) { + GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); + GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); int i; - SEG *datum_l, - *datum_r, - *seg; + SEG *seg, + *seg_l, + *seg_r; gseg_picksplit_item *sort_items; OffsetNumber *left, *right; @@ -337,7 +343,7 @@ gseg_picksplit(GistEntryVector *entryvec, palloc(maxoff * sizeof(gseg_picksplit_item)); for (i = 1; i <= maxoff; i++) { - seg = (SEG *) DatumGetPointer(entryvec->vector[i].key); + seg = DatumGetSegP(entryvec->vector[i].key); /* center calculation is done this way to avoid possible overflow */ sort_items[i - 1].center = seg->lower * 0.5f + seg->upper * 0.5f; sort_items[i - 1].index = i; @@ -359,13 +365,17 @@ gseg_picksplit(GistEntryVector *entryvec, /* * Emit segments to the left output page, and compute its bounding box. */ - datum_l = (SEG *) palloc(sizeof(SEG)); - memcpy(datum_l, sort_items[0].data, sizeof(SEG)); + seg_l = (SEG *) palloc(sizeof(SEG)); + memcpy(seg_l, sort_items[0].data, sizeof(SEG)); *left++ = sort_items[0].index; v->spl_nleft++; for (i = 1; i < firstright; i++) { - datum_l = seg_union(datum_l, sort_items[i].data); + Datum sortitem = PointerGetDatum(sort_items[i].data); + + seg_l = DatumGetSegP(DirectFunctionCall2(seg_union, + PointerGetDatum(seg_l), + sortitem)); *left++ = sort_items[i].index; v->spl_nleft++; } @@ -373,30 +383,36 @@ gseg_picksplit(GistEntryVector *entryvec, /* * Likewise for the right page. */ - datum_r = (SEG *) palloc(sizeof(SEG)); - memcpy(datum_r, sort_items[firstright].data, sizeof(SEG)); + seg_r = (SEG *) palloc(sizeof(SEG)); + memcpy(seg_r, sort_items[firstright].data, sizeof(SEG)); *right++ = sort_items[firstright].index; v->spl_nright++; for (i = firstright + 1; i < maxoff; i++) { - datum_r = seg_union(datum_r, sort_items[i].data); + Datum sortitem = PointerGetDatum(sort_items[i].data); + + seg_r = DatumGetSegP(DirectFunctionCall2(seg_union, + PointerGetDatum(seg_r), + sortitem)); *right++ = sort_items[i].index; v->spl_nright++; } - v->spl_ldatum = PointerGetDatum(datum_l); - v->spl_rdatum = PointerGetDatum(datum_r); + v->spl_ldatum = PointerGetDatum(seg_l); + v->spl_rdatum = PointerGetDatum(seg_r); - return v; + PG_RETURN_POINTER(v); } /* ** Equality methods */ -bool * -gseg_same(SEG *b1, SEG *b2, bool *result) +Datum +gseg_same(PG_FUNCTION_ARGS) { - if (seg_same(b1, b2)) + bool *result = (bool *) PG_GETARG_POINTER(2); + + if (DirectFunctionCall2(seg_same, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))) *result = TRUE; else *result = FALSE; @@ -405,18 +421,16 @@ gseg_same(SEG *b1, SEG *b2, bool *result) fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE")); #endif - return (result); + PG_RETURN_POINTER(result); } /* ** SUPPORT ROUTINES */ -bool -gseg_leaf_consistent(SEG *key, - SEG *query, - StrategyNumber strategy) +static Datum +gseg_leaf_consistent(Datum key, Datum query, StrategyNumber strategy) { - bool retval; + Datum retval; #ifdef GIST_QUERY_DEBUG fprintf(stderr, "leaf_consistent, %d\n", strategy); @@ -425,41 +439,40 @@ gseg_leaf_consistent(SEG *key, switch (strategy) { case RTLeftStrategyNumber: - retval = (bool) seg_left(key, query); + retval = DirectFunctionCall2(seg_left, key, query); break; case RTOverLeftStrategyNumber: - retval = (bool) seg_over_left(key, query); + retval = DirectFunctionCall2(seg_over_left, key, query); break; case RTOverlapStrategyNumber: - retval = (bool) seg_overlap(key, query); + retval = DirectFunctionCall2(seg_overlap, key, query); break; case RTOverRightStrategyNumber: - retval = (bool) seg_over_right(key, query); + retval = DirectFunctionCall2(seg_over_right, key, query); break; case RTRightStrategyNumber: - retval = (bool) seg_right(key, query); + retval = DirectFunctionCall2(seg_right, key, query); break; case RTSameStrategyNumber: - retval = (bool) seg_same(key, query); + retval = DirectFunctionCall2(seg_same, key, query); break; case RTContainsStrategyNumber: case RTOldContainsStrategyNumber: - retval = (bool) seg_contains(key, query); + retval = DirectFunctionCall2(seg_contains, key, query); break; case RTContainedByStrategyNumber: case RTOldContainedByStrategyNumber: - retval = (bool) seg_contained(key, query); + retval = DirectFunctionCall2(seg_contained, key, query); break; default: retval = FALSE; } - return (retval); + + PG_RETURN_DATUM(retval); } -bool -gseg_internal_consistent(SEG *key, - SEG *query, - StrategyNumber strategy) +static Datum +gseg_internal_consistent(Datum key, Datum query, StrategyNumber strategy) { bool retval; @@ -470,117 +483,147 @@ gseg_internal_consistent(SEG *key, switch (strategy) { case RTLeftStrategyNumber: - retval = (bool) !seg_over_right(key, query); + retval = + !DatumGetBool(DirectFunctionCall2(seg_over_right, key, query)); break; case RTOverLeftStrategyNumber: - retval = (bool) !seg_right(key, query); + retval = + !DatumGetBool(DirectFunctionCall2(seg_right, key, query)); break; case RTOverlapStrategyNumber: - retval = (bool) seg_overlap(key, query); + retval = + DatumGetBool(DirectFunctionCall2(seg_overlap, key, query)); break; case RTOverRightStrategyNumber: - retval = (bool) !seg_left(key, query); + retval = + !DatumGetBool(DirectFunctionCall2(seg_left, key, query)); break; case RTRightStrategyNumber: - retval = (bool) !seg_over_left(key, query); + retval = + !DatumGetBool(DirectFunctionCall2(seg_over_left, key, query)); break; case RTSameStrategyNumber: case RTContainsStrategyNumber: case RTOldContainsStrategyNumber: - retval = (bool) seg_contains(key, query); + retval = + DatumGetBool(DirectFunctionCall2(seg_contains, key, query)); break; case RTContainedByStrategyNumber: case RTOldContainedByStrategyNumber: - retval = (bool) seg_overlap(key, query); + retval = + DatumGetBool(DirectFunctionCall2(seg_overlap, key, query)); break; default: retval = FALSE; } - return (retval); + + PG_RETURN_BOOL(retval); } -SEG * -gseg_binary_union(SEG *r1, SEG *r2, int *sizep) +static Datum +gseg_binary_union(Datum r1, Datum r2, int *sizep) { - SEG *retval; + Datum retval; - retval = seg_union(r1, r2); + retval = DirectFunctionCall2(seg_union, r1, r2); *sizep = sizeof(SEG); return (retval); } -bool -seg_contains(SEG *a, SEG *b) +Datum +seg_contains(PG_FUNCTION_ARGS) { - return ((a->lower <= b->lower) && (a->upper >= b->upper)); + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + + PG_RETURN_BOOL((a->lower <= b->lower) && (a->upper >= b->upper)); } -bool -seg_contained(SEG *a, SEG *b) +Datum +seg_contained(PG_FUNCTION_ARGS) { - return (seg_contains(b, a)); + Datum a = PG_GETARG_DATUM(0); + Datum b = PG_GETARG_DATUM(1); + + PG_RETURN_DATUM(DirectFunctionCall2(seg_contains, b, a)); } /***************************************************************************** * Operator class for R-tree indexing *****************************************************************************/ -bool -seg_same(SEG *a, SEG *b) +Datum +seg_same(PG_FUNCTION_ARGS) { - return seg_cmp(a, b) == 0; + int cmp = DatumGetInt32( + DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(cmp == 0); } /* seg_overlap -- does a overlap b? */ -bool -seg_overlap(SEG *a, SEG *b) +Datum +seg_overlap(PG_FUNCTION_ARGS) { - return ( - ((a->upper >= b->upper) && (a->lower <= b->upper)) - || - ((b->upper >= a->upper) && (b->lower <= a->upper)) - ); + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + + PG_RETURN_BOOL(((a->upper >= b->upper) && (a->lower <= b->upper)) || + ((b->upper >= a->upper) && (b->lower <= a->upper))); } -/* seg_overleft -- is the right edge of (a) located at or left of the right edge of (b)? +/* seg_over_left -- is the right edge of (a) located at or left of the right edge of (b)? */ -bool -seg_over_left(SEG *a, SEG *b) +Datum +seg_over_left(PG_FUNCTION_ARGS) { - return (a->upper <= b->upper); + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + + PG_RETURN_BOOL(a->upper <= b->upper); } /* seg_left -- is (a) entirely on the left of (b)? */ -bool -seg_left(SEG *a, SEG *b) +Datum +seg_left(PG_FUNCTION_ARGS) { - return (a->upper < b->lower); + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + + PG_RETURN_BOOL(a->upper < b->lower); } /* seg_right -- is (a) entirely on the right of (b)? */ -bool -seg_right(SEG *a, SEG *b) +Datum +seg_right(PG_FUNCTION_ARGS) { - return (a->lower > b->upper); + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + + PG_RETURN_BOOL(a->lower > b->upper); } -/* seg_overright -- is the left edge of (a) located at or right of the left edge of (b)? +/* seg_over_right -- is the left edge of (a) located at or right of the left edge of (b)? */ -bool -seg_over_right(SEG *a, SEG *b) +Datum +seg_over_right(PG_FUNCTION_ARGS) { - return (a->lower >= b->lower); -} + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + PG_RETURN_BOOL(a->lower >= b->lower); +} -SEG * -seg_union(SEG *a, SEG *b) +Datum +seg_union(PG_FUNCTION_ARGS) { + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); SEG *n; n = (SEG *) palloc(sizeof(*n)); @@ -613,13 +656,14 @@ seg_union(SEG *a, SEG *b) n->l_ext = b->l_ext; } - return (n); + PG_RETURN_POINTER(n); } - -SEG * -seg_inter(SEG *a, SEG *b) +Datum +seg_inter(PG_FUNCTION_ARGS) { + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); SEG *n; n = (SEG *) palloc(sizeof(*n)); @@ -652,10 +696,10 @@ seg_inter(SEG *a, SEG *b) n->l_ext = b->l_ext; } - return (n); + PG_RETURN_POINTER(n); } -void +static void rt_seg_size(SEG *a, float *size) { if (a == (SEG *) NULL || a->upper <= a->lower) @@ -669,7 +713,7 @@ rt_seg_size(SEG *a, float *size) Datum seg_size(PG_FUNCTION_ARGS) { - SEG *seg = (SEG *) PG_GETARG_POINTER(0); + SEG *seg = PG_GETARG_SEG_P(0); PG_RETURN_FLOAT4((float) Abs(seg->upper - seg->lower)); } @@ -678,16 +722,19 @@ seg_size(PG_FUNCTION_ARGS) /***************************************************************************** * Miscellaneous operators *****************************************************************************/ -int32 -seg_cmp(SEG *a, SEG *b) +Datum +seg_cmp(PG_FUNCTION_ARGS) { + SEG *a = PG_GETARG_SEG_P(0); + SEG *b = PG_GETARG_SEG_P(1); + /* * First compare on lower boundary position */ if (a->lower < b->lower) - return -1; + PG_RETURN_INT32(-1); if (a->lower > b->lower) - return 1; + PG_RETURN_INT32(1); /* * a->lower == b->lower, so consider type of boundary. @@ -699,27 +746,27 @@ seg_cmp(SEG *a, SEG *b) if (a->l_ext != b->l_ext) { if (a->l_ext == '-') - return -1; + PG_RETURN_INT32(-1); if (b->l_ext == '-') - return 1; + PG_RETURN_INT32(1); if (a->l_ext == '<') - return -1; + PG_RETURN_INT32(-1); if (b->l_ext == '<') - return 1; + PG_RETURN_INT32(1); if (a->l_ext == '>') - return 1; + PG_RETURN_INT32(1); if (b->l_ext == '>') - return -1; + PG_RETURN_INT32(-1); } /* * For other boundary types, consider # of significant digits first. */ if (a->l_sigd < b->l_sigd) /* (a) is blurred and is likely to include (b) */ - return -1; + PG_RETURN_INT32(-1); if (a->l_sigd > b->l_sigd) /* (a) is less blurred and is likely to be * included in (b) */ - return 1; + PG_RETURN_INT32(1); /* * For same # of digits, an approximate boundary is more blurred than @@ -728,9 +775,9 @@ seg_cmp(SEG *a, SEG *b) if (a->l_ext != b->l_ext) { if (a->l_ext == '~') /* (a) is approximate, while (b) is exact */ - return -1; + PG_RETURN_INT32(-1); if (b->l_ext == '~') - return 1; + PG_RETURN_INT32(1); /* can't get here unless data is corrupt */ elog(ERROR, "bogus lower boundary types %d %d", (int) a->l_ext, (int) b->l_ext); @@ -742,9 +789,9 @@ seg_cmp(SEG *a, SEG *b) * First compare on upper boundary position */ if (a->upper < b->upper) - return -1; + PG_RETURN_INT32(-1); if (a->upper > b->upper) - return 1; + PG_RETURN_INT32(1); /* * a->upper == b->upper, so consider type of boundary. @@ -756,17 +803,17 @@ seg_cmp(SEG *a, SEG *b) if (a->u_ext != b->u_ext) { if (a->u_ext == '-') - return 1; + PG_RETURN_INT32(1); if (b->u_ext == '-') - return -1; + PG_RETURN_INT32(-1); if (a->u_ext == '<') - return -1; + PG_RETURN_INT32(-1); if (b->u_ext == '<') - return 1; + PG_RETURN_INT32(1); if (a->u_ext == '>') - return 1; + PG_RETURN_INT32(1); if (b->u_ext == '>') - return -1; + PG_RETURN_INT32(-1); } /* @@ -774,10 +821,10 @@ seg_cmp(SEG *a, SEG *b) * result here is converse of the lower-boundary case. */ if (a->u_sigd < b->u_sigd) /* (a) is blurred and is likely to include (b) */ - return 1; + PG_RETURN_INT32(1); if (a->u_sigd > b->u_sigd) /* (a) is less blurred and is likely to be * included in (b) */ - return -1; + PG_RETURN_INT32(-1); /* * For same # of digits, an approximate boundary is more blurred than @@ -786,45 +833,61 @@ seg_cmp(SEG *a, SEG *b) if (a->u_ext != b->u_ext) { if (a->u_ext == '~') /* (a) is approximate, while (b) is exact */ - return 1; + PG_RETURN_INT32(1); if (b->u_ext == '~') - return -1; + PG_RETURN_INT32(-1); /* can't get here unless data is corrupt */ elog(ERROR, "bogus upper boundary types %d %d", (int) a->u_ext, (int) b->u_ext); } - return 0; + PG_RETURN_INT32(0); } -bool -seg_lt(SEG *a, SEG *b) +Datum +seg_lt(PG_FUNCTION_ARGS) { - return seg_cmp(a, b) < 0; + int cmp = DatumGetInt32( + DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(cmp < 0); } -bool -seg_le(SEG *a, SEG *b) +Datum +seg_le(PG_FUNCTION_ARGS) { - return seg_cmp(a, b) <= 0; + int cmp = DatumGetInt32( + DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(cmp <= 0); } -bool -seg_gt(SEG *a, SEG *b) +Datum +seg_gt(PG_FUNCTION_ARGS) { - return seg_cmp(a, b) > 0; + int cmp = DatumGetInt32( + DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(cmp > 0); } -bool -seg_ge(SEG *a, SEG *b) +Datum +seg_ge(PG_FUNCTION_ARGS) { - return seg_cmp(a, b) >= 0; + int cmp = DatumGetInt32( + DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(cmp >= 0); } -bool -seg_different(SEG *a, SEG *b) + +Datum +seg_different(PG_FUNCTION_ARGS) { - return seg_cmp(a, b) != 0; + int cmp = DatumGetInt32( + DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1))); + + PG_RETURN_BOOL(cmp != 0); } @@ -888,7 +951,7 @@ restore(char *result, float val, int n) if (Abs(exp) <= 4) { /* - * remove the decimal point from the mantyssa and write the digits + * remove the decimal point from the mantissa and write the digits * to the buf array */ for (p = result + sign, i = 10, dp = 0; *p != 'e'; p++, i++) @@ -985,24 +1048,6 @@ restore(char *result, float val, int n) ** Miscellany */ -bool -seg_contains_int(SEG *a, int *b) -{ - return ((a->lower <= *b) && (a->upper >= *b)); -} - -bool -seg_contains_float4(SEG *a, float4 *b) -{ - return ((a->lower <= *b) && (a->upper >= *b)); -} - -bool -seg_contains_float8(SEG *a, float8 *b) -{ - return ((a->lower <= *b) && (a->upper >= *b)); -} - /* find out the number of significant digits in a string representing * a floating point number */ diff --git a/contrib/seg/sort-segments.pl b/contrib/seg/sort-segments.pl index a465468d5b..04eafd92f2 100755 --- a/contrib/seg/sort-segments.pl +++ b/contrib/seg/sort-segments.pl @@ -2,6 +2,10 @@ # this script will sort any table with the segment data type in its last column +use strict; + +my @rows; + while (<>) { chomp; @@ -10,11 +14,11 @@ while (<>) foreach ( sort { - @ar = split("\t", $a); - $valA = pop @ar; + my @ar = split("\t", $a); + my $valA = pop @ar; $valA =~ s/[~<> ]+//g; @ar = split("\t", $b); - $valB = pop @ar; + my $valB = pop @ar; $valB =~ s/[~<> ]+//g; $valA <=> $valB } @rows) diff --git a/contrib/seg/sql/seg.sql b/contrib/seg/sql/seg.sql index 7b7f138dbf..aa91931474 100644 --- a/contrib/seg/sql/seg.sql +++ b/contrib/seg/sql/seg.sql @@ -4,6 +4,11 @@ CREATE EXTENSION seg; +-- Check whether any of our opclasses fail amvalidate +SELECT amname, opcname +FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod +WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid); + -- -- testing the input and output functions -- diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c index 8fad1fc80c..69dd290a77 100644 --- a/contrib/sepgsql/database.c +++ b/contrib/sepgsql/database.c @@ -4,7 +4,7 @@ * * Routines corresponding to database objects * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c index d1e9f53cda..b643720e36 100644 --- a/contrib/sepgsql/dml.c +++ b/contrib/sepgsql/dml.c @@ -4,7 +4,7 @@ * * Routines to handle DML permission checks * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ @@ -190,6 +190,7 @@ check_relation_privileges(Oid relOid, switch (relkind) { case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: result = sepgsql_avc_check_perms(&object, SEPG_CLASS_DB_TABLE, required, @@ -225,7 +226,7 @@ check_relation_privileges(Oid relOid, /* * Only columns owned by relations shall be checked */ - if (relkind != RELKIND_RELATION) + if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) return true; /* diff --git a/contrib/sepgsql/expected/alter.out b/contrib/sepgsql/expected/alter.out index fb2545fa5c..0948139f93 100644 --- a/contrib/sepgsql/expected/alter.out +++ b/contrib/sepgsql/expected/alter.out @@ -26,6 +26,13 @@ SET search_path = regtest_schema_1, regtest_schema_2, public; CREATE TABLE regtest_table_1 (a int, b text); CREATE TABLE regtest_table_2 (c text) inherits (regtest_table_1); CREATE TABLE regtest_table_3 (x int primary key, y text); +--- +-- partitioned table parent +CREATE TABLE regtest_ptable_1 (o int, p text) PARTITION BY RANGE (o); +-- partitioned table children +CREATE TABLE regtest_ptable_1_ones PARTITION OF regtest_ptable_1 FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE regtest_ptable_1_tens PARTITION OF regtest_ptable_1 FOR VALUES FROM ('10') TO ('100'); +--- CREATE SEQUENCE regtest_seq_1; CREATE VIEW regtest_view_1 AS SELECT * FROM regtest_table_1 WHERE a > 0; CREATE FUNCTION regtest_func_1 (text) RETURNS bool @@ -54,6 +61,10 @@ LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_reg LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_table_1" ALTER TABLE regtest_table_1 OWNER TO regress_sepgsql_test_user; LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_table_1" +ALTER TABLE regtest_ptable_1 OWNER TO regress_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_ptable_1" +ALTER TABLE regtest_ptable_1_ones OWNER TO regress_sepgsql_test_user; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_ptable_1_ones" ALTER SEQUENCE regtest_seq_1 OWNER TO regress_sepgsql_test_user; LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema_1.regtest_seq_1" ALTER SEQUENCE regtest_seq_1 OWNER TO regress_sepgsql_test_user; @@ -73,6 +84,14 @@ ALTER TABLE regtest_table_1 SET SCHEMA regtest_schema_2; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_1" LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_table_1" +ALTER TABLE regtest_ptable_1 SET SCHEMA regtest_schema_2; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_1" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_ptable_1" +ALTER TABLE regtest_ptable_1_ones SET SCHEMA regtest_schema_2; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_1" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_1.regtest_ptable_1_ones" ALTER SEQUENCE regtest_seq_1 SET SCHEMA regtest_schema_2; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_1" LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" @@ -97,6 +116,16 @@ LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_reg LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="public" LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_table_1" +--- +-- partitioned table parent +ALTER TABLE regtest_ptable_1 RENAME TO regtest_ptable; +LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_ptable_1" +-- partitioned table child +ALTER TABLE regtest_ptable_1_ones RENAME TO regtest_table_part; +LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_ptable_1_ones" +--- ALTER SEQUENCE regtest_seq_1 RENAME TO regtest_seq; LOG: SELinux: allowed { add_name remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema_2" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema_2.regtest_seq_1" @@ -170,8 +199,11 @@ LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_reg LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table column a" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_table_3" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table regtest_table_3 column x" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4eq(integer,integer)" ALTER TABLE regtest_table ADD CONSTRAINT test_ck CHECK (b like '%abc%') NOT VALID; -- not supported ALTER TABLE regtest_table VALIDATE CONSTRAINT test_ck; -- not supported +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" ALTER TABLE regtest_table DROP CONSTRAINT test_ck; -- not supported CREATE TRIGGER regtest_test_trig BEFORE UPDATE ON regtest_table FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); @@ -194,6 +226,83 @@ ALTER TABLE regtest_table_2 NO INHERIT regtest_table; -- not supported ALTER TABLE regtest_table_2 INHERIT regtest_table; -- not supported ALTER TABLE regtest_table SET TABLESPACE pg_default; LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_table" +--- +-- partitioned table parent +ALTER TABLE regtest_ptable ADD COLUMN d float; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: ALTER TABLE regtest_ptable ADD COLUMN d float; + ^ +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.d" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.d" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.d" +ALTER TABLE regtest_ptable DROP COLUMN d; +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.d" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.d" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.d" +ALTER TABLE regtest_ptable ALTER p SET DEFAULT 'abcd'; -- not supported by sepgsql +ALTER TABLE regtest_ptable ALTER p SET DEFAULT 'XYZ'; -- not supported by sepgsql +ALTER TABLE regtest_ptable ALTER p DROP DEFAULT; -- not supported by sepgsql +ALTER TABLE regtest_ptable ALTER p SET NOT NULL; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.p" +ALTER TABLE regtest_ptable ALTER p DROP NOT NULL; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.p" +ALTER TABLE regtest_ptable ALTER p SET STATISTICS -1; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.p" +ALTER TABLE regtest_ptable ALTER p SET (n_distinct = 999); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.p" +ALTER TABLE regtest_ptable ALTER p SET STORAGE PLAIN; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.p" +ALTER TABLE regtest_ptable ADD CONSTRAINT test_ck CHECK (p like '%abc%') NOT VALID; -- not supported by sepgsql +ALTER TABLE regtest_ptable DROP CONSTRAINT test_ck; -- not supported by sepgsql +ALTER TABLE regtest_ptable SET WITH OIDS; +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.oid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.oid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.oid" +ALTER TABLE regtest_ptable SET WITHOUT OIDS; +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.oid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_1_tens.oid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_ptable.oid" +ALTER TABLE regtest_ptable SET TABLESPACE pg_default; +-- partitioned table child +ALTER TABLE regtest_table_part ALTER p SET DEFAULT 'abcd'; -- not supported by sepgsql +ALTER TABLE regtest_table_part ALTER p SET DEFAULT 'XYZ'; -- not supported by sepgsql +ALTER TABLE regtest_table_part ALTER p DROP DEFAULT; -- not supported by sepgsql +ALTER TABLE regtest_table_part ALTER p SET NOT NULL; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +ALTER TABLE regtest_table_part ALTER p DROP NOT NULL; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +ALTER TABLE regtest_table_part ALTER p SET STATISTICS -1; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +ALTER TABLE regtest_table_part ALTER p SET (n_distinct = 999); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +ALTER TABLE regtest_table_part ALTER p SET STORAGE PLAIN; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema_2.regtest_table_part.p" +ALTER TABLE regtest_table_part ADD CONSTRAINT test_ck CHECK (p like '%abc%') NOT VALID; -- not supported by sepgsql +ALTER TABLE regtest_table_part VALIDATE CONSTRAINT test_ck; -- not supported by sepgsql +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +ALTER TABLE regtest_table_part DROP CONSTRAINT test_ck; -- not supported by sepgsql +CREATE TRIGGER regtest_part_test_trig BEFORE UPDATE ON regtest_table_part + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); +ALTER TABLE regtest_table_part DISABLE TRIGGER regtest_part_test_trig; -- not supported by sepgsql +ALTER TABLE regtest_table_part ENABLE TRIGGER regtest_part_test_trig; -- not supported by sepgsql +ALTER TABLE regtest_table_part SET (fillfactor = 75); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_table_part" +ALTER TABLE regtest_table_part RESET (fillfactor); +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_table_part" +ALTER TABLE regtest_table_part SET TABLESPACE pg_default; +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema_2.regtest_table_part" +--- ALTER VIEW regtest_view SET (security_barrier); LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema_2.regtest_view" ALTER SEQUENCE regtest_seq INCREMENT BY 10 START WITH 1000; @@ -205,13 +314,15 @@ RESET sepgsql.debug_audit; RESET client_min_messages; DROP DATABASE sepgsql_test_regression; DROP SCHEMA regtest_schema CASCADE; -NOTICE: drop cascades to 3 other objects +NOTICE: drop cascades to 4 other objects DETAIL: drop cascades to table regtest_table_2 drop cascades to table regtest_table_3 drop cascades to constraint test_fk on table regtest_table +drop cascades to table regtest_ptable_1_tens DROP SCHEMA regtest_schema_2 CASCADE; -NOTICE: drop cascades to 4 other objects +NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to table regtest_table +drop cascades to table regtest_ptable drop cascades to sequence regtest_seq drop cascades to view regtest_view drop cascades to function regtest_func(text) diff --git a/contrib/sepgsql/expected/ddl.out b/contrib/sepgsql/expected/ddl.out index c04b72fe88..1c0409a7a6 100644 --- a/contrib/sepgsql/expected/ddl.out +++ b/contrib/sepgsql/expected/ddl.out @@ -76,14 +76,62 @@ LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_reg LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_2.oid" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_2.ctid" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_2.a" +CREATE TABLE regtest_ptable (a int) PARTITION BY RANGE (a); +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: CREATE TABLE regtest_ptable (a int) PARTITION BY RANGE (a); + ^ +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.a" +CREATE TABLE regtest_ptable_ones PARTITION OF regtest_ptable FOR VALUES FROM ('0') TO ('10'); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_ones" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.a" +CREATE TABLE regtest_ptable_tens PARTITION OF regtest_ptable FOR VALUES FROM ('10') TO ('100'); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_tens" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.a" +ALTER TABLE regtest_ptable ADD COLUMN q int; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: ALTER TABLE regtest_ptable ADD COLUMN q int; + ^ +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.q" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.q" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.q" -- corresponding toast table should not have label and permission checks ALTER TABLE regtest_table_2 ADD COLUMN b text; LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_2.b" -- VACUUM FULL internally create a new table and swap them later. VACUUM FULL regtest_table; +VACUUM FULL regtest_ptable; CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100; LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_view" +CREATE VIEW regtest_pview AS SELECT * FROM regtest_ptable WHERE a < 99; +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_pview" CREATE SEQUENCE regtest_seq; LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_seq" @@ -132,9 +180,57 @@ LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_reg LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_table_3_y_seq" +CREATE TABLE regtest_ptable_3 (o int, p serial) PARTITION BY RANGE (o); +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: CREATE TABLE regtest_ptable_3 (o int, p serial) PARTITION BY... + ^ +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_ptable_3_p_seq" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_3" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.o" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.p" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_ptable_3_p_seq" +CREATE TABLE regtest_ptable_3_ones PARTITION OF regtest_ptable_3 FOR VALUES FROM ('0') to ('10'); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_3_ones" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.o" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.p" +CREATE TABLE regtest_ptable_3_tens PARTITION OF regtest_ptable_3 FOR VALUES FROM ('10') to ('100'); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_3_tens" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.o" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.p" CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y; LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_view_2" +CREATE VIEW regtest_pview_2 AS SELECT * FROM regtest_ptable_3 WHERE o < p; +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_pview_2" CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql AS 'BEGIN RETURN $1 * $1 < 100; END'; LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" @@ -187,6 +283,7 @@ ALTER TABLE regtest_table_4 ALTER COLUMN y TYPE float; LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_4.y" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.float8(integer)" DROP INDEX regtest_index_tbl4_y; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_table_4" @@ -212,6 +309,91 @@ LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regte LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_4.x" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_4.y" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_4.z" +-- For partitioned tables +CREATE TABLE regtest_ptable_4 (x int, y int, z int) PARTITION BY RANGE (x); +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: CREATE TABLE regtest_ptable_4 (x int, y int, z int) PARTITIO... + ^ +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: CREATE TABLE regtest_ptable_4 (x int, y int, z int) PARTITIO... + ^ +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LINE 1: CREATE TABLE regtest_ptable_4 (x int, y int, z int) PARTITIO... + ^ +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.x" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.y" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.z" +CREATE TABLE regtest_ptable_4_ones PARTITION OF regtest_ptable_4 FOR VALUES FROM ('0') TO ('10'); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.tableoid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.cmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.xmax" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.cmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.xmin" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.ctid" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.x" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.y" +LOG: SELinux: allowed { create } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.z" +CREATE INDEX regtest_pindex_tbl4_y ON regtest_ptable_4_ones(y); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +CREATE INDEX regtest_pindex_tbl4_z ON regtest_ptable_4_ones(z); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +ALTER TABLE regtest_ptable_4 ALTER COLUMN y TYPE float; +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.y" +LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="pg_catalog" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.y" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.float8(integer)" +DROP INDEX regtest_pindex_tbl4_y; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +ALTER TABLE regtest_ptable_4_ones + ADD CONSTRAINT regtest_ptbl4_con EXCLUDE USING btree (z WITH =); +LOG: SELinux: allowed { add_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +DROP TABLE regtest_ptable_4 CASCADE; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { setattr } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4_ones" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.x" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.y" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4_ones.z" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_4" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.x" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.y" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_4.z" -- -- DROP Permission checks (with clean-up) -- @@ -233,6 +415,13 @@ ALTER TABLE regtest_table DROP COLUMN y; LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table.y" ALTER TABLE regtest_table_2 SET WITHOUT OIDS; LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table_2.oid" +ALTER TABLE regtest_ptable DROP COLUMN q CASCADE; +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.q" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.q" +NOTICE: drop cascades to view regtest_pview +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_pview" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.q" DROP TABLE regtest_table; LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_table_x_seq" @@ -248,14 +437,76 @@ LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regte LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table.ctid" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table.x" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_table.z" +DROP TABLE regtest_ptable CASCADE; +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_tens" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_tens.a" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_ones" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_ones.a" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable.a" DROP OWNED BY regress_sepgsql_test_user; LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { search } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=system_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="public" LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="regtest_schema.regtest_func_2(integer)" LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_pview_2" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_view_t:s0 tclass=db_view name="regtest_schema.regtest_view_2" LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_3_tens" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.o" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_tens.p" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_3_ones" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.o" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3_ones.p" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_ptable_3_p_seq" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_ptable_3" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.tableoid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.cmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.xmax" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.cmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.xmin" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.ctid" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.o" +LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="regtest_schema.regtest_ptable_3.p" +LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_seq_t:s0 tclass=db_sequence name="regtest_schema.regtest_table_3_y_seq" LOG: SELinux: allowed { remove_name } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_schema_t:s0 tclass=db_schema name="regtest_schema" LOG: SELinux: allowed { drop } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="regtest_schema.regtest_table_3" diff --git a/contrib/sepgsql/expected/dml.out b/contrib/sepgsql/expected/dml.out index 8716ac735d..31243c723b 100644 --- a/contrib/sepgsql/expected/dml.out +++ b/contrib/sepgsql/expected/dml.out @@ -21,6 +21,23 @@ SECURITY LABEL ON TABLE t5 IS 'system_u:object_r:sepgsql_table_t:s0'; SECURITY LABEL ON COLUMN t5.e IS 'system_u:object_r:sepgsql_table_t:s0'; SECURITY LABEL ON COLUMN t5.f IS 'system_u:object_r:sepgsql_ro_table_t:s0'; SECURITY LABEL ON COLUMN t5.g IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +--- +-- partitioned table parent +CREATE TABLE t1p (o int, p text, q text) PARTITION BY RANGE (o); +SECURITY LABEL ON TABLE t1p IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p.o IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p.p IS 'system_u:object_r:sepgsql_ro_table_t:s0'; +SECURITY LABEL ON COLUMN t1p.q IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +-- partitioned table children +CREATE TABLE t1p_ones PARTITION OF t1p FOR VALUES FROM ('0') TO ('10'); +SECURITY LABEL ON COLUMN t1p_ones.o IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_ones.p IS 'system_u:object_r:sepgsql_ro_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_ones.q IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +CREATE TABLE t1p_tens PARTITION OF t1p FOR VALUES FROM ('10') TO ('100'); +SECURITY LABEL ON COLUMN t1p_tens.o IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_tens.p IS 'system_u:object_r:sepgsql_ro_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_tens.q IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +--- CREATE TABLE customer (cid int primary key, cname text, ccredit text); SECURITY LABEL ON COLUMN customer.ccredit IS 'system_u:object_r:sepgsql_secret_table_t:s0'; INSERT INTO customer VALUES (1, 'Taro', '1111-2222-3333-4444'), @@ -33,24 +50,44 @@ SECURITY LABEL ON FUNCTION customer_credit(int) SELECT objtype, objname, label FROM pg_seclabels WHERE provider = 'selinux' AND objtype in ('table', 'column') - AND objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g') -ORDER BY objname; - objtype | objname | label ----------+---------+--------------------------------------------- - table | t1 | system_u:object_r:sepgsql_table_t:s0 - table | t2 | system_u:object_r:sepgsql_ro_table_t:s0 - table | t3 | system_u:object_r:sepgsql_fixed_table_t:s0 - table | t4 | system_u:object_r:sepgsql_secret_table_t:s0 - table | t5 | system_u:object_r:sepgsql_table_t:s0 - column | t5.e | system_u:object_r:sepgsql_table_t:s0 - column | t5.f | system_u:object_r:sepgsql_ro_table_t:s0 - column | t5.g | system_u:object_r:sepgsql_secret_table_t:s0 -(8 rows) + AND objname in ('t1', 't2', 't3', 't4', + 't5', 't5.e', 't5.f', 't5.g', + 't1p', 't1p.o', 't1p.p', 't1p.q', + 't1p_ones', 't1p_ones.o', 't1p_ones.p', 't1p_ones.q', + 't1p_tens', 't1p_tens.o', 't1p_tens.p', 't1p_tens.q') +ORDER BY objname COLLATE "C"; + objtype | objname | label +---------+------------+--------------------------------------------- + table | t1 | system_u:object_r:sepgsql_table_t:s0 + table | t1p | system_u:object_r:sepgsql_table_t:s0 + column | t1p.o | system_u:object_r:sepgsql_table_t:s0 + column | t1p.p | system_u:object_r:sepgsql_ro_table_t:s0 + column | t1p.q | system_u:object_r:sepgsql_secret_table_t:s0 + table | t1p_ones | unconfined_u:object_r:sepgsql_table_t:s0 + column | t1p_ones.o | system_u:object_r:sepgsql_table_t:s0 + column | t1p_ones.p | system_u:object_r:sepgsql_ro_table_t:s0 + column | t1p_ones.q | system_u:object_r:sepgsql_secret_table_t:s0 + table | t1p_tens | unconfined_u:object_r:sepgsql_table_t:s0 + column | t1p_tens.o | system_u:object_r:sepgsql_table_t:s0 + column | t1p_tens.p | system_u:object_r:sepgsql_ro_table_t:s0 + column | t1p_tens.q | system_u:object_r:sepgsql_secret_table_t:s0 + table | t2 | system_u:object_r:sepgsql_ro_table_t:s0 + table | t3 | system_u:object_r:sepgsql_fixed_table_t:s0 + table | t4 | system_u:object_r:sepgsql_secret_table_t:s0 + table | t5 | system_u:object_r:sepgsql_table_t:s0 + column | t5.e | system_u:object_r:sepgsql_table_t:s0 + column | t5.f | system_u:object_r:sepgsql_ro_table_t:s0 + column | t5.g | system_u:object_r:sepgsql_secret_table_t:s0 +(20 rows) CREATE SCHEMA my_schema_1; CREATE TABLE my_schema_1.ts1 (a int, b text); +CREATE TABLE my_schema_1.pts1 (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE my_schema_1.pts1_ones PARTITION OF my_schema_1.pts1 FOR VALUES FROM ('0') to ('10'); CREATE SCHEMA my_schema_2; CREATE TABLE my_schema_2.ts2 (x int, y text); +CREATE TABLE my_schema_2.pts2 (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE my_schema_2.pts2_tens PARTITION OF my_schema_2.pts2 FOR VALUES FROM ('10') to ('100'); SECURITY LABEL ON SCHEMA my_schema_2 IS 'system_u:object_r:sepgsql_regtest_invisible_schema_t:s0'; -- Hardwired Rules @@ -99,7 +136,42 @@ SELECT e,f FROM t5; -- ok ---+--- (0 rows) -SELECT * FROM customer; -- failed +--- +-- partitioned table parent +SELECT * FROM t1p; -- failed +ERROR: SELinux: security policy violation +SELECT o,p FROM t1p; -- ok + o | p +---+--- +(0 rows) + +--partitioned table children +SELECT * FROM t1p_ones; -- failed +ERROR: SELinux: security policy violation +SELECT o FROM t1p_ones; -- ok + o +--- +(0 rows) + +SELECT o,p FROM t1p_ones; -- ok + o | p +---+--- +(0 rows) + +SELECT * FROM t1p_tens; -- failed +ERROR: SELinux: security policy violation +SELECT o FROM t1p_tens; -- ok + o +--- +(0 rows) + +SELECT o,p FROM t1p_tens; -- ok + o | p +---+--- +(0 rows) + +--- +SELECT * FROM customer; -- failed ERROR: SELinux: security policy violation SELECT cid, cname, customer_credit(cid) FROM customer; -- ok cid | cname | customer_credit @@ -108,7 +180,7 @@ SELECT cid, cname, customer_credit(cid) FROM customer; -- ok 2 | Hanako | 5555-6666-7777-???? (2 rows) -SELECT count(*) FROM t5; -- ok +SELECT count(*) FROM t5; -- ok count ------- 0 @@ -116,6 +188,34 @@ SELECT count(*) FROM t5; -- ok SELECT count(*) FROM t5 WHERE g IS NULL; -- failed ERROR: SELinux: security policy violation +--- +-- partitioned table parent +SELECT count(*) FROM t1p; -- ok + count +------- + 0 +(1 row) + +SELECT count(*) FROM t1p WHERE q IS NULL; -- failed +ERROR: SELinux: security policy violation +-- partitioned table children +SELECT count(*) FROM t1p_ones; -- ok + count +------- + 0 +(1 row) + +SELECT count(*) FROM t1p_ones WHERE q IS NULL; -- failed +ERROR: SELinux: security policy violation +SELECT count(*) FROM t1p_tens; -- ok + count +------- + 0 +(1 row) + +SELECT count(*) FROM t1p_tens WHERE q IS NULL; -- failed +ERROR: SELinux: security policy violation +--- INSERT INTO t1 VALUES (4, 'abc'); -- ok INSERT INTO t2 VALUES (4, 'xyz'); -- failed ERROR: SELinux: security policy violation @@ -127,6 +227,22 @@ ERROR: SELinux: security policy violation INSERT INTO t5 (e,f) VALUES ('abc', 'def'); -- failed ERROR: SELinux: security policy violation INSERT INTO t5 (e) VALUES ('abc'); -- ok +--- +-- partitioned table parent +INSERT INTO t1p (o,p) VALUES (9, 'mno'); -- failed +ERROR: SELinux: security policy violation +INSERT INTO t1p (o) VALUES (9); -- ok +INSERT INTO t1p (o,p) VALUES (99, 'pqr'); -- failed +ERROR: SELinux: security policy violation +INSERT INTO t1p (o) VALUES (99); -- ok +-- partitioned table children +INSERT INTO t1p_ones (o,p) VALUES (9, 'mno'); -- failed +ERROR: SELinux: security policy violation +INSERT INTO t1p_ones (o) VALUES (9); -- ok +INSERT INTO t1p_tens (o,p) VALUES (99, 'pqr'); -- failed +ERROR: SELinux: security policy violation +INSERT INTO t1p_tens (o) VALUES (99); -- ok +--- UPDATE t1 SET b = b || '_upd'; -- ok UPDATE t2 SET y = y || '_upd'; -- failed ERROR: SELinux: security policy violation @@ -138,6 +254,23 @@ UPDATE t5 SET e = 'xyz'; -- ok UPDATE t5 SET e = f || '_upd'; -- ok UPDATE t5 SET e = g || '_upd'; -- failed ERROR: SELinux: security policy violation +--- +-- partitioned table parent +UPDATE t1p SET o = 9 WHERE o < 10; -- ok +UPDATE t1p SET o = 99 WHERE o >= 10; -- ok +UPDATE t1p SET o = ascii(COALESCE(p,'upd'))%10 WHERE o < 10; -- ok +UPDATE t1p SET o = ascii(COALESCE(q,'upd'))%100 WHERE o >= 10; -- failed +ERROR: SELinux: security policy violation +-- partitioned table children +UPDATE t1p_ones SET o = 9; -- ok +UPDATE t1p_ones SET o = ascii(COALESCE(p,'upd'))%10; -- ok +UPDATE t1p_ones SET o = ascii(COALESCE(q,'upd'))%10; -- failed +ERROR: SELinux: security policy violation +UPDATE t1p_tens SET o = 99; -- ok +UPDATE t1p_tens SET o = ascii(COALESCE(p,'upd'))%100; -- ok +UPDATE t1p_tens SET o = ascii(COALESCE(q,'upd'))%100; -- failed +ERROR: SELinux: security policy violation +--- DELETE FROM t1; -- ok DELETE FROM t2; -- failed ERROR: SELinux: security policy violation @@ -149,6 +282,20 @@ DELETE FROM t5; -- ok DELETE FROM t5 WHERE f IS NULL; -- ok DELETE FROM t5 WHERE g IS NULL; -- failed ERROR: SELinux: security policy violation +--- +-- partitioned table parent +DELETE FROM t1p; -- ok +DELETE FROM t1p WHERE p IS NULL; -- ok +DELETE FROM t1p WHERE q IS NULL; -- failed +ERROR: SELinux: security policy violation +-- partitioned table children +DELETE FROM t1p_ones WHERE p IS NULL; -- ok +DELETE FROM t1p_ones WHERE q IS NULL; -- failed; +ERROR: SELinux: security policy violation +DELETE FROM t1p_tens WHERE p IS NULL; -- ok +DELETE FROM t1p_tens WHERE q IS NULL; -- failed +ERROR: SELinux: security policy violation +--- -- -- COPY TO/FROM statements -- @@ -160,6 +307,19 @@ ERROR: SELinux: security policy violation COPY t5 TO '/dev/null'; -- failed ERROR: SELinux: security policy violation COPY t5(e,f) TO '/dev/null'; -- ok +--- +-- partitioned table parent +COPY (SELECT * FROM t1p) TO '/dev/null'; -- failed +ERROR: SELinux: security policy violation +COPY (SELECT (o,p) FROM t1p) TO '/dev/null'; -- ok +-- partitioned table children +COPY t1p_ones TO '/dev/null'; -- failed +ERROR: SELinux: security policy violation +COPY t1p_ones(o,p) TO '/dev/null'; -- ok +COPY t1p_tens TO '/dev/null'; -- failed +ERROR: SELinux: security policy violation +COPY t1p_tens(o,p) TO '/dev/null'; -- ok +--- COPY t1 FROM '/dev/null'; -- ok COPY t2 FROM '/dev/null'; -- failed ERROR: SELinux: security policy violation @@ -171,6 +331,19 @@ ERROR: SELinux: security policy violation COPY t5 (e,f) FROM '/dev/null'; -- failed ERROR: SELinux: security policy violation COPY t5 (e) FROM '/dev/null'; -- ok +--- +-- partitioned table parent +COPY t1p FROM '/dev/null'; -- failed +ERROR: SELinux: security policy violation +COPY t1p (o) FROM '/dev/null'; -- ok +-- partitioned table children +COPY t1p_ones FROM '/dev/null'; -- failed +ERROR: SELinux: security policy violation +COPY t1p_ones (o) FROM '/dev/null'; -- ok +COPY t1p_tens FROM '/dev/null'; -- failed +ERROR: SELinux: security policy violation +COPY t1p_tens (o) FROM '/dev/null'; -- ok +--- -- -- Schema search path -- @@ -202,8 +375,13 @@ DROP TABLE IF EXISTS t2 CASCADE; DROP TABLE IF EXISTS t3 CASCADE; DROP TABLE IF EXISTS t4 CASCADE; DROP TABLE IF EXISTS t5 CASCADE; +DROP TABLE IF EXISTS t1p CASCADE; DROP TABLE IF EXISTS customer CASCADE; DROP SCHEMA IF EXISTS my_schema_1 CASCADE; -NOTICE: drop cascades to table my_schema_1.ts1 +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table my_schema_1.ts1 +drop cascades to table my_schema_1.pts1 DROP SCHEMA IF EXISTS my_schema_2 CASCADE; -NOTICE: drop cascades to table my_schema_2.ts2 +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table my_schema_2.ts2 +drop cascades to table my_schema_2.pts2 diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out index 7af51897da..0300bc6fb4 100644 --- a/contrib/sepgsql/expected/label.out +++ b/contrib/sepgsql/expected/label.out @@ -53,6 +53,18 @@ CREATE TABLE var_tbl(x int, y text); INSERT INTO var_tbl VALUES (2,'xxx'), (3,'yyy'), (4,'zzz'), (5,'xyz'); SECURITY LABEL ON TABLE var_tbl IS 'system_u:object_r:sepgsql_regtest_var_table_t:s0'; +CREATE TABLE foo_ptbl(o int, p text) PARTITION BY RANGE (o); +CREATE TABLE foo_ptbl_ones PARTITION OF foo_ptbl FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE foo_ptbl_tens PARTITION OF foo_ptbl FOR VALUES FROM ('10') TO ('100'); +INSERT INTO foo_ptbl VALUES (0, 'aaa'), (9,'bbb'), (10,'ccc'), (99,'ddd'); +SECURITY LABEL ON TABLE foo_ptbl + IS 'system_u:object_r:sepgsql_regtest_foo_table_t:s0'; +CREATE TABLE var_ptbl(q int, r text) PARTITION BY RANGE (q); +CREATE TABLE var_ptbl_ones PARTITION OF var_ptbl FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE var_ptbl_tens PARTITION OF var_ptbl FOR VALUES FROM ('10') TO ('100'); +INSERT INTO var_ptbl VALUES (0,'xxx'), (9,'yyy'), (10,'zzz'), (99,'xyz'); +SECURITY LABEL ON TABLE var_ptbl + IS 'system_u:object_r:sepgsql_regtest_var_table_t:s0'; -- -- Tests for default labeling behavior -- @@ -72,36 +84,90 @@ SELECT sepgsql_getcon(); -- confirm client privilege CREATE TABLE t4 (m int, n text); INSERT INTO t4 VALUES (1,'mmm'), (2,'nnn'), (3,'ooo'); +SELECT sepgsql_getcon(); -- confirm client privilege + sepgsql_getcon +----------------------------------------------------- + unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 +(1 row) + +CREATE TABLE tpart (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE tpart_ones PARTITION OF tpart FOR VALUES FROM ('0') TO ('10'); +SELECT sepgsql_getcon(); -- confirm client privilege + sepgsql_getcon +---------------------------------------------------- + unconfined_u:unconfined_r:sepgsql_regtest_dba_t:s0 +(1 row) + +CREATE TABLE tpart_tens PARTITION OF tpart FOR VALUES FROM ('10') TO ('100'); +INSERT INTO tpart VALUES (0, 'aaa'); +INSERT INTO tpart VALUES (9, 'bbb'); +INSERT INTO tpart VALUES (99, 'ccc'); SELECT objtype, objname, label FROM pg_seclabels - WHERE provider = 'selinux' AND objtype = 'table' AND objname in ('t1', 't2', 't3'); - objtype | objname | label ----------+---------+----------------------------------------------- - table | t1 | unconfined_u:object_r:sepgsql_table_t:s0 - table | t2 | unconfined_u:object_r:sepgsql_table_t:s0 - table | t3 | unconfined_u:object_r:user_sepgsql_table_t:s0 -(3 rows) + WHERE provider = 'selinux' AND objtype = 'table' AND objname in ('t1', 't2', 't3', + 'tpart', + 'tpart_ones', + 'tpart_tens') + ORDER BY objname COLLATE "C" ASC; + objtype | objname | label +---------+------------+----------------------------------------------- + table | t1 | unconfined_u:object_r:sepgsql_table_t:s0 + table | t2 | unconfined_u:object_r:sepgsql_table_t:s0 + table | t3 | unconfined_u:object_r:user_sepgsql_table_t:s0 + table | tpart | unconfined_u:object_r:user_sepgsql_table_t:s0 + table | tpart_ones | unconfined_u:object_r:user_sepgsql_table_t:s0 + table | tpart_tens | unconfined_u:object_r:sepgsql_table_t:s0 +(6 rows) SELECT objtype, objname, label FROM pg_seclabels - WHERE provider = 'selinux' AND objtype = 'column' AND (objname like 't3.%' OR objname like 't4.%'); - objtype | objname | label ----------+-------------+----------------------------------------------- - column | t3.t | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.s | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.ctid | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.xmin | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.cmin | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.xmax | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.cmax | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t3.tableoid | unconfined_u:object_r:user_sepgsql_table_t:s0 - column | t4.n | unconfined_u:object_r:sepgsql_table_t:s0 - column | t4.m | unconfined_u:object_r:sepgsql_table_t:s0 - column | t4.ctid | unconfined_u:object_r:sepgsql_sysobj_t:s0 - column | t4.xmin | unconfined_u:object_r:sepgsql_sysobj_t:s0 - column | t4.cmin | unconfined_u:object_r:sepgsql_sysobj_t:s0 - column | t4.xmax | unconfined_u:object_r:sepgsql_sysobj_t:s0 - column | t4.cmax | unconfined_u:object_r:sepgsql_sysobj_t:s0 - column | t4.tableoid | unconfined_u:object_r:sepgsql_sysobj_t:s0 -(16 rows) + WHERE provider = 'selinux' AND objtype = 'column' AND (objname like 't3.%' + OR objname like 't4.%' + OR objname like 'tpart.%' + OR objname like 'tpart_ones.%' + OR objname like 'tpart_tens.%') + ORDER BY objname COLLATE "C" ASC; + objtype | objname | label +---------+---------------------+----------------------------------------------- + column | t3.cmax | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.cmin | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.ctid | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.s | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.t | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.tableoid | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.xmax | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t3.xmin | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | t4.cmax | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | t4.cmin | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | t4.ctid | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | t4.m | unconfined_u:object_r:sepgsql_table_t:s0 + column | t4.n | unconfined_u:object_r:sepgsql_table_t:s0 + column | t4.tableoid | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | t4.xmax | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | t4.xmin | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | tpart.cmax | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.cmin | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.ctid | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.o | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.p | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.tableoid | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.xmax | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart.xmin | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.cmax | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.cmin | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.ctid | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.o | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.p | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.tableoid | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.xmax | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_ones.xmin | unconfined_u:object_r:user_sepgsql_table_t:s0 + column | tpart_tens.cmax | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | tpart_tens.cmin | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | tpart_tens.ctid | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | tpart_tens.o | unconfined_u:object_r:sepgsql_table_t:s0 + column | tpart_tens.p | unconfined_u:object_r:sepgsql_table_t:s0 + column | tpart_tens.tableoid | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | tpart_tens.xmax | unconfined_u:object_r:sepgsql_sysobj_t:s0 + column | tpart_tens.xmin | unconfined_u:object_r:sepgsql_sysobj_t:s0 +(40 rows) -- -- Tests for SECURITY LABEL @@ -122,6 +188,16 @@ SECURITY LABEL ON COLUMN t2 ERROR: column name must be qualified SECURITY LABEL ON COLUMN t2.b IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok +SECURITY LABEL ON TABLE tpart + IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok +SECURITY LABEL ON TABLE tpart + IS 'invalid security context'; -- failed +ERROR: SELinux: invalid security label: "invalid security context" +SECURITY LABEL ON COLUMN tpart + IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- failed +ERROR: column name must be qualified +SECURITY LABEL ON COLUMN tpart.o + IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok -- -- Tests for Trusted Procedures -- @@ -422,8 +498,19 @@ SELECT * FROM foo_tbl; -- OK 4 | ddd (4 rows) +SELECT * FROM foo_ptbl; -- OK + o | p +----+----- + 0 | aaa + 9 | bbb + 10 | ccc + 99 | ddd +(4 rows) + SELECT * FROM var_tbl; -- failed ERROR: SELinux: security policy violation +SELECT * FROM var_ptbl; -- failed +ERROR: SELinux: security policy violation SELECT * FROM auth_tbl; -- failed ERROR: SELinux: security policy violation SELECT sepgsql_setcon(NULL); -- end of session @@ -438,11 +525,15 @@ SELECT sepgsql_getcon(); unconfined_u:unconfined_r:sepgsql_regtest_pool_t:s0 (1 row) --- the pooler cannot touch these tables directry +-- the pooler cannot touch these tables directly SELECT * FROM foo_tbl; -- failed ERROR: SELinux: security policy violation +SELECT * FROM foo_ptbl; -- failed +ERROR: SELinux: security policy violation SELECT * FROM var_tbl; -- failed ERROR: SELinux: security policy violation +SELECT * FROM var_ptbl; -- failed +ERROR: SELinux: security policy violation -- switch to "var" SELECT auth_func('var', 'b2145aac704ce76dbe1ac7adac535b23'); auth_func @@ -458,6 +549,8 @@ SELECT sepgsql_getcon(); SELECT * FROM foo_tbl; -- failed ERROR: SELinux: security policy violation +SELECT * FROM foo_ptbl; -- failed +ERROR: SELinux: security policy violation SELECT * FROM var_tbl; -- OK x | y ---+----- @@ -467,6 +560,15 @@ SELECT * FROM var_tbl; -- OK 5 | xyz (4 rows) +SELECT * FROM var_ptbl; -- OK + q | r +----+----- + 0 | xxx + 9 | yyy + 10 | zzz + 99 | xyz +(4 rows) + SELECT * FROM auth_tbl; -- failed ERROR: SELinux: security policy violation SELECT sepgsql_setcon(NULL); -- end of session @@ -501,6 +603,7 @@ DROP TABLE IF EXISTS t1 CASCADE; DROP TABLE IF EXISTS t2 CASCADE; DROP TABLE IF EXISTS t3 CASCADE; DROP TABLE IF EXISTS t4 CASCADE; +DROP TABLE IF EXISTS tpart CASCADE; DROP FUNCTION IF EXISTS f1() CASCADE; DROP FUNCTION IF EXISTS f2() CASCADE; DROP FUNCTION IF EXISTS f3() CASCADE; diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out index 1ce47c48b0..7b55142653 100644 --- a/contrib/sepgsql/expected/misc.out +++ b/contrib/sepgsql/expected/misc.out @@ -8,6 +8,10 @@ ERROR: SELinux: LOAD is not permitted -- CREATE TABLE t1 (x int, y text); INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x); +CREATE TABLE t1p (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE t1p_ones PARTITION OF t1p FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE t1p_tens PARTITION OF t1p FOR VALUES FROM ('10') TO ('100'); +INSERT INTO t1p (SELECT x, md5(x::text) FROM generate_series(0,99) x); SET sepgsql.debug_audit = on; SET client_min_messages = log; -- regular function and operators @@ -27,6 +31,57 @@ LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_re 100 | f899139df5e1059396431415e770c6dd (6 rows) +SELECT * FROM t1p WHERE o > 50 AND p like '%64%'; +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4eq(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4le(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4eq(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4le(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4le(integer,integer)" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p column p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column p" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4gt(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" + o | p +----+---------------------------------- + 77 | 28dd2c7955ce926456240b2ff0100bde + 89 | 7647966b7343c29048673252e490f736 + 90 | 8613985ec49eb8f757ae6439e879bb2a + 91 | 54229abfcfa5649e7003b83dd4755294 + 99 | ac627ab1ccbdb62ec96e702f07f6425b +(5 rows) + +SELECT * FROM t1p_ones WHERE o > 50 AND p like '%64%'; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column p" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4gt(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" + o | p +---+--- +(0 rows) + +SELECT * FROM t1p_tens WHERE o > 50 AND p like '%64%'; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column p" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4gt(integer,integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" + o | p +----+---------------------------------- + 77 | 28dd2c7955ce926456240b2ff0100bde + 89 | 7647966b7343c29048673252e490f736 + 90 | 8613985ec49eb8f757ae6439e879bb2a + 91 | 54229abfcfa5649e7003b83dd4755294 + 99 | ac627ab1ccbdb62ec96e702f07f6425b +(5 rows) + -- aggregate function SELECT MIN(x), AVG(x) FROM t1; LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1" @@ -41,13 +96,56 @@ LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_re 1 | 50.5000000000000000 (1 row) +SELECT MIN(o), AVG(o) FROM t1p; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column o" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" + min | avg +-----+--------------------- + 0 | 49.5000000000000000 +(1 row) + +SELECT MIN(o), AVG(o) FROM t1p_ones; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column o" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" + min | avg +-----+-------------------- + 0 | 4.5000000000000000 +(1 row) + +SELECT MIN(o), AVG(o) FROM t1p_tens; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column o" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.avg(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4_avg_accum(bigint[],integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int8_avg(bigint[])" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.min(integer)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.int4smaller(integer,integer)" + min | avg +-----+--------------------- + 10 | 54.5000000000000000 +(1 row) + -- window function SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%'; LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column x" LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1 column y" -LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.row_number()" LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.row_number()" row_number | x | y ------------+----+---------------------------------- 1 | 2 | c81e728d9d4c2f636f067f89cc14862c @@ -64,9 +162,71 @@ LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_re 12 | 94 | f4b9ec30ad9f68f89b29639786cb62ef (12 rows) +SELECT row_number() OVER (order by o), * FROM t1p WHERE p like '%86%'; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p column p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column p" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column p" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.row_number()" + row_number | o | p +------------+----+---------------------------------- + 1 | 2 | c81e728d9d4c2f636f067f89cc14862c + 2 | 17 | 70efdf2ec9b086079795c442636b55fb + 3 | 22 | b6d767d2f8ed5d21a44b0e5886680cb9 + 4 | 27 | 02e74f10e0327ad868d138f2b4fdd6f0 + 5 | 33 | 182be0c5cdcd5072bb1864cdee4d3d6e + 6 | 43 | 17e62166fc8586dfa4d1bc0e1742c08b + 7 | 54 | a684eceee76fc522773286a895bc8436 + 8 | 73 | d2ddea18f00665ce8623e36bd4e3c7c5 + 9 | 76 | fbd7939d674997cdb4692d34de8633c4 + 10 | 89 | 7647966b7343c29048673252e490f736 + 11 | 90 | 8613985ec49eb8f757ae6439e879bb2a + 12 | 94 | f4b9ec30ad9f68f89b29639786cb62ef +(12 rows) + +SELECT row_number() OVER (order by o), * FROM t1p_ones WHERE p like '%86%'; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_ones" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_ones column p" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.row_number()" + row_number | o | p +------------+---+---------------------------------- + 1 | 2 | c81e728d9d4c2f636f067f89cc14862c +(1 row) + +SELECT row_number() OVER (order by o), * FROM t1p_tens WHERE p like '%86%'; +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_table name="public.t1p_tens" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column o" +LOG: SELinux: allowed { select } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column name="table t1p_tens column p" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.textlike(pg_catalog.text,pg_catalog.text)" +LOG: SELinux: allowed { execute } scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0-s0:c0.c255 tcontext=system_u:object_r:sepgsql_proc_exec_t:s0 tclass=db_procedure name="pg_catalog.row_number()" + row_number | o | p +------------+----+---------------------------------- + 1 | 17 | 70efdf2ec9b086079795c442636b55fb + 2 | 22 | b6d767d2f8ed5d21a44b0e5886680cb9 + 3 | 27 | 02e74f10e0327ad868d138f2b4fdd6f0 + 4 | 33 | 182be0c5cdcd5072bb1864cdee4d3d6e + 5 | 43 | 17e62166fc8586dfa4d1bc0e1742c08b + 6 | 54 | a684eceee76fc522773286a895bc8436 + 7 | 73 | d2ddea18f00665ce8623e36bd4e3c7c5 + 8 | 76 | fbd7939d674997cdb4692d34de8633c4 + 9 | 89 | 7647966b7343c29048673252e490f736 + 10 | 90 | 8613985ec49eb8f757ae6439e879bb2a + 11 | 94 | f4b9ec30ad9f68f89b29639786cb62ef +(11 rows) + RESET sepgsql.debug_audit; RESET client_min_messages; -- -- Cleanup -- DROP TABLE IF EXISTS t1 CASCADE; +DROP TABLE IF EXISTS t1p CASCADE; diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c index 4119322238..dadf99e74b 100644 --- a/contrib/sepgsql/hooks.c +++ b/contrib/sepgsql/hooks.c @@ -4,7 +4,7 @@ * * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks. * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "miscadmin.h" #include "tcop/utility.h" #include "utils/guc.h" +#include "utils/queryenvironment.h" #include "sepgsql.h" @@ -297,16 +298,18 @@ sepgsql_exec_check_perms(List *rangeTabls, bool abort) * break whole of the things if nefarious user would use. */ static void -sepgsql_utility_command(Node *parsetree, +sepgsql_utility_command(PlannedStmt *pstmt, const char *queryString, ProcessUtilityContext context, ParamListInfo params, + QueryEnvironment *queryEnv, DestReceiver *dest, #ifdef PGXC bool sentToRemote, #endif /* PGXC */ char *completionTag) { + Node *parsetree = pstmt->utilityStmt; sepgsql_context_info_t saved_context_info = sepgsql_context_info; ListCell *cell; @@ -365,20 +368,16 @@ sepgsql_utility_command(Node *parsetree, } if (next_ProcessUtility_hook) - (*next_ProcessUtility_hook) (parsetree, queryString, - context, params, + (*next_ProcessUtility_hook) (pstmt, queryString, + context, params, queryEnv, dest, -#ifdef PGXC sentToRemote, -#endif completionTag); else - standard_ProcessUtility(parsetree, queryString, - context, params, + standard_ProcessUtility(pstmt, queryString, + context, params, queryEnv, dest, -#ifdef PGXC sentToRemote, -#endif completionTag); } PG_CATCH(); diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c index e12a0e8cb6..6239800189 100644 --- a/contrib/sepgsql/label.c +++ b/contrib/sepgsql/label.c @@ -4,12 +4,22 @@ * * Routines to support SELinux labels (security context) * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ #include "postgres.h" +#include <selinux/label.h> + +/* + * <selinux/label.h> includes <stdbool.h>, which creates an incompatible + * #define for bool. Get rid of that so we can use our own typedef. + * (We don't care if <stdbool.h> redefines "true"/"false"; those are close + * enough.) + */ +#undef bool + #include "access/heapam.h" #include "access/htup_details.h" #include "access/genam.h" @@ -37,8 +47,6 @@ #include "sepgsql.h" -#include <selinux/label.h> - /* * Saved hook entries (if stacked) */ @@ -590,7 +598,7 @@ PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in); Datum sepgsql_mcstrans_in(PG_FUNCTION_ARGS) { - text *label = PG_GETARG_TEXT_P(0); + text *label = PG_GETARG_TEXT_PP(0); char *raw_label; char *result; @@ -630,7 +638,7 @@ PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out); Datum sepgsql_mcstrans_out(PG_FUNCTION_ARGS) { - text *label = PG_GETARG_TEXT_P(0); + text *label = PG_GETARG_TEXT_PP(0); char *qual_label; char *result; @@ -779,7 +787,8 @@ exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId) case RelationRelationId: relForm = (Form_pg_class) GETSTRUCT(tuple); - if (relForm->relkind == RELKIND_RELATION) + if (relForm->relkind == RELKIND_RELATION || + relForm->relkind == RELKIND_PARTITIONED_TABLE) objtype = SELABEL_DB_TABLE; else if (relForm->relkind == RELKIND_SEQUENCE) objtype = SELABEL_DB_SEQUENCE; @@ -803,7 +812,8 @@ exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId) case AttributeRelationId: attForm = (Form_pg_attribute) GETSTRUCT(tuple); - if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION) + if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION && + get_rel_relkind(attForm->attrelid) != RELKIND_PARTITIONED_TABLE) continue; /* no need to assign security label */ objtype = SELABEL_DB_COLUMN; diff --git a/contrib/sepgsql/launcher b/contrib/sepgsql/launcher index eb827a0e0a..0fc96ea0d4 100755 --- a/contrib/sepgsql/launcher +++ b/contrib/sepgsql/launcher @@ -2,7 +2,7 @@ # # A wrapper script to launch psql command in regression test # -# Copyright (c) 2010-2016, PostgreSQL Global Development Group +# Copyright (c) 2010-2017, PostgreSQL Global Development Group # # ------------------------------------------------------------------------- diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c index 9cc21f05b5..4ccf4a5e60 100644 --- a/contrib/sepgsql/proc.c +++ b/contrib/sepgsql/proc.c @@ -4,7 +4,7 @@ * * Routines corresponding to procedure objects * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c index 1f1ab04d39..59a6d9be6e 100644 --- a/contrib/sepgsql/relation.c +++ b/contrib/sepgsql/relation.c @@ -4,7 +4,7 @@ * * Routines corresponding to relation/attribute objects * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ @@ -54,12 +54,13 @@ sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum) ObjectAddress object; Form_pg_attribute attForm; StringInfoData audit_name; + char relkind = get_rel_relkind(relOid); /* - * Only attributes within regular relation have individual security - * labels. + * Only attributes within regular relations or partition relations have + * individual security labels. */ - if (get_rel_relkind(relOid) != RELKIND_RELATION) + if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) return; /* @@ -135,8 +136,9 @@ sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) { ObjectAddress object; char *audit_name; + char relkind = get_rel_relkind(relOid); - if (get_rel_relkind(relOid) != RELKIND_RELATION) + if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) return; /* @@ -167,8 +169,9 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, { ObjectAddress object; char *audit_name; + char relkind = get_rel_relkind(relOid); - if (get_rel_relkind(relOid) != RELKIND_RELATION) + if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot set security label on non-regular columns"))); @@ -209,8 +212,9 @@ sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum) { ObjectAddress object; char *audit_name; + char relkind = get_rel_relkind(relOid); - if (get_rel_relkind(relOid) != RELKIND_RELATION) + if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE) return; /* @@ -243,7 +247,7 @@ sepgsql_relation_post_create(Oid relOid) HeapTuple tuple; Form_pg_class classForm; ObjectAddress object; - uint16 tclass; + uint16_t tclass; char *scontext; /* subject */ char *tcontext; /* schema */ char *rcontext; /* relation */ @@ -291,6 +295,7 @@ sepgsql_relation_post_create(Oid relOid) switch (classForm->relkind) { case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: @@ -333,7 +338,8 @@ sepgsql_relation_post_create(Oid relOid) true); /* - * Assign the default security label on the new relation + * Assign the default security label on the new regular or partitioned + * relation. */ object.classId = RelationRelationId; object.objectId = relOid; @@ -341,10 +347,10 @@ sepgsql_relation_post_create(Oid relOid) SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext); /* - * We also assigns a default security label on columns of the new regular - * tables. + * We also assign a default security label on columns of a new table. */ - if (classForm->relkind == RELKIND_RELATION) + if (classForm->relkind == RELKIND_RELATION || + classForm->relkind == RELKIND_PARTITIONED_TABLE) { Relation arel; ScanKeyData akey; @@ -413,13 +419,13 @@ sepgsql_relation_drop(Oid relOid) { ObjectAddress object; char *audit_name; - uint16_t tclass; - char relkind; + uint16_t tclass = 0; + char relkind = get_rel_relkind(relOid); - relkind = get_rel_relkind(relOid); switch (relkind) { case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: @@ -479,7 +485,7 @@ sepgsql_relation_drop(Oid relOid) /* * check db_column:{drop} permission */ - if (relkind == RELKIND_RELATION) + if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE) { Form_pg_attribute attForm; CatCList *attrList; @@ -521,11 +527,10 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel) { ObjectAddress object; char *audit_name; - char relkind; + char relkind = get_rel_relkind(relOid); uint16_t tclass = 0; - relkind = get_rel_relkind(relOid); - if (relkind == RELKIND_RELATION) + if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE) tclass = SEPG_CLASS_DB_TABLE; else if (relkind == RELKIND_SEQUENCE) tclass = SEPG_CLASS_DB_SEQUENCE; @@ -585,6 +590,7 @@ sepgsql_relation_setattr(Oid relOid) switch (get_rel_relkind(relOid)) { case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: tclass = SEPG_CLASS_DB_TABLE; break; case RELKIND_SEQUENCE: diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c index 5285fc5452..940384bf40 100644 --- a/contrib/sepgsql/schema.c +++ b/contrib/sepgsql/schema.c @@ -4,7 +4,7 @@ * * Routines corresponding to schema objects * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c index b0b9fadede..7728a18333 100644 --- a/contrib/sepgsql/selinux.c +++ b/contrib/sepgsql/selinux.c @@ -5,7 +5,7 @@ * Interactions between userspace and selinux in kernelspace, * using libselinux api. * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ * When we ask SELinux whether the required privileges are allowed or not, * we use security_compute_av(3). It needs us to represent object classes * and access vectors using 'external' codes defined in the security policy. - * It is determinded in the runtime, not build time. So, it needs an internal + * It is determined in the runtime, not build time. So, it needs an internal * service to translate object class/access vectors which we want to check * into the code which kernel want to be given. */ diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h index c080716f8f..9d245c2780 100644 --- a/contrib/sepgsql/sepgsql.h +++ b/contrib/sepgsql/sepgsql.h @@ -4,7 +4,7 @@ * * Definitions corresponding to SE-PostgreSQL * - * Copyright (c) 2010-2016, PostgreSQL Global Development Group + * Copyright (c) 2010-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ @@ -276,12 +276,6 @@ extern char *sepgsql_get_label(Oid relOid, Oid objOid, int32 subId); extern void sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel); -extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); -extern Datum sepgsql_setcon(PG_FUNCTION_ARGS); -extern Datum sepgsql_mcstrans_in(PG_FUNCTION_ARGS); -extern Datum sepgsql_mcstrans_out(PG_FUNCTION_ARGS); -extern Datum sepgsql_restorecon(PG_FUNCTION_ARGS); - /* * dml.c */ diff --git a/contrib/sepgsql/sql/alter.sql b/contrib/sepgsql/sql/alter.sql index 0bd35279fa..14000eaaee 100644 --- a/contrib/sepgsql/sql/alter.sql +++ b/contrib/sepgsql/sql/alter.sql @@ -32,6 +32,15 @@ CREATE TABLE regtest_table_2 (c text) inherits (regtest_table_1); CREATE TABLE regtest_table_3 (x int primary key, y text); +--- +-- partitioned table parent +CREATE TABLE regtest_ptable_1 (o int, p text) PARTITION BY RANGE (o); + +-- partitioned table children +CREATE TABLE regtest_ptable_1_ones PARTITION OF regtest_ptable_1 FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE regtest_ptable_1_tens PARTITION OF regtest_ptable_1 FOR VALUES FROM ('10') TO ('100'); +--- + CREATE SEQUENCE regtest_seq_1; CREATE VIEW regtest_view_1 AS SELECT * FROM regtest_table_1 WHERE a > 0; @@ -55,6 +64,8 @@ ALTER SCHEMA regtest_schema_1 OWNER TO regress_sepgsql_test_user; ALTER SCHEMA regtest_schema_1 OWNER TO regress_sepgsql_test_user; ALTER TABLE regtest_table_1 OWNER TO regress_sepgsql_test_user; ALTER TABLE regtest_table_1 OWNER TO regress_sepgsql_test_user; +ALTER TABLE regtest_ptable_1 OWNER TO regress_sepgsql_test_user; +ALTER TABLE regtest_ptable_1_ones OWNER TO regress_sepgsql_test_user; ALTER SEQUENCE regtest_seq_1 OWNER TO regress_sepgsql_test_user; ALTER SEQUENCE regtest_seq_1 OWNER TO regress_sepgsql_test_user; ALTER VIEW regtest_view_1 OWNER TO regress_sepgsql_test_user; @@ -66,6 +77,8 @@ ALTER FUNCTION regtest_func_1(text) OWNER TO regress_sepgsql_test_user; -- ALTER xxx SET SCHEMA -- ALTER TABLE regtest_table_1 SET SCHEMA regtest_schema_2; +ALTER TABLE regtest_ptable_1 SET SCHEMA regtest_schema_2; +ALTER TABLE regtest_ptable_1_ones SET SCHEMA regtest_schema_2; ALTER SEQUENCE regtest_seq_1 SET SCHEMA regtest_schema_2; ALTER VIEW regtest_view_1 SET SCHEMA regtest_schema_2; ALTER FUNCTION regtest_func_1(text) SET SCHEMA regtest_schema_2; @@ -76,6 +89,14 @@ ALTER FUNCTION regtest_func_1(text) SET SCHEMA regtest_schema_2; ALTER DATABASE sepgsql_test_regression_1 RENAME TO sepgsql_test_regression; ALTER SCHEMA regtest_schema_1 RENAME TO regtest_schema; ALTER TABLE regtest_table_1 RENAME TO regtest_table; + +--- +-- partitioned table parent +ALTER TABLE regtest_ptable_1 RENAME TO regtest_ptable; +-- partitioned table child +ALTER TABLE regtest_ptable_1_ones RENAME TO regtest_table_part; +--- + ALTER SEQUENCE regtest_seq_1 RENAME TO regtest_seq; ALTER VIEW regtest_view_1 RENAME TO regtest_view; ALTER FUNCTION regtest_func_1(text) RENAME TO regtest_func; @@ -121,6 +142,50 @@ ALTER TABLE regtest_table_2 NO INHERIT regtest_table; -- not supported ALTER TABLE regtest_table_2 INHERIT regtest_table; -- not supported ALTER TABLE regtest_table SET TABLESPACE pg_default; +--- +-- partitioned table parent +ALTER TABLE regtest_ptable ADD COLUMN d float; +ALTER TABLE regtest_ptable DROP COLUMN d; +ALTER TABLE regtest_ptable ALTER p SET DEFAULT 'abcd'; -- not supported by sepgsql +ALTER TABLE regtest_ptable ALTER p SET DEFAULT 'XYZ'; -- not supported by sepgsql +ALTER TABLE regtest_ptable ALTER p DROP DEFAULT; -- not supported by sepgsql +ALTER TABLE regtest_ptable ALTER p SET NOT NULL; +ALTER TABLE regtest_ptable ALTER p DROP NOT NULL; +ALTER TABLE regtest_ptable ALTER p SET STATISTICS -1; +ALTER TABLE regtest_ptable ALTER p SET (n_distinct = 999); +ALTER TABLE regtest_ptable ALTER p SET STORAGE PLAIN; +ALTER TABLE regtest_ptable ADD CONSTRAINT test_ck CHECK (p like '%abc%') NOT VALID; -- not supported by sepgsql +ALTER TABLE regtest_ptable DROP CONSTRAINT test_ck; -- not supported by sepgsql + +ALTER TABLE regtest_ptable SET WITH OIDS; +ALTER TABLE regtest_ptable SET WITHOUT OIDS; +ALTER TABLE regtest_ptable SET TABLESPACE pg_default; + +-- partitioned table child +ALTER TABLE regtest_table_part ALTER p SET DEFAULT 'abcd'; -- not supported by sepgsql +ALTER TABLE regtest_table_part ALTER p SET DEFAULT 'XYZ'; -- not supported by sepgsql +ALTER TABLE regtest_table_part ALTER p DROP DEFAULT; -- not supported by sepgsql +ALTER TABLE regtest_table_part ALTER p SET NOT NULL; +ALTER TABLE regtest_table_part ALTER p DROP NOT NULL; +ALTER TABLE regtest_table_part ALTER p SET STATISTICS -1; +ALTER TABLE regtest_table_part ALTER p SET (n_distinct = 999); +ALTER TABLE regtest_table_part ALTER p SET STORAGE PLAIN; +ALTER TABLE regtest_table_part ADD CONSTRAINT test_ck CHECK (p like '%abc%') NOT VALID; -- not supported by sepgsql +ALTER TABLE regtest_table_part VALIDATE CONSTRAINT test_ck; -- not supported by sepgsql +ALTER TABLE regtest_table_part DROP CONSTRAINT test_ck; -- not supported by sepgsql + +CREATE TRIGGER regtest_part_test_trig BEFORE UPDATE ON regtest_table_part + FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); + +ALTER TABLE regtest_table_part DISABLE TRIGGER regtest_part_test_trig; -- not supported by sepgsql +ALTER TABLE regtest_table_part ENABLE TRIGGER regtest_part_test_trig; -- not supported by sepgsql + +ALTER TABLE regtest_table_part SET (fillfactor = 75); +ALTER TABLE regtest_table_part RESET (fillfactor); + +ALTER TABLE regtest_table_part SET TABLESPACE pg_default; +--- + ALTER VIEW regtest_view SET (security_barrier); ALTER SEQUENCE regtest_seq INCREMENT BY 10 START WITH 1000; diff --git a/contrib/sepgsql/sql/ddl.sql b/contrib/sepgsql/sql/ddl.sql index 2fc66e4c37..ae431f6cd2 100644 --- a/contrib/sepgsql/sql/ddl.sql +++ b/contrib/sepgsql/sql/ddl.sql @@ -32,13 +32,21 @@ ALTER TABLE regtest_table ADD COLUMN z int; CREATE TABLE regtest_table_2 (a int) WITH OIDS; +CREATE TABLE regtest_ptable (a int) PARTITION BY RANGE (a); +CREATE TABLE regtest_ptable_ones PARTITION OF regtest_ptable FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE regtest_ptable_tens PARTITION OF regtest_ptable FOR VALUES FROM ('10') TO ('100'); + +ALTER TABLE regtest_ptable ADD COLUMN q int; + -- corresponding toast table should not have label and permission checks ALTER TABLE regtest_table_2 ADD COLUMN b text; -- VACUUM FULL internally create a new table and swap them later. VACUUM FULL regtest_table; +VACUUM FULL regtest_ptable; CREATE VIEW regtest_view AS SELECT * FROM regtest_table WHERE x < 100; +CREATE VIEW regtest_pview AS SELECT * FROM regtest_ptable WHERE a < 99; CREATE SEQUENCE regtest_seq; @@ -57,8 +65,12 @@ SET SESSION AUTHORIZATION regress_sepgsql_test_user; SET search_path = regtest_schema, public; CREATE TABLE regtest_table_3 (x int, y serial); +CREATE TABLE regtest_ptable_3 (o int, p serial) PARTITION BY RANGE (o); +CREATE TABLE regtest_ptable_3_ones PARTITION OF regtest_ptable_3 FOR VALUES FROM ('0') to ('10'); +CREATE TABLE regtest_ptable_3_tens PARTITION OF regtest_ptable_3 FOR VALUES FROM ('10') to ('100'); CREATE VIEW regtest_view_2 AS SELECT * FROM regtest_table_3 WHERE x < y; +CREATE VIEW regtest_pview_2 AS SELECT * FROM regtest_ptable_3 WHERE o < p; CREATE FUNCTION regtest_func_2(int) RETURNS bool LANGUAGE plpgsql AS 'BEGIN RETURN $1 * $1 < 100; END'; @@ -77,6 +89,18 @@ ALTER TABLE regtest_table_4 ADD CONSTRAINT regtest_tbl4_con EXCLUDE USING btree (z WITH =); DROP TABLE regtest_table_4 CASCADE; +-- For partitioned tables +CREATE TABLE regtest_ptable_4 (x int, y int, z int) PARTITION BY RANGE (x); +CREATE TABLE regtest_ptable_4_ones PARTITION OF regtest_ptable_4 FOR VALUES FROM ('0') TO ('10'); + +CREATE INDEX regtest_pindex_tbl4_y ON regtest_ptable_4_ones(y); +CREATE INDEX regtest_pindex_tbl4_z ON regtest_ptable_4_ones(z); +ALTER TABLE regtest_ptable_4 ALTER COLUMN y TYPE float; +DROP INDEX regtest_pindex_tbl4_y; +ALTER TABLE regtest_ptable_4_ones + ADD CONSTRAINT regtest_ptbl4_con EXCLUDE USING btree (z WITH =); +DROP TABLE regtest_ptable_4 CASCADE; + -- -- DROP Permission checks (with clean-up) -- @@ -90,7 +114,10 @@ DROP VIEW regtest_view; ALTER TABLE regtest_table DROP COLUMN y; ALTER TABLE regtest_table_2 SET WITHOUT OIDS; +ALTER TABLE regtest_ptable DROP COLUMN q CASCADE; + DROP TABLE regtest_table; +DROP TABLE regtest_ptable CASCADE; DROP OWNED BY regress_sepgsql_test_user; diff --git a/contrib/sepgsql/sql/dml.sql b/contrib/sepgsql/sql/dml.sql index 7a64b9e213..19201f4b90 100644 --- a/contrib/sepgsql/sql/dml.sql +++ b/contrib/sepgsql/sql/dml.sql @@ -27,6 +27,26 @@ SECURITY LABEL ON COLUMN t5.e IS 'system_u:object_r:sepgsql_table_t:s0'; SECURITY LABEL ON COLUMN t5.f IS 'system_u:object_r:sepgsql_ro_table_t:s0'; SECURITY LABEL ON COLUMN t5.g IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +--- +-- partitioned table parent +CREATE TABLE t1p (o int, p text, q text) PARTITION BY RANGE (o); +SECURITY LABEL ON TABLE t1p IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p.o IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p.p IS 'system_u:object_r:sepgsql_ro_table_t:s0'; +SECURITY LABEL ON COLUMN t1p.q IS 'system_u:object_r:sepgsql_secret_table_t:s0'; + +-- partitioned table children +CREATE TABLE t1p_ones PARTITION OF t1p FOR VALUES FROM ('0') TO ('10'); +SECURITY LABEL ON COLUMN t1p_ones.o IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_ones.p IS 'system_u:object_r:sepgsql_ro_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_ones.q IS 'system_u:object_r:sepgsql_secret_table_t:s0'; +CREATE TABLE t1p_tens PARTITION OF t1p FOR VALUES FROM ('10') TO ('100'); +SECURITY LABEL ON COLUMN t1p_tens.o IS 'system_u:object_r:sepgsql_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_tens.p IS 'system_u:object_r:sepgsql_ro_table_t:s0'; +SECURITY LABEL ON COLUMN t1p_tens.q IS 'system_u:object_r:sepgsql_secret_table_t:s0'; + +--- + CREATE TABLE customer (cid int primary key, cname text, ccredit text); SECURITY LABEL ON COLUMN customer.ccredit IS 'system_u:object_r:sepgsql_secret_table_t:s0'; INSERT INTO customer VALUES (1, 'Taro', '1111-2222-3333-4444'), @@ -40,13 +60,22 @@ SECURITY LABEL ON FUNCTION customer_credit(int) SELECT objtype, objname, label FROM pg_seclabels WHERE provider = 'selinux' AND objtype in ('table', 'column') - AND objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g') -ORDER BY objname; + AND objname in ('t1', 't2', 't3', 't4', + 't5', 't5.e', 't5.f', 't5.g', + 't1p', 't1p.o', 't1p.p', 't1p.q', + 't1p_ones', 't1p_ones.o', 't1p_ones.p', 't1p_ones.q', + 't1p_tens', 't1p_tens.o', 't1p_tens.p', 't1p_tens.q') +ORDER BY objname COLLATE "C"; CREATE SCHEMA my_schema_1; CREATE TABLE my_schema_1.ts1 (a int, b text); +CREATE TABLE my_schema_1.pts1 (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE my_schema_1.pts1_ones PARTITION OF my_schema_1.pts1 FOR VALUES FROM ('0') to ('10'); + CREATE SCHEMA my_schema_2; CREATE TABLE my_schema_2.ts2 (x int, y text); +CREATE TABLE my_schema_2.pts2 (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE my_schema_2.pts2_tens PARTITION OF my_schema_2.pts2 FOR VALUES FROM ('10') to ('100'); SECURITY LABEL ON SCHEMA my_schema_2 IS 'system_u:object_r:sepgsql_regtest_invisible_schema_t:s0'; @@ -67,12 +96,36 @@ SELECT * FROM t4; -- failed SELECT * FROM t5; -- failed SELECT e,f FROM t5; -- ok -SELECT * FROM customer; -- failed +--- +-- partitioned table parent +SELECT * FROM t1p; -- failed +SELECT o,p FROM t1p; -- ok +--partitioned table children +SELECT * FROM t1p_ones; -- failed +SELECT o FROM t1p_ones; -- ok +SELECT o,p FROM t1p_ones; -- ok +SELECT * FROM t1p_tens; -- failed +SELECT o FROM t1p_tens; -- ok +SELECT o,p FROM t1p_tens; -- ok +--- + +SELECT * FROM customer; -- failed SELECT cid, cname, customer_credit(cid) FROM customer; -- ok -SELECT count(*) FROM t5; -- ok +SELECT count(*) FROM t5; -- ok SELECT count(*) FROM t5 WHERE g IS NULL; -- failed +--- +-- partitioned table parent +SELECT count(*) FROM t1p; -- ok +SELECT count(*) FROM t1p WHERE q IS NULL; -- failed +-- partitioned table children +SELECT count(*) FROM t1p_ones; -- ok +SELECT count(*) FROM t1p_ones WHERE q IS NULL; -- failed +SELECT count(*) FROM t1p_tens; -- ok +SELECT count(*) FROM t1p_tens WHERE q IS NULL; -- failed +--- + INSERT INTO t1 VALUES (4, 'abc'); -- ok INSERT INTO t2 VALUES (4, 'xyz'); -- failed INSERT INTO t3 VALUES (4, 'stu'); -- ok @@ -81,6 +134,19 @@ INSERT INTO t5 VALUES (1,2,3); -- failed INSERT INTO t5 (e,f) VALUES ('abc', 'def'); -- failed INSERT INTO t5 (e) VALUES ('abc'); -- ok +--- +-- partitioned table parent +INSERT INTO t1p (o,p) VALUES (9, 'mno'); -- failed +INSERT INTO t1p (o) VALUES (9); -- ok +INSERT INTO t1p (o,p) VALUES (99, 'pqr'); -- failed +INSERT INTO t1p (o) VALUES (99); -- ok +-- partitioned table children +INSERT INTO t1p_ones (o,p) VALUES (9, 'mno'); -- failed +INSERT INTO t1p_ones (o) VALUES (9); -- ok +INSERT INTO t1p_tens (o,p) VALUES (99, 'pqr'); -- failed +INSERT INTO t1p_tens (o) VALUES (99); -- ok +--- + UPDATE t1 SET b = b || '_upd'; -- ok UPDATE t2 SET y = y || '_upd'; -- failed UPDATE t3 SET t = t || '_upd'; -- failed @@ -89,6 +155,21 @@ UPDATE t5 SET e = 'xyz'; -- ok UPDATE t5 SET e = f || '_upd'; -- ok UPDATE t5 SET e = g || '_upd'; -- failed +--- +-- partitioned table parent +UPDATE t1p SET o = 9 WHERE o < 10; -- ok +UPDATE t1p SET o = 99 WHERE o >= 10; -- ok +UPDATE t1p SET o = ascii(COALESCE(p,'upd'))%10 WHERE o < 10; -- ok +UPDATE t1p SET o = ascii(COALESCE(q,'upd'))%100 WHERE o >= 10; -- failed +-- partitioned table children +UPDATE t1p_ones SET o = 9; -- ok +UPDATE t1p_ones SET o = ascii(COALESCE(p,'upd'))%10; -- ok +UPDATE t1p_ones SET o = ascii(COALESCE(q,'upd'))%10; -- failed +UPDATE t1p_tens SET o = 99; -- ok +UPDATE t1p_tens SET o = ascii(COALESCE(p,'upd'))%100; -- ok +UPDATE t1p_tens SET o = ascii(COALESCE(q,'upd'))%100; -- failed +--- + DELETE FROM t1; -- ok DELETE FROM t2; -- failed DELETE FROM t3; -- failed @@ -97,6 +178,18 @@ DELETE FROM t5; -- ok DELETE FROM t5 WHERE f IS NULL; -- ok DELETE FROM t5 WHERE g IS NULL; -- failed +--- +-- partitioned table parent +DELETE FROM t1p; -- ok +DELETE FROM t1p WHERE p IS NULL; -- ok +DELETE FROM t1p WHERE q IS NULL; -- failed +-- partitioned table children +DELETE FROM t1p_ones WHERE p IS NULL; -- ok +DELETE FROM t1p_ones WHERE q IS NULL; -- failed; +DELETE FROM t1p_tens WHERE p IS NULL; -- ok +DELETE FROM t1p_tens WHERE q IS NULL; -- failed +--- + -- -- COPY TO/FROM statements -- @@ -107,6 +200,17 @@ COPY t4 TO '/dev/null'; -- failed COPY t5 TO '/dev/null'; -- failed COPY t5(e,f) TO '/dev/null'; -- ok +--- +-- partitioned table parent +COPY (SELECT * FROM t1p) TO '/dev/null'; -- failed +COPY (SELECT (o,p) FROM t1p) TO '/dev/null'; -- ok +-- partitioned table children +COPY t1p_ones TO '/dev/null'; -- failed +COPY t1p_ones(o,p) TO '/dev/null'; -- ok +COPY t1p_tens TO '/dev/null'; -- failed +COPY t1p_tens(o,p) TO '/dev/null'; -- ok +--- + COPY t1 FROM '/dev/null'; -- ok COPY t2 FROM '/dev/null'; -- failed COPY t3 FROM '/dev/null'; -- ok @@ -115,6 +219,17 @@ COPY t5 FROM '/dev/null'; -- failed COPY t5 (e,f) FROM '/dev/null'; -- failed COPY t5 (e) FROM '/dev/null'; -- ok +--- +-- partitioned table parent +COPY t1p FROM '/dev/null'; -- failed +COPY t1p (o) FROM '/dev/null'; -- ok +-- partitioned table children +COPY t1p_ones FROM '/dev/null'; -- failed +COPY t1p_ones (o) FROM '/dev/null'; -- ok +COPY t1p_tens FROM '/dev/null'; -- failed +COPY t1p_tens (o) FROM '/dev/null'; -- ok +--- + -- -- Schema search path -- @@ -132,6 +247,7 @@ DROP TABLE IF EXISTS t2 CASCADE; DROP TABLE IF EXISTS t3 CASCADE; DROP TABLE IF EXISTS t4 CASCADE; DROP TABLE IF EXISTS t5 CASCADE; +DROP TABLE IF EXISTS t1p CASCADE; DROP TABLE IF EXISTS customer CASCADE; DROP SCHEMA IF EXISTS my_schema_1 CASCADE; DROP SCHEMA IF EXISTS my_schema_2 CASCADE; diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql index 04085e57a4..d19c6edb4c 100644 --- a/contrib/sepgsql/sql/label.sql +++ b/contrib/sepgsql/sql/label.sql @@ -64,6 +64,22 @@ INSERT INTO var_tbl VALUES (2,'xxx'), (3,'yyy'), (4,'zzz'), (5,'xyz'); SECURITY LABEL ON TABLE var_tbl IS 'system_u:object_r:sepgsql_regtest_var_table_t:s0'; +CREATE TABLE foo_ptbl(o int, p text) PARTITION BY RANGE (o); +CREATE TABLE foo_ptbl_ones PARTITION OF foo_ptbl FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE foo_ptbl_tens PARTITION OF foo_ptbl FOR VALUES FROM ('10') TO ('100'); + +INSERT INTO foo_ptbl VALUES (0, 'aaa'), (9,'bbb'), (10,'ccc'), (99,'ddd'); +SECURITY LABEL ON TABLE foo_ptbl + IS 'system_u:object_r:sepgsql_regtest_foo_table_t:s0'; + +CREATE TABLE var_ptbl(q int, r text) PARTITION BY RANGE (q); +CREATE TABLE var_ptbl_ones PARTITION OF var_ptbl FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE var_ptbl_tens PARTITION OF var_ptbl FOR VALUES FROM ('10') TO ('100'); + +INSERT INTO var_ptbl VALUES (0,'xxx'), (9,'yyy'), (10,'zzz'), (99,'xyz'); +SECURITY LABEL ON TABLE var_ptbl + IS 'system_u:object_r:sepgsql_regtest_var_table_t:s0'; + -- -- Tests for default labeling behavior -- @@ -75,10 +91,30 @@ INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu'); CREATE TABLE t4 (m int, n text); INSERT INTO t4 VALUES (1,'mmm'), (2,'nnn'), (3,'ooo'); +-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0 +CREATE TABLE tpart (o int, p text) PARTITION BY RANGE (o); + +CREATE TABLE tpart_ones PARTITION OF tpart FOR VALUES FROM ('0') TO ('10'); +-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_dba_t:s0 +CREATE TABLE tpart_tens PARTITION OF tpart FOR VALUES FROM ('10') TO ('100'); + +INSERT INTO tpart VALUES (0, 'aaa'); +INSERT INTO tpart VALUES (9, 'bbb'); +INSERT INTO tpart VALUES (99, 'ccc'); + SELECT objtype, objname, label FROM pg_seclabels - WHERE provider = 'selinux' AND objtype = 'table' AND objname in ('t1', 't2', 't3'); + WHERE provider = 'selinux' AND objtype = 'table' AND objname in ('t1', 't2', 't3', + 'tpart', + 'tpart_ones', + 'tpart_tens') + ORDER BY objname COLLATE "C" ASC; SELECT objtype, objname, label FROM pg_seclabels - WHERE provider = 'selinux' AND objtype = 'column' AND (objname like 't3.%' OR objname like 't4.%'); + WHERE provider = 'selinux' AND objtype = 'column' AND (objname like 't3.%' + OR objname like 't4.%' + OR objname like 'tpart.%' + OR objname like 'tpart_ones.%' + OR objname like 'tpart_tens.%') + ORDER BY objname COLLATE "C" ASC; -- -- Tests for SECURITY LABEL @@ -92,6 +128,14 @@ SECURITY LABEL ON COLUMN t2 IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- be failed SECURITY LABEL ON COLUMN t2.b IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok +SECURITY LABEL ON TABLE tpart + IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok +SECURITY LABEL ON TABLE tpart + IS 'invalid security context'; -- failed +SECURITY LABEL ON COLUMN tpart + IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- failed +SECURITY LABEL ON COLUMN tpart.o + IS 'system_u:object_r:sepgsql_ro_table_t:s0'; -- ok -- -- Tests for Trusted Procedures @@ -198,18 +242,22 @@ SELECT auth_func('foo', 'acbd18db4cc2f85cedef654fccc4a4d8'); SELECT sepgsql_getcon(); SELECT * FROM foo_tbl; -- OK +SELECT * FROM foo_ptbl; -- OK SELECT * FROM var_tbl; -- failed +SELECT * FROM var_ptbl; -- failed SELECT * FROM auth_tbl; -- failed SELECT sepgsql_setcon(NULL); -- end of session SELECT sepgsql_getcon(); --- the pooler cannot touch these tables directry +-- the pooler cannot touch these tables directly SELECT * FROM foo_tbl; -- failed +SELECT * FROM foo_ptbl; -- failed SELECT * FROM var_tbl; -- failed +SELECT * FROM var_ptbl; -- failed -- switch to "var" SELECT auth_func('var', 'b2145aac704ce76dbe1ac7adac535b23'); @@ -217,8 +265,10 @@ SELECT auth_func('var', 'b2145aac704ce76dbe1ac7adac535b23'); SELECT sepgsql_getcon(); SELECT * FROM foo_tbl; -- failed +SELECT * FROM foo_ptbl; -- failed SELECT * FROM var_tbl; -- OK +SELECT * FROM var_ptbl; -- OK SELECT * FROM auth_tbl; -- failed @@ -236,6 +286,7 @@ DROP TABLE IF EXISTS t1 CASCADE; DROP TABLE IF EXISTS t2 CASCADE; DROP TABLE IF EXISTS t3 CASCADE; DROP TABLE IF EXISTS t4 CASCADE; +DROP TABLE IF EXISTS tpart CASCADE; DROP FUNCTION IF EXISTS f1() CASCADE; DROP FUNCTION IF EXISTS f2() CASCADE; DROP FUNCTION IF EXISTS f3() CASCADE; diff --git a/contrib/sepgsql/sql/misc.sql b/contrib/sepgsql/sql/misc.sql index c277711781..bd5b6e27c2 100644 --- a/contrib/sepgsql/sql/misc.sql +++ b/contrib/sepgsql/sql/misc.sql @@ -10,17 +10,31 @@ LOAD '$libdir/sepgsql'; -- failed CREATE TABLE t1 (x int, y text); INSERT INTO t1 (SELECT x, md5(x::text) FROM generate_series(1,100) x); +CREATE TABLE t1p (o int, p text) PARTITION BY RANGE (o); +CREATE TABLE t1p_ones PARTITION OF t1p FOR VALUES FROM ('0') TO ('10'); +CREATE TABLE t1p_tens PARTITION OF t1p FOR VALUES FROM ('10') TO ('100'); +INSERT INTO t1p (SELECT x, md5(x::text) FROM generate_series(0,99) x); + SET sepgsql.debug_audit = on; SET client_min_messages = log; -- regular function and operators SELECT * FROM t1 WHERE x > 50 AND y like '%64%'; +SELECT * FROM t1p WHERE o > 50 AND p like '%64%'; +SELECT * FROM t1p_ones WHERE o > 50 AND p like '%64%'; +SELECT * FROM t1p_tens WHERE o > 50 AND p like '%64%'; -- aggregate function SELECT MIN(x), AVG(x) FROM t1; +SELECT MIN(o), AVG(o) FROM t1p; +SELECT MIN(o), AVG(o) FROM t1p_ones; +SELECT MIN(o), AVG(o) FROM t1p_tens; -- window function SELECT row_number() OVER (order by x), * FROM t1 WHERE y like '%86%'; +SELECT row_number() OVER (order by o), * FROM t1p WHERE p like '%86%'; +SELECT row_number() OVER (order by o), * FROM t1p_ones WHERE p like '%86%'; +SELECT row_number() OVER (order by o), * FROM t1p_tens WHERE p like '%86%'; RESET sepgsql.debug_audit; RESET client_min_messages; @@ -28,3 +42,4 @@ RESET client_min_messages; -- Cleanup -- DROP TABLE IF EXISTS t1 CASCADE; +DROP TABLE IF EXISTS t1p CASCADE; diff --git a/contrib/sepgsql/uavc.c b/contrib/sepgsql/uavc.c index 10fa9a0b0b..6fd58c7e42 100644 --- a/contrib/sepgsql/uavc.c +++ b/contrib/sepgsql/uavc.c @@ -6,7 +6,7 @@ * access control decisions recently used, and reduce number of kernel * invocations to avoid unnecessary performance hit. * - * Copyright (c) 2011-2016, PostgreSQL Global Development Group + * Copyright (c) 2011-2017, PostgreSQL Global Development Group * * ------------------------------------------------------------------------- */ @@ -498,13 +498,11 @@ sepgsql_avc_init(void) int rc; /* - * All the avc stuff shall be allocated on avc_mem_cxt + * All the avc stuff shall be allocated in avc_mem_cxt */ avc_mem_cxt = AllocSetContextCreate(TopMemoryContext, "userspace access vector cache", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); memset(avc_slots, 0, sizeof(avc_slots)); avc_num_caches = 0; avc_lru_hint = 0; diff --git a/contrib/spi/autoinc.c b/contrib/spi/autoinc.c index 41eae4fdc4..8bf742230e 100644 --- a/contrib/spi/autoinc.c +++ b/contrib/spi/autoinc.c @@ -3,6 +3,7 @@ */ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/pg_type.h" #include "commands/sequence.h" #include "commands/trigger.h" @@ -23,6 +24,7 @@ autoinc(PG_FUNCTION_ARGS) int *chattrs; /* attnums of attributes to change */ int chnattrs = 0; /* # of above */ Datum *newvals; /* vals of above */ + bool *newnulls; /* null flags for above */ char **args; /* arguments */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ @@ -64,6 +66,7 @@ autoinc(PG_FUNCTION_ARGS) chattrs = (int *) palloc(nargs / 2 * sizeof(int)); newvals = (Datum *) palloc(nargs / 2 * sizeof(Datum)); + newnulls = (bool *) palloc(nargs / 2 * sizeof(bool)); for (i = 0; i < nargs;) { @@ -71,7 +74,7 @@ autoinc(PG_FUNCTION_ARGS) int32 val; Datum seqname; - if (attnum < 0) + if (attnum <= 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("\"%s\" has no attribute \"%s\"", @@ -102,23 +105,23 @@ autoinc(PG_FUNCTION_ARGS) newvals[chnattrs] = DirectFunctionCall1(nextval, seqname); newvals[chnattrs] = Int32GetDatum((int32) DatumGetInt64(newvals[chnattrs])); } - pfree(DatumGetTextP(seqname)); + newnulls[chnattrs] = false; + pfree(DatumGetTextPP(seqname)); chnattrs++; i++; } if (chnattrs > 0) { - rettuple = SPI_modifytuple(rel, rettuple, chnattrs, chattrs, newvals, NULL); - if (rettuple == NULL) - /* internal error */ - elog(ERROR, "autoinc (%s): %d returned by SPI_modifytuple", - relname, SPI_result); + rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc, + chnattrs, chattrs, + newvals, newnulls); } pfree(relname); pfree(chattrs); pfree(newvals); + pfree(newnulls); return PointerGetDatum(rettuple); } diff --git a/contrib/spi/insert_username.c b/contrib/spi/insert_username.c index 3812525c4c..a2e1747ff7 100644 --- a/contrib/spi/insert_username.c +++ b/contrib/spi/insert_username.c @@ -1,6 +1,4 @@ /* - * insert_username.c - * $Modified: Thu Oct 16 08:13:42 1997 by brook $ * contrib/spi/insert_username.c * * insert user name in response to a trigger @@ -8,6 +6,7 @@ */ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/pg_type.h" #include "commands/trigger.h" #include "executor/spi.h" @@ -26,6 +25,7 @@ insert_username(PG_FUNCTION_ARGS) Trigger *trigger; /* to get trigger name */ int nargs; /* # of arguments */ Datum newval; /* new value of column */ + bool newnull; /* null flag */ char **args; /* arguments */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ @@ -67,7 +67,7 @@ insert_username(PG_FUNCTION_ARGS) attnum = SPI_fnumber(tupdesc, args[0]); - if (attnum < 0) + if (attnum <= 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("\"%s\" has no attribute \"%s\"", relname, args[0]))); @@ -80,13 +80,11 @@ insert_username(PG_FUNCTION_ARGS) /* create fields containing name */ newval = CStringGetTextDatum(GetUserNameFromId(GetUserId(), false)); + newnull = false; /* construct new tuple */ - rettuple = SPI_modifytuple(rel, rettuple, 1, &attnum, &newval, NULL); - if (rettuple == NULL) - /* internal error */ - elog(ERROR, "insert_username (\"%s\"): %d returned by SPI_modifytuple", - relname, SPI_result); + rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc, + 1, &attnum, &newval, &newnull); pfree(relname); diff --git a/contrib/spi/moddatetime.c b/contrib/spi/moddatetime.c index c6d33b7355..70476f77c2 100644 --- a/contrib/spi/moddatetime.c +++ b/contrib/spi/moddatetime.c @@ -15,11 +15,12 @@ OH, me, I'm Terry Mackintosh <terry@terrym.com> */ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/pg_type.h" #include "executor/spi.h" #include "commands/trigger.h" +#include "utils/builtins.h" #include "utils/rel.h" -#include "utils/timestamp.h" PG_MODULE_MAGIC; @@ -34,6 +35,7 @@ moddatetime(PG_FUNCTION_ARGS) int attnum; /* positional number of field to change */ Oid atttypid; /* type OID of field to change */ Datum newdt; /* The current datetime. */ + bool newdtnull; /* null flag for it */ char **args; /* arguments */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ @@ -84,9 +86,9 @@ moddatetime(PG_FUNCTION_ARGS) /* * This is where we check to see if the field we are supposed to update - * even exists. The above function must return -1 if name not found? + * even exists. */ - if (attnum < 0) + if (attnum <= 0) ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("\"%s\" has no attribute \"%s\"", @@ -115,22 +117,13 @@ moddatetime(PG_FUNCTION_ARGS) args[0], relname))); newdt = (Datum) 0; /* keep compiler quiet */ } + newdtnull = false; -/* 1 is the number of items in the arrays attnum and newdt. - attnum is the positional number of the field to be updated. - newdt is the new datetime stamp. - NOTE that attnum and newdt are not arrays, but then a 1 element array - is not an array any more then they are. Thus, they can be considered a - one element array. -*/ - rettuple = SPI_modifytuple(rel, rettuple, 1, &attnum, &newdt, NULL); - - if (rettuple == NULL) - /* internal error */ - elog(ERROR, "moddatetime (%s): %d returned by SPI_modifytuple", - relname, SPI_result); + /* Replace the attnum'th column with newdt */ + rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc, + 1, &attnum, &newdt, &newdtnull); -/* Clean up */ + /* Clean up */ pfree(relname); return PointerGetDatum(rettuple); diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c index 01dd717522..208ff6103d 100644 --- a/contrib/spi/refint.c +++ b/contrib/spi/refint.c @@ -89,7 +89,7 @@ check_primary_key(PG_FUNCTION_ARGS) /* internal error */ elog(ERROR, "check_primary_key: cannot process DELETE events"); - /* If UPDATion the must check new Tuple, not old one */ + /* If UPDATE, then must check new Tuple, not old one */ else tuple = trigdata->tg_newtuple; @@ -135,7 +135,7 @@ check_primary_key(PG_FUNCTION_ARGS) int fnumber = SPI_fnumber(tupdesc, args[i]); /* Bad guys may give us un-existing column in CREATE TRIGGER */ - if (fnumber < 0) + if (fnumber <= 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("there is no attribute \"%s\" in relation \"%s\"", @@ -362,7 +362,7 @@ check_foreign_key(PG_FUNCTION_ARGS) int fnumber = SPI_fnumber(tupdesc, args[i]); /* Bad guys may give us un-existing column in CREATE TRIGGER */ - if (fnumber < 0) + if (fnumber <= 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("there is no attribute \"%s\" in relation \"%s\"", @@ -469,6 +469,7 @@ check_foreign_key(PG_FUNCTION_ARGS) char *type; fn = SPI_fnumber(tupdesc, args_temp[k - 1]); + Assert(fn > 0); /* already checked above */ nv = SPI_getvalue(newtuple, tupdesc, fn); type = SPI_gettype(tupdesc, fn); diff --git a/contrib/spi/timetravel.c b/contrib/spi/timetravel.c index 5a345841c6..19bf8a892c 100644 --- a/contrib/spi/timetravel.c +++ b/contrib/spi/timetravel.c @@ -11,6 +11,7 @@ #include <ctype.h> +#include "access/htup_details.h" #include "catalog/pg_type.h" #include "commands/trigger.h" #include "executor/spi.h" @@ -50,7 +51,7 @@ static EPlan *find_plan(char *ident, EPlan **eplan, int *nplans); * and stop_date eq INFINITY [ and update_user eq current user ] * and all other column values as in new tuple, and insert tuple * with old data and stop_date eq current date - * ELSE - skip updation of tuple. + * ELSE - skip updating of tuple. * 2. IF a delete affects tuple with stop_date eq INFINITY * then insert the same tuple with stop_date eq current date * [ and delete_user eq current user ] @@ -157,7 +158,7 @@ timetravel(PG_FUNCTION_ARGS) for (i = 0; i < MinAttrNum; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); - if (attnum[i] < 0) + if (attnum[i] <= 0) elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != ABSTIMEOID) elog(ERROR, "timetravel (%s): attribute %s must be of abstime type", @@ -166,7 +167,7 @@ timetravel(PG_FUNCTION_ARGS) for (; i < argc; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); - if (attnum[i] < 0) + if (attnum[i] <= 0) elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != TEXTOID) elog(ERROR, "timetravel (%s): attribute %s must be of text type", @@ -183,13 +184,13 @@ timetravel(PG_FUNCTION_ARGS) int chnattrs = 0; int chattrs[MaxAttrNum]; Datum newvals[MaxAttrNum]; - char newnulls[MaxAttrNum]; + bool newnulls[MaxAttrNum]; oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) { newvals[chnattrs] = GetCurrentAbsoluteTime(); - newnulls[chnattrs] = ' '; + newnulls[chnattrs] = false; chattrs[chnattrs] = attnum[a_time_on]; chnattrs++; } @@ -201,7 +202,7 @@ timetravel(PG_FUNCTION_ARGS) (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) >= NOEND_ABSTIME)) elog(ERROR, "timetravel (%s): %s is infinity", relname, args[a_time_on]); newvals[chnattrs] = NOEND_ABSTIME; - newnulls[chnattrs] = ' '; + newnulls[chnattrs] = false; chattrs[chnattrs] = attnum[a_time_off]; chnattrs++; } @@ -220,21 +221,23 @@ timetravel(PG_FUNCTION_ARGS) { /* clear update_user value */ newvals[chnattrs] = nulltext; - newnulls[chnattrs] = 'n'; + newnulls[chnattrs] = true; chattrs[chnattrs] = attnum[a_upd_user]; chnattrs++; /* clear delete_user value */ newvals[chnattrs] = nulltext; - newnulls[chnattrs] = 'n'; + newnulls[chnattrs] = true; chattrs[chnattrs] = attnum[a_del_user]; chnattrs++; /* set insert_user value */ newvals[chnattrs] = newuser; - newnulls[chnattrs] = ' '; + newnulls[chnattrs] = false; chattrs[chnattrs] = attnum[a_ins_user]; chnattrs++; } - rettuple = SPI_modifytuple(rel, trigtuple, chnattrs, chattrs, newvals, newnulls); + rettuple = heap_modify_tuple_by_cols(trigtuple, tupdesc, + chnattrs, chattrs, + newvals, newnulls); return PointerGetDatum(rettuple); /* end of INSERT */ } @@ -395,13 +398,11 @@ timetravel(PG_FUNCTION_ARGS) chnattrs++; } - rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls); - /* - * SPI_copytuple allocates tmptuple in upper executor context - have - * to free allocation using SPI_pfree + * Use SPI_modifytuple() here because we are inside SPI environment + * but rettuple must be allocated in caller's context. */ - /* SPI_pfree(tmptuple); */ + rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls); } else /* DELETE case */ diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c index 82a4c1bd70..42846436eb 100644 --- a/contrib/sslinfo/sslinfo.c +++ b/contrib/sslinfo/sslinfo.c @@ -225,7 +225,7 @@ PG_FUNCTION_INFO_V1(ssl_client_dn_field); Datum ssl_client_dn_field(PG_FUNCTION_ARGS) { - text *fieldname = PG_GETARG_TEXT_P(0); + text *fieldname = PG_GETARG_TEXT_PP(0); Datum result; if (!(MyProcPort->peer)) @@ -260,7 +260,7 @@ PG_FUNCTION_INFO_V1(ssl_issuer_field); Datum ssl_issuer_field(PG_FUNCTION_ARGS) { - text *fieldname = PG_GETARG_TEXT_P(0); + text *fieldname = PG_GETARG_TEXT_PP(0); Datum result; if (!(MyProcPort->peer)) @@ -402,8 +402,6 @@ ssl_extension_info(PG_FUNCTION_ARGS) MemoryContext oldcontext; SSLExtensionInfoContext *fctx; - STACK_OF(X509_EXTENSION) *ext_stack = NULL; - if (SRF_IS_FIRSTCALL()) { @@ -427,16 +425,10 @@ ssl_extension_info(PG_FUNCTION_ARGS) errmsg("function returning record called in context that cannot accept type record"))); fctx->tupdesc = BlessTupleDesc(tupdesc); - /* Get all extensions of certificate */ - if (cert && cert->cert_info) - ext_stack = cert->cert_info->extensions; - /* Set max_calls as a count of extensions in certificate */ max_calls = cert != NULL ? X509_get_ext_count(cert) : 0; - if (cert != NULL && - ext_stack != NULL && - max_calls > 0) + if (max_calls > 0) { /* got results, keep track of them */ funcctx->max_calls = max_calls; @@ -462,8 +454,6 @@ ssl_extension_info(PG_FUNCTION_ARGS) max_calls = funcctx->max_calls; fctx = funcctx->user_fctx; - ext_stack = cert->cert_info->extensions; - /* do while there are more left to send */ if (call_cntr < max_calls) { @@ -486,7 +476,7 @@ ssl_extension_info(PG_FUNCTION_ARGS) errmsg("could not create OpenSSL BIO structure"))); /* Get the extension from the certificate */ - ext = sk_X509_EXTENSION_value(ext_stack, call_cntr); + ext = X509_get_ext(cert, call_cntr); obj = X509_EXTENSION_get_object(ext); /* Get the extension name */ diff --git a/contrib/start-scripts/freebsd b/contrib/start-scripts/freebsd index 758574b427..c6ac8cd47a 100644 --- a/contrib/start-scripts/freebsd +++ b/contrib/start-scripts/freebsd @@ -28,8 +28,7 @@ PGLOG="$PGDATA/serverlog" PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # What to use to start up the postmaster. (If you want the script to wait -# until the server has started, you could use "pg_ctl start -w" here. -# But without -w, pg_ctl adds no value.) +# until the server has started, you could use "pg_ctl start" here.) DAEMON="$prefix/bin/postmaster" # What to use to shut down the postmaster @@ -48,10 +47,10 @@ case $1 in echo -n ' postgresql' ;; stop) - su -l $PGUSER -c "$PGCTL stop -D '$PGDATA' -s -m fast" + su -l $PGUSER -c "$PGCTL stop -D '$PGDATA' -s" ;; restart) - su -l $PGUSER -c "$PGCTL stop -D '$PGDATA' -s -m fast -w" + su -l $PGUSER -c "$PGCTL stop -D '$PGDATA' -s" su -l $PGUSER -c "$DAEMON -D '$PGDATA' &" >>$PGLOG 2>&1 ;; status) diff --git a/contrib/start-scripts/linux b/contrib/start-scripts/linux index 763a8064ab..44a775b030 100644 --- a/contrib/start-scripts/linux +++ b/contrib/start-scripts/linux @@ -60,8 +60,7 @@ PGLOG="$PGDATA/serverlog" PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # What to use to start up the postmaster. (If you want the script to wait -# until the server has started, you could use "pg_ctl start -w" here. -# But without -w, pg_ctl adds no value.) +# until the server has started, you could use "pg_ctl start" here.) DAEMON="$prefix/bin/postmaster" # What to use to shut down the postmaster @@ -97,21 +96,21 @@ case $1 in ;; stop) echo -n "Stopping PostgreSQL: " - su - $PGUSER -c "$PGCTL stop -D '$PGDATA' -s -m fast" + su - $PGUSER -c "$PGCTL stop -D '$PGDATA' -s" echo "ok" ;; restart) echo -n "Restarting PostgreSQL: " - su - $PGUSER -c "$PGCTL stop -D '$PGDATA' -s -m fast -w" + su - $PGUSER -c "$PGCTL stop -D '$PGDATA' -s" test -e "$PG_OOM_ADJUST_FILE" && echo "$PG_MASTER_OOM_SCORE_ADJ" > "$PG_OOM_ADJUST_FILE" su - $PGUSER -c "$DAEMON_ENV $DAEMON -D '$PGDATA' &" >>$PGLOG 2>&1 echo "ok" ;; reload) - echo -n "Reload PostgreSQL: " - su - $PGUSER -c "$PGCTL reload -D '$PGDATA' -s" - echo "ok" - ;; + echo -n "Reload PostgreSQL: " + su - $PGUSER -c "$PGCTL reload -D '$PGDATA' -s" + echo "ok" + ;; status) su - $PGUSER -c "$PGCTL status -D '$PGDATA'" ;; diff --git a/contrib/start-scripts/osx/PostgreSQL b/contrib/start-scripts/osx/PostgreSQL index 24872b0944..7ff1d0e377 100755 --- a/contrib/start-scripts/osx/PostgreSQL +++ b/contrib/start-scripts/osx/PostgreSQL @@ -29,7 +29,7 @@ # modified by Ray Aspeitia 12-03-2003 : # added log rotation script to db startup # modified StartupParameters.plist "Provides" parameter to make it easier to -# start and stop with the SystemStarter utitlity +# start and stop with the SystemStarter utility # use the below command in order to correctly start/stop/restart PG with log rotation script: # SystemStarter [start|stop|restart] PostgreSQL @@ -65,8 +65,7 @@ ROTATESEC="604800" PATH="$prefix/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin" # What to use to start up the postmaster. (If you want the script to wait -# until the server has started, you could use "pg_ctl start -w" here. -# But without -w, pg_ctl adds no value.) +# until the server has started, you could use "pg_ctl start" here.) DAEMON="$prefix/bin/postmaster" # What to use to shut down the postmaster @@ -90,15 +89,15 @@ StartService () { StopService () { ConsoleMessage "Stopping PostgreSQL database server" - sudo -u $PGUSER sh -c "$PGCTL stop -D '${PGDATA}' -s -m fast" + sudo -u $PGUSER sh -c "$PGCTL stop -D '${PGDATA}' -s" } RestartService () { if [ "${POSTGRESQL:=-NO-}" = "-YES-" ]; then ConsoleMessage "Restarting PostgreSQL database server" - # should match StopService: - sudo -u $PGUSER sh -c "$PGCTL stop -D '${PGDATA}' -s -m fast" - # should match StartService: + # should match StopService: + sudo -u $PGUSER sh -c "$PGCTL stop -D '${PGDATA}' -s" + # should match StartService: if [ "${ROTATELOGS}" = "1" ]; then sudo -u $PGUSER sh -c "${DAEMON} -D '${PGDATA}' &" 2>&1 | ${LOGUTIL} "${PGLOG}" ${ROTATESEC} & else diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c index 787c02d08f..7434ca9373 100644 --- a/contrib/tablefunc/tablefunc.c +++ b/contrib/tablefunc/tablefunc.c @@ -10,7 +10,7 @@ * And contributors: * Nabil Sayegh <postgresql@e-trolley.de> * - * Copyright (c) 2002-2016, PostgreSQL Global Development Group + * Copyright (c) 2002-2017, PostgreSQL Global Development Group * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without a written agreement diff --git a/contrib/tablefunc/tablefunc.h b/contrib/tablefunc/tablefunc.h index 3477ed823f..87fa1e5dcb 100644 --- a/contrib/tablefunc/tablefunc.h +++ b/contrib/tablefunc/tablefunc.h @@ -10,7 +10,7 @@ * And contributors: * Nabil Sayegh <postgresql@e-trolley.de> * - * Copyright (c) 2002-2016, PostgreSQL Global Development Group + * Copyright (c) 2002-2017, PostgreSQL Global Development Group * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose, without fee, and without a written agreement @@ -36,13 +36,4 @@ #include "fmgr.h" -/* - * External declarations - */ -extern Datum normal_rand(PG_FUNCTION_ARGS); -extern Datum crosstab(PG_FUNCTION_ARGS); -extern Datum crosstab_hash(PG_FUNCTION_ARGS); -extern Datum connectby_text(PG_FUNCTION_ARGS); -extern Datum connectby_text_serial(PG_FUNCTION_ARGS); - #endif /* TABLEFUNC_H */ diff --git a/contrib/tcn/tcn.c b/contrib/tcn/tcn.c index 7352b292b9..124110830f 100644 --- a/contrib/tcn/tcn.c +++ b/contrib/tcn/tcn.c @@ -3,7 +3,7 @@ * tcn.c * triggered change notification support for PostgreSQL * - * Portions Copyright (c) 2011-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 2011-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * diff --git a/contrib/test_decoding/.gitignore b/contrib/test_decoding/.gitignore index 1f95503494..b4903eba65 100644 --- a/contrib/test_decoding/.gitignore +++ b/contrib/test_decoding/.gitignore @@ -1,5 +1,6 @@ # Generated subdirectories /log/ -/isolation_output/ -/regression_output/ +/results/ +/output_iso/ /tmp_check/ +/tmp_check_iso/ diff --git a/contrib/test_decoding/Makefile b/contrib/test_decoding/Makefile index 309cb0b39a..6c18189d9d 100644 --- a/contrib/test_decoding/Makefile +++ b/contrib/test_decoding/Makefile @@ -5,7 +5,7 @@ PGFILEDESC = "test_decoding - example of a logical decoding output plugin" # Note: because we don't tell the Makefile there are any regression tests, # we have to clean those result files explicitly -EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output +EXTRA_CLEAN = $(pg_regress_clean_files) ifdef USE_PGXS PG_CONFIG = pg_config @@ -38,14 +38,12 @@ submake-test_decoding: $(MAKE) -C $(top_builddir)/contrib/test_decoding REGRESSCHECKS=ddl xact rewrite toast permissions decoding_in_xact \ - decoding_into_rel binary prepared replorigin time messages + decoding_into_rel binary prepared replorigin time messages \ + spill slot regresscheck: | submake-regress submake-test_decoding temp-install - $(MKDIR_P) regression_output $(pg_regress_check) \ --temp-config $(top_srcdir)/contrib/test_decoding/logical.conf \ - --temp-instance=./tmp_check \ - --outputdir=./regression_output \ $(REGRESSCHECKS) regresscheck-install-force: | submake-regress submake-test_decoding temp-install @@ -55,10 +53,8 @@ regresscheck-install-force: | submake-regress submake-test_decoding temp-install ISOLATIONCHECKS=mxact delayed_startup ondisk_startup concurrent_ddl_dml isolationcheck: | submake-isolation submake-test_decoding temp-install - $(MKDIR_P) isolation_output $(pg_isolation_regress_check) \ --temp-config $(top_srcdir)/contrib/test_decoding/logical.conf \ - --outputdir=./isolation_output \ $(ISOLATIONCHECKS) isolationcheck-install-force: all | submake-isolation submake-test_decoding temp-install diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index bd9b42f401..1e22c1eefc 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -58,7 +58,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d SELECT slot_name, plugin, slot_type, active, NOT catalog_xmin IS NULL AS catalog_xmin_set, xmin IS NULl AS data_xmin_not_set, - pg_xlog_location_diff(restart_lsn, '0/01000000') > 0 AS some_wal + pg_wal_lsn_diff(restart_lsn, '0/01000000') > 0 AS some_wal FROM pg_replication_slots; slot_name | plugin | slot_type | active | catalog_xmin_set | data_xmin_not_set | some_wal -----------------+---------------+-----------+--------+------------------+-------------------+---------- @@ -274,9 +274,9 @@ INSERT INTO tr_etoomuch (id, data) SELECT g.i, -g.i FROM generate_series(8000, 12000) g(i) ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data; SELECT substring(data, 1, 29), count(*) -FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') +FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') WITH ORDINALITY GROUP BY 1 -ORDER BY min(location - '0/0'); +ORDER BY min(ordinality); substring | count -------------------------------+------- BEGIN | 1 @@ -416,12 +416,12 @@ CREATE TABLE replication_metadata ( WITH (user_catalog_table = true) ; \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Modifiers | Storage | Stats target | Description -----------+---------+-------------------------------------------------------------------+----------+--------------+------------- - id | integer | not null default nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | not null | plain | | - options | text[] | | extended | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- + id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) Options: user_catalog_table=true @@ -430,12 +430,12 @@ INSERT INTO replication_metadata(relation, options) VALUES ('foo', ARRAY['a', 'b']); ALTER TABLE replication_metadata RESET (user_catalog_table); \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Modifiers | Storage | Stats target | Description -----------+---------+-------------------------------------------------------------------+----------+--------------+------------- - id | integer | not null default nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | not null | plain | | - options | text[] | | extended | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- + id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) @@ -443,12 +443,12 @@ INSERT INTO replication_metadata(relation, options) VALUES ('bar', ARRAY['a', 'b']); ALTER TABLE replication_metadata SET (user_catalog_table = true); \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Modifiers | Storage | Stats target | Description -----------+---------+-------------------------------------------------------------------+----------+--------------+------------- - id | integer | not null default nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | not null | plain | | - options | text[] | | extended | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- + id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) Options: user_catalog_table=true @@ -461,13 +461,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text; ERROR: cannot rewrite table "replication_metadata" used as a catalog table ALTER TABLE replication_metadata SET (user_catalog_table = false); \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Modifiers | Storage | Stats target | Description -----------------+---------+-------------------------------------------------------------------+----------+--------------+------------- - id | integer | not null default nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | not null | plain | | - options | text[] | | extended | | - rewritemeornot | integer | | plain | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- + id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | + rewritemeornot | integer | | | | plain | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) Options: user_catalog_table=false @@ -702,7 +702,7 @@ SELECT pg_drop_replication_slot('regression_slot'); /* check that the slot is gone */ SELECT * FROM pg_replication_slots; - slot_name | plugin | slot_type | datoid | database | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn ------------+--------+-----------+--------+----------+--------+------------+------+--------------+-------------+--------------------- + slot_name | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn +-----------+--------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+--------------------- (0 rows) diff --git a/contrib/test_decoding/expected/ondisk_startup.out b/contrib/test_decoding/expected/ondisk_startup.out index 65115c830a..c7b1f45b46 100644 --- a/contrib/test_decoding/expected/ondisk_startup.out +++ b/contrib/test_decoding/expected/ondisk_startup.out @@ -1,21 +1,30 @@ Parsed test spec with 3 sessions -starting permutation: s2txid s1init s3txid s2alter s2c s1insert s1checkpoint s1start s1insert s1alter s1insert s1start -step s2txid: BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT txid_current() IS NULL; +starting permutation: s2b s2txid s1init s3b s3txid s2alter s2c s2b s2txid s3c s2c s1insert s1checkpoint s1start s1insert s1alter s1insert s1start +step s2b: BEGIN; +step s2txid: SELECT txid_current() IS NULL; ?column? f step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); <waiting ...> -step s3txid: BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT txid_current() IS NULL; +step s3b: BEGIN; +step s3txid: SELECT txid_current() IS NULL; ?column? f step s2alter: ALTER TABLE do_write ADD COLUMN addedbys2 int; step s2c: COMMIT; +step s2b: BEGIN; +step s2txid: SELECT txid_current() IS NULL; +?column? + +f +step s3c: COMMIT; step s1init: <... completed> ?column? init +step s2c: COMMIT; step s1insert: INSERT INTO do_write DEFAULT VALUES; step s1checkpoint: CHECKPOINT; step s1start: SELECT data FROM pg_logical_slot_get_changes('isolation_slot', NULL, NULL, 'include-xids', 'false'); diff --git a/contrib/test_decoding/expected/slot.out b/contrib/test_decoding/expected/slot.out new file mode 100644 index 0000000000..9f5f8a9b76 --- /dev/null +++ b/contrib/test_decoding/expected/slot.out @@ -0,0 +1,103 @@ +-- predictability +SET synchronous_commit = on; +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_p', 'test_decoding'); + ?column? +---------- + init +(1 row) + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_t', 'test_decoding', true); + ?column? +---------- + init +(1 row) + +SELECT pg_drop_replication_slot('regression_slot_p'); + pg_drop_replication_slot +-------------------------- + +(1 row) + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_p', 'test_decoding', false); + ?column? +---------- + init +(1 row) + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_t2', 'test_decoding', true); + ?column? +---------- + init +(1 row) + +-- here we want to start a new session and wait till old one is gone +select pg_backend_pid() as oldpid \gset +\c - +SET synchronous_commit = on; +do 'declare c int = 0; +begin + while (select count(*) from pg_replication_slots where active_pid = ' + :'oldpid' + ') > 0 loop c := c + 1; perform pg_sleep(0.01); end loop; + raise log ''slot test looped % times'', c; +end'; +-- should fail because the temporary slots were dropped automatically +SELECT pg_drop_replication_slot('regression_slot_t'); +ERROR: replication slot "regression_slot_t" does not exist +SELECT pg_drop_replication_slot('regression_slot_t2'); +ERROR: replication slot "regression_slot_t2" does not exist +-- permanent slot has survived +SELECT pg_drop_replication_slot('regression_slot_p'); + pg_drop_replication_slot +-------------------------- + +(1 row) + +-- test switching between slots in a session +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot1', 'test_decoding', true); + ?column? +---------- + init +(1 row) + +CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120)); +BEGIN; +INSERT INTO replication_example(somedata, text) VALUES (1, 1); +INSERT INTO replication_example(somedata, text) VALUES (1, 2); +COMMIT; +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot2', 'test_decoding', true); + ?column? +---------- + init +(1 row) + +INSERT INTO replication_example(somedata, text) VALUES (1, 3); +SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + data +--------------------------------------------------------------------------------------------------------- + BEGIN + table public.replication_example: INSERT: id[integer]:1 somedata[integer]:1 text[character varying]:'1' + table public.replication_example: INSERT: id[integer]:2 somedata[integer]:1 text[character varying]:'2' + COMMIT + BEGIN + table public.replication_example: INSERT: id[integer]:3 somedata[integer]:1 text[character varying]:'3' + COMMIT +(7 rows) + +SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + data +--------------------------------------------------------------------------------------------------------- + BEGIN + table public.replication_example: INSERT: id[integer]:3 somedata[integer]:1 text[character varying]:'3' + COMMIT +(3 rows) + +DROP TABLE replication_example; +-- error +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot1', 'test_decoding', true); +ERROR: replication slot "regression_slot1" already exists +-- both should error as they should be dropped on error +SELECT pg_drop_replication_slot('regression_slot1'); +ERROR: replication slot "regression_slot1" does not exist +SELECT pg_drop_replication_slot('regression_slot2'); +ERROR: replication slot "regression_slot2" does not exist diff --git a/contrib/test_decoding/expected/spill.out b/contrib/test_decoding/expected/spill.out new file mode 100644 index 0000000000..10734bdb6a --- /dev/null +++ b/contrib/test_decoding/expected/spill.out @@ -0,0 +1,256 @@ +-- predictability +SET synchronous_commit = on; +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); + ?column? +---------- + init +(1 row) + +CREATE TABLE spill_test(data text); +-- consume DDL +SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + data +------ +(0 rows) + +-- spilling main xact +BEGIN; +INSERT INTO spill_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1, 5000) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------+-------+---------------------------------------------------------------------+------------------------------------------------------------------------ + 'serialize-topbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-topbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-topbig--1:5000' +(1 row) + +-- spilling subxact, nothing in main +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------+-------+---------------------------------------------------------------------+------------------------------------------------------------------------ + 'serialize-subbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subbig--1:5000' +(1 row) + +-- spilling subxact, spilling main xact +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------------+-------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------- + 'serialize-subbig-topbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--1:5000' + 'serialize-subbig-topbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--2:10000' +(2 rows) + +-- spilling subxact, non-spilling main xact +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-------------------------------+-------+---------------------------------------------------------------------------------+--------------------------------------------------------------------------------- + 'serialize-subbig-topsmall--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-topsmall--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subbig-topsmall--1:5000' + 'serialize-subbig-topsmall--2 | 1 | table public.spill_test: INSERT: data[text]:'serialize-subbig-topsmall--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-subbig-topsmall--2:5001' +(2 rows) + +-- not-spilling subxact, spilling main xact +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------------+-------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------- + 'serialize-subbig-topbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--1:5000' + 'serialize-subbig-topbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-subbig-topbig--2:10000' +(2 rows) + +-- spilling main xact, spilling subxact +BEGIN; +INSERT INTO spill_test SELECT 'serialize-topbig-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-topbig-subbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------------+-------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------- + 'serialize-topbig-subbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-topbig-subbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-topbig-subbig--1:5000' + 'serialize-topbig-subbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-topbig-subbig--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-topbig-subbig--2:10000' +(2 rows) + +-- spilling main xact, not spilling subxact +BEGIN; +INSERT INTO spill_test SELECT 'serialize-topbig-subsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-topbig-subsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +RELEASE SAVEPOINT s; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-------------------------------+-------+---------------------------------------------------------------------------------+--------------------------------------------------------------------------------- + 'serialize-topbig-subsmall--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-topbig-subsmall--1:1' | table public.spill_test: INSERT: data[text]:'serialize-topbig-subsmall--1:5000' + 'serialize-topbig-subsmall--2 | 1 | table public.spill_test: INSERT: data[text]:'serialize-topbig-subsmall--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-topbig-subsmall--2:5001' +(2 rows) + +-- spilling subxact, followed by another spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-subbig-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s1; +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-subbig-subbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s2; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------------+-------+-------------------------------------------------------------------------------+-------------------------------------------------------------------------------- + 'serialize-subbig-subbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-subbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subbig-subbig--1:5000' + 'serialize-subbig-subbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-subbig--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-subbig-subbig--2:10000' +(2 rows) + +-- spilling subxact, followed by not spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-subbig-subsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s1; +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-subbig-subsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +RELEASE SAVEPOINT s2; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-------------------------------+-------+---------------------------------------------------------------------------------+--------------------------------------------------------------------------------- + 'serialize-subbig-subsmall--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subbig-subsmall--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subbig-subsmall--1:5000' + 'serialize-subbig-subsmall--2 | 1 | table public.spill_test: INSERT: data[text]:'serialize-subbig-subsmall--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-subbig-subsmall--2:5001' +(2 rows) + +-- not spilling subxact, followed by spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-subsmall-subbig--1:'||g.i FROM generate_series(1, 1) g(i); +RELEASE SAVEPOINT s1; +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-subsmall-subbig--2:'||g.i FROM generate_series(2, 5001) g(i); +RELEASE SAVEPOINT s2; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-------------------------------+-------+------------------------------------------------------------------------------+--------------------------------------------------------------------------------- + 'serialize-subsmall-subbig--1 | 1 | table public.spill_test: INSERT: data[text]:'serialize-subsmall-subbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-subsmall-subbig--1:1' + 'serialize-subsmall-subbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-subsmall-subbig--2:2' | table public.spill_test: INSERT: data[text]:'serialize-subsmall-subbig--2:5001' +(2 rows) + +-- spilling subxact, containing another spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s2; +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +------------------------------------+-------+--------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------- + 'serialize-nested-subbig-subbig--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbig--1:5000' + 'serialize-nested-subbig-subbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbig--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbig--2:10000' +(2 rows) + +-- spilling subxact, containing a not spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +RELEASE SAVEPOINT s2; +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +--------------------------------------+-------+----------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------- + 'serialize-nested-subbig-subsmall--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subsmall--1:1' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subsmall--1:5000' + 'serialize-nested-subbig-subsmall--2 | 1 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subsmall--2:5001' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subsmall--2:5001' +(2 rows) + +-- not spilling subxact, containing a spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subsmall-subbig--1:'||g.i FROM generate_series(1, 1) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subsmall-subbig--2:'||g.i FROM generate_series(2, 5001) g(i); +RELEASE SAVEPOINT s2; +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +--------------------------------------+-------+-------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------- + 'serialize-nested-subsmall-subbig--1 | 1 | table public.spill_test: INSERT: data[text]:'serialize-nested-subsmall-subbig--1:1' | table public.spill_test: INSERT: data[text]:'serialize-nested-subsmall-subbig--1:1' + 'serialize-nested-subsmall-subbig--2 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subsmall-subbig--2:2' | table public.spill_test: INSERT: data[text]:'serialize-nested-subsmall-subbig--2:5001' +(2 rows) + +-- not spilling subxact, containing a spilling subxact that aborts and one that commits +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbigabort--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbigabort--2:'||g.i FROM generate_series(5001, 10000) g(i); +ROLLBACK TO SAVEPOINT s2; +SAVEPOINT s3; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbigabort-subbig-3:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + regexp_split_to_array | count | array_agg | array_agg +-----------------------------------------------+-------+-------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------- + 'serialize-nested-subbig-subbigabort--1 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort--1:1' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort--1:5000' + 'serialize-nested-subbig-subbigabort-subbig-3 | 5000 | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:5001' | table public.spill_test: INSERT: data[text]:'serialize-nested-subbig-subbigabort-subbig-3:10000' +(2 rows) + +DROP TABLE spill_test; +SELECT pg_drop_replication_slot('regression_slot'); + pg_drop_replication_slot +-------------------------- + +(1 row) + diff --git a/contrib/test_decoding/specs/ondisk_startup.spec b/contrib/test_decoding/specs/ondisk_startup.spec index 8223705639..12c57a813d 100644 --- a/contrib/test_decoding/specs/ondisk_startup.spec +++ b/contrib/test_decoding/specs/ondisk_startup.spec @@ -24,7 +24,8 @@ step "s1alter" { ALTER TABLE do_write ADD COLUMN addedbys1 int; } session "s2" setup { SET synchronous_commit=on; } -step "s2txid" { BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT txid_current() IS NULL; } +step "s2b" { BEGIN; } +step "s2txid" { SELECT txid_current() IS NULL; } step "s2alter" { ALTER TABLE do_write ADD COLUMN addedbys2 int; } step "s2c" { COMMIT; } @@ -32,7 +33,8 @@ step "s2c" { COMMIT; } session "s3" setup { SET synchronous_commit=on; } -step "s3txid" { BEGIN ISOLATION LEVEL REPEATABLE READ; SELECT txid_current() IS NULL; } +step "s3b" { BEGIN; } +step "s3txid" { SELECT txid_current() IS NULL; } step "s3c" { COMMIT; } # Force usage of ondisk snapshot by starting and not finishing a @@ -40,4 +42,4 @@ step "s3c" { COMMIT; } # reached. In combination with a checkpoint forcing a snapshot to be # written and a new restart point computed that'll lead to the usage # of the snapshot. -permutation "s2txid" "s1init" "s3txid" "s2alter" "s2c" "s1insert" "s1checkpoint" "s1start" "s1insert" "s1alter" "s1insert" "s1start" +permutation "s2b" "s2txid" "s1init" "s3b" "s3txid" "s2alter" "s2c" "s2b" "s2txid" "s3c" "s2c" "s1insert" "s1checkpoint" "s1start" "s1insert" "s1alter" "s1insert" "s1start" diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql index e99b2d37d9..057dae056b 100644 --- a/contrib/test_decoding/sql/ddl.sql +++ b/contrib/test_decoding/sql/ddl.sql @@ -29,7 +29,7 @@ SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_d SELECT slot_name, plugin, slot_type, active, NOT catalog_xmin IS NULL AS catalog_xmin_set, xmin IS NULl AS data_xmin_not_set, - pg_xlog_location_diff(restart_lsn, '0/01000000') > 0 AS some_wal + pg_wal_lsn_diff(restart_lsn, '0/01000000') > 0 AS some_wal FROM pg_replication_slots; /* @@ -146,9 +146,9 @@ SELECT g.i, -g.i FROM generate_series(8000, 12000) g(i) ON CONFLICT(id) DO UPDATE SET data = EXCLUDED.data; SELECT substring(data, 1, 29), count(*) -FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') +FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') WITH ORDINALITY GROUP BY 1 -ORDER BY min(location - '0/0'); +ORDER BY min(ordinality); /* * check whether we decode subtransactions correctly in relation with each diff --git a/contrib/test_decoding/sql/slot.sql b/contrib/test_decoding/sql/slot.sql new file mode 100644 index 0000000000..fa9561f54e --- /dev/null +++ b/contrib/test_decoding/sql/slot.sql @@ -0,0 +1,55 @@ +-- predictability +SET synchronous_commit = on; + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_p', 'test_decoding'); +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_t', 'test_decoding', true); + +SELECT pg_drop_replication_slot('regression_slot_p'); +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_p', 'test_decoding', false); + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot_t2', 'test_decoding', true); + +-- here we want to start a new session and wait till old one is gone +select pg_backend_pid() as oldpid \gset +\c - +SET synchronous_commit = on; + +do 'declare c int = 0; +begin + while (select count(*) from pg_replication_slots where active_pid = ' + :'oldpid' + ') > 0 loop c := c + 1; perform pg_sleep(0.01); end loop; + raise log ''slot test looped % times'', c; +end'; + +-- should fail because the temporary slots were dropped automatically +SELECT pg_drop_replication_slot('regression_slot_t'); +SELECT pg_drop_replication_slot('regression_slot_t2'); + +-- permanent slot has survived +SELECT pg_drop_replication_slot('regression_slot_p'); + +-- test switching between slots in a session +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot1', 'test_decoding', true); + +CREATE TABLE replication_example(id SERIAL PRIMARY KEY, somedata int, text varchar(120)); +BEGIN; +INSERT INTO replication_example(somedata, text) VALUES (1, 1); +INSERT INTO replication_example(somedata, text) VALUES (1, 2); +COMMIT; + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot2', 'test_decoding', true); + +INSERT INTO replication_example(somedata, text) VALUES (1, 3); + +SELECT data FROM pg_logical_slot_get_changes('regression_slot1', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); +SELECT data FROM pg_logical_slot_get_changes('regression_slot2', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + +DROP TABLE replication_example; + +-- error +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot1', 'test_decoding', true); + +-- both should error as they should be dropped on error +SELECT pg_drop_replication_slot('regression_slot1'); +SELECT pg_drop_replication_slot('regression_slot2'); diff --git a/contrib/test_decoding/sql/spill.sql b/contrib/test_decoding/sql/spill.sql new file mode 100644 index 0000000000..e638cacd3f --- /dev/null +++ b/contrib/test_decoding/sql/spill.sql @@ -0,0 +1,179 @@ +-- predictability +SET synchronous_commit = on; + +SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); + +CREATE TABLE spill_test(data text); + +-- consume DDL +SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); + +-- spilling main xact +BEGIN; +INSERT INTO spill_test SELECT 'serialize-topbig--1:'||g.i FROM generate_series(1, 5000) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, nothing in main +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, spilling main xact +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, non-spilling main xact +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- not-spilling subxact, spilling main xact +BEGIN; +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-subbig-topbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling main xact, spilling subxact +BEGIN; +INSERT INTO spill_test SELECT 'serialize-topbig-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-topbig-subbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling main xact, not spilling subxact +BEGIN; +INSERT INTO spill_test SELECT 'serialize-topbig-subsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s; +INSERT INTO spill_test SELECT 'serialize-topbig-subsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +RELEASE SAVEPOINT s; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, followed by another spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-subbig-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s1; +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-subbig-subbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s2; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, followed by not spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-subbig-subsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +RELEASE SAVEPOINT s1; +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-subbig-subsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +RELEASE SAVEPOINT s2; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4], COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- not spilling subxact, followed by spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-subsmall-subbig--1:'||g.i FROM generate_series(1, 1) g(i); +RELEASE SAVEPOINT s1; +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-subsmall-subbig--2:'||g.i FROM generate_series(2, 5001) g(i); +RELEASE SAVEPOINT s2; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, containing another spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbig--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbig--2:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s2; +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- spilling subxact, containing a not spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subsmall--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subsmall--2:'||g.i FROM generate_series(5001, 5001) g(i); +RELEASE SAVEPOINT s2; +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- not spilling subxact, containing a spilling subxact +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subsmall-subbig--1:'||g.i FROM generate_series(1, 1) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subsmall-subbig--2:'||g.i FROM generate_series(2, 5001) g(i); +RELEASE SAVEPOINT s2; +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +-- not spilling subxact, containing a spilling subxact that aborts and one that commits +BEGIN; +SAVEPOINT s1; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbigabort--1:'||g.i FROM generate_series(1, 5000) g(i); +SAVEPOINT s2; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbigabort--2:'||g.i FROM generate_series(5001, 10000) g(i); +ROLLBACK TO SAVEPOINT s2; +SAVEPOINT s3; +INSERT INTO spill_test SELECT 'serialize-nested-subbig-subbigabort-subbig-3:'||g.i FROM generate_series(5001, 10000) g(i); +RELEASE SAVEPOINT s1; +COMMIT; +SELECT (regexp_split_to_array(data, ':'))[4] COLLATE "C", COUNT(*), (array_agg(data))[1], (array_agg(data))[count(*)] +FROM pg_logical_slot_get_changes('regression_slot', NULL,NULL) WHERE data ~ 'INSERT' +GROUP BY 1 ORDER BY 1; + +DROP TABLE spill_test; + +SELECT pg_drop_replication_slot('regression_slot'); diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c index c3508f0e13..21cfd673c6 100644 --- a/contrib/test_decoding/test_decoding.c +++ b/contrib/test_decoding/test_decoding.c @@ -3,7 +3,7 @@ * test_decoding.c * example logical decoding output plugin * - * Copyright (c) 2012-2016, PostgreSQL Global Development Group + * Copyright (c) 2012-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/test_decoding/test_decoding.c @@ -102,9 +102,7 @@ pg_decode_startup(LogicalDecodingContext *ctx, OutputPluginOptions *opt, data = palloc0(sizeof(TestDecodingData)); data->context = AllocSetContextCreate(ctx->context, "text conversion context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + ALLOCSET_DEFAULT_SIZES); data->include_xids = true; data->include_timestamp = false; data->skip_empty_xacts = false; diff --git a/contrib/tsearch2/Makefile b/contrib/tsearch2/Makefile deleted file mode 100644 index 36dcedc688..0000000000 --- a/contrib/tsearch2/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# contrib/tsearch2/Makefile - -MODULES = tsearch2 - -EXTENSION = tsearch2 -DATA = tsearch2--1.0.sql tsearch2--unpackaged--1.0.sql -PGFILEDESC = "tsearch2 - backward-compatible text search functionality" - -REGRESS = tsearch2 - -ifdef USE_PGXS -PG_CONFIG = pg_config -PGXS := $(shell $(PG_CONFIG) --pgxs) -include $(PGXS) -else -subdir = contrib/tsearch2 -top_builddir = ../.. -include $(top_builddir)/src/Makefile.global -include $(top_srcdir)/contrib/contrib-global.mk -endif diff --git a/contrib/tsearch2/data/test_tsearch.data b/contrib/tsearch2/data/test_tsearch.data deleted file mode 100644 index 29a26f2428..0000000000 --- a/contrib/tsearch2/data/test_tsearch.data +++ /dev/null @@ -1,508 +0,0 @@ -\n -\n -\n -\n -\n -\n -\n -\n -\n i8 hy qo xa jl wr le l5 ja jx zf ro vw wd wa cc mm wh fn yd td l8 ec rv th oc ix ir sm y4 gh pr qg ue cx ww zv c9 zv tx eo f5 gd km b9 wb rm ym yl xj u7 xz uk iq tm ux di if uc hc ge -\n gr ty ph jh po wa iw ag wq r3 yd ow rb ip et ej yl a9 dk pu y6 su ov hf xe qe sd qr zt kp ml ea tp pg dq e3 s3 hh gn hz j7 hb qs qd v0 v4 w0 nu ee wk ez un rd sz wx e7 pn yf gh uh ki kx rb qv f1 bh sr yj ry r2 -\n q1 q8 wp w9 vs ww rq de qt wo qp sa rv mc sn u8 yl -\n hv ra sa fr qs ps 4w z5 ls wt ad wy q6 zg bd vt wa e4 ft w7 ld es yg et ic pm sw ja qv ov jm ma b3 wu wi qy ug hs wh ex rt tj en ur e2 ut gv as ui dy qy du qo gv cy lx kw xm fl x2 hd ny nu hh dt wg wh rs wb wz yy yu tj ha ak rw sw io h1 ux ku v6 wc qa rv xb s8 qd f2 zo k2 ew w4 yh yu yi -\n rs tt gp qh wt q6 lg zh vr b8 uy uu lh px jm ww qe xu fp fd rs qu ki dr fn gq gw jv oq zt 2r lc ke wg l9 x3 x5 7g vs ar e7 u2 s8 t0 av dj kl nm u2 zp gf yw ee oc tw a1 -\n qs uz wr gq q9 rl e0 pe dj a9 hp qw aw er kq pp uu pl zo wp fr r6 ej pv u5 hh av lw ko qc pn qj ez n8 wn eu tq -\n po h9 rd qs hr la u0 um me wp 0p rl mv rc ab r0 fe fj fk qn jh iy cn lb bl ln b5 ll yg yh qt qp uz od dq as gn cr qa wa cu fy zy vo xk eq vg mr ns yy t7 yi op th yo ov pv em tc hg az io s5 ct os wu lq dr mp hk si gx -\n hm k5 pw a5 qh nb q3 ql wr wt z7 oz wu wh kv q8 c3 mt mg hb a3 rz pz uo y1 rb av us ek dz q0 d3 qw j2 ls wy qq jf ng eo gl ed ix em he qt du hp jc f2 m9 qp hb l4 gy zf l6 qr dn cp x1 oh qk kk s3 hy wg zs ot wj sl oz ie e9 ay it u5 ai hm gh py hz qk ki h8 ja zu qb ei vc qj hg ev h6 yh u0 tb id -\n qg d1 bt c5 r3 iv g6 d7 rc ml gk uh yn y0 zo uh qd wh ib uo u4 om qg ql yz -\n hb a3 q5 pl yj lo qy ki sy fo rj kk zq dl wn 7a zi wn wm yr w3 tv r1 -\n ft k6 iz qn qj q2 q3 bl zd av ro wo lk tg ea ew ed y1 ia yl ic g6 po aw sc zm qn gl wq qw zr jp wt j5 gs vt qt yc rr op yw tl ye hr i8 tb uu j0 xd lz vu nl qd fu wg pf wj bt ee wh t2 tp sz um oo tg ha u4 f5 sw pq pr ju qk mh ki zb vj ob cx df hj ef cj q6 u9 tv rv o4 sy ru fq ir -\n ps ko uk tz vv um t9 uk k2 ja o6 ob -\n qs nb gh ld q7 jc sp el w0 py qx i2 qe la rl qw tu ti dq ue iv oi wa qr ed t3 fg oa of rr fv qz xn wu wq te hx -\n yb ty pq az fi qg qn la bu ji lg wg q8 mi cv rl up lg om oq ym pv in aq gg js ha on ww qr bj vn pv he b5 mh qe cc mk qt rb eu qy rw tr qo ec op sn oh e2 ao iv e4 hy dt s6 qt p1 hb ih qs wg x1 bd l1 t1 ro r9 uv wb aw gu os t0 ah e0 s0 hj pe or qj zz ql fd ks qv bq qm bg ec ry oj u8 u0 yj ru r1 yx o7 -\n z4 wr qz cg nq ir bb gb w7 e5 zc pj e9 px uo fp ts aq db q9 iy qe zv xu a9 l1 mb qw tc qu fi hw ur de e4 hk lj wo wf fi ep rl wh vh ek vp oi sv rh ay hj px aa er tv do ir -\n tr o9 gb tt pp qa qs a5 ps rf q1 kj by ub ru ox co o8 ny wp wa ws rd kk b1 zc rl rz uo ts ig fh db qm q0 bg rr fu ld lr wb en nd cw vr hy rn qr en em au p8 so oh ut hz gq wp ow be ky wj dw t1 pl er wc ot na r9 wl ou un um wx iq sc e8 sn re rr f7 hz h4 ce wz qx wx kp px tl tx ai wq hf ec 6u rz og yt ok yy yp -\n sa pp a7 qm qh of je qj lo ph wt h0 ji cg z8 2v xs zl mo ik hm on tu d8 av ot pn iv ez ja qn pq wy 7r mq qu p1 tu p6 ti ur pj uy ui qo i9 qa nj xm s1 ya fb 7j ro wn t6 wz yu iq yi go en pb aj f5 hf ug uh hk av pr wl wz im ja v9 u2 ks it br wv wn se ia o5 ox ei r2 ig aj sp -\n sa tn z8 ew uo eh g8 zt wy 27 ff uh te en pd eh hv 2e wh ty oi sw xx 2p qs mx wb q3 rl eq aa eu -\n d4 ef ta zq j2 em c0 vv wf kj dw uk ql y9 rn -\n sq nm kl w8 ur kz c1 pc y1 g4 oi jv wr zy ew by se ec yn ti gq gt rd l5 ej yp tk da qz qx ir wm on q2 to ew -\n rd gu z2 kj qk bl 6d wy nw xq iu 8t ri uc kq nx ql oa vi kd o6 -\n ra gr he wy q0 ow ti ia pb ha qr lv ms qu pu qw qr ml qt ep sv i5 of fm oe nl xh x1 xz u4 ha ao fc ug pw nh n9 qv kh vx uq w1 u0 ei if -\n q1 d2 qz zd jd qb wj nt ah mj ea ed y1 et fj qe en b8 ty iv ht fv tn tm sg jb ky ai en us tl ud iu zj ql u1 ci ru iw tw -\n fr ub h9 pd ub jk vh z6 wu wh wp 5z yt w9 w0 uy om tl rc r6 ax d7 et y2 tw dz se vf ii m3 lf b4 jf vr qw qy uf es qp en tl to ye ue ph e3 uy i0 jl pz oe qo zp wp ft ka zf qd wd kr qf l9 mm wf qx ef t3 x8 ex rg ev s8 ys it da rw al hn tc f6 fv nd nc ad fj nr x0 bx yq ti rx ok tb hx o8 dp -\n o0 jq un xu q8 wo qq gg ta oj ec az dl bl wb -\n o9 ij pq gu gp nv qk gg la q4 nw bo z8 9a iw wu q8 eh wi nt jk ut ys c1 r5 up y1 yl py oy ht gd td db qn cz qw lp re c7 dh j5 ia bz dj qr qt wd wf qi rt sv ul uz tl ta yr e4 tm sg pc jv hc hv lc xg xm br vf r8 na wl ou td wc up rj s8 e8 ir ys ii qk p0 lt ho wb x8 bv lw w1 rz ew aa rv ry gx o8 -\n tt hn gn un db fu uq qf d4 q3 pp ji lf wu bx q8 hx kb ny t5 bn hb ex yf ef yj g1 g2 to yk g3 ej sk hy dv qc gj qv sy bg wr na wy bx z0 rc rm ml ug te qp i5 ue oj s4 im oq qt gx sa gt l4 sv at v3 bq mv wd x3 80 x8 aq xk rg yp en gs us dq ak tz al tx o2 dg f9 kv or h4 jy k1 jo h8 kp lt os kh as tn eu ul tm su an tw sp -\n za yi pe sh pv y4 y5 hy th jg qy qt ke ti ue qk yy ie cq wl p0 lw mf er w5 -\n k9 bt xu kc me is o5 z9 kb gv ur rc oe sk qn ve wi mm rn eu to ue uy qa xf by t1 td t7 aw up yf pr dk cg zr sc 3d at rw ec rl st zo rn do -\n o9 z5 wy vi ya ea ee fo gf va ov ww rr wr lb ro qq vr gj nw ru ym iv s4 hu tm wo wp zs br fs wg ej du y1 yt yu e7 eb em dd pq v7 cr um ae oz 0z kc tq rw zl rt wb y9 xv tm tq di eo te gc -\n tt un qs qn a7 qh je qj k0 o1 wr q6 wy ab q9 qm wr ea er eh pi hi sc hs m6 w1 bv lo zr tn yk ep op es ve xx sb ux hg sa gq qp wd n2 zh wf xf wj y3 wl e7 os u4 on ip kn ko qp s7 ly zn ba wu u4 kh f4 zo y9 q6 oh iw tq -\n qa a4 gu a7 cp z1 he ma q7 lu dp w7 ea rc ee d8 y4 tw ez im ae bv ii qe vb zt lc lv wm ro lk qr hp re tw yv es fp as zu oe qu qi bp wg cp p7 v4 ek rd wc ar rj tj e8 od e0 pm h2 h4 in qf wu wi 19 bj rl rc ee yj et tw ep -\n gv qd kj cd t3 c3 ih ws rg mc rx lh fd g8 gh cc vw b7 qe at j7 qo ws wg oy t6 t9 go eb e8 us u5 rq oe zj jy oz cj wb be ei pm og se w4 yu xw su yx if -\n o9 ub rd hw gs z3 ql nq ru wg jc 1t kv mr zm ah dd jk w8 ej aq ig y8 pp fj li wq jj cc qr no wy wu en bx yr qy oo es fy pd tk ix ph yr sf vx pn p2 jq fs ed oy yk os ie s9 u5 ak ud gd uf kb xc u1 xm eu xw 19 wn vh w1 to ee er aa rb rn ru an r1 ei -\n se kl 7h b6 xs ym tp an ta qb gn uo pt xi cl qp qy op vr ym ri ti tl i5 e1 e4 i9 ff i5 qp jx ht ql uo en pe ku h7 iw wn w4 ey ia si -\n ql xt wi k6 ew sf eg up eh oy sq ja g9 i3 qe cv l1 qq bv w2 la eu wg ec ef oh fs tb pc xd qs nl qu fn dy oi iu yf re fc hj hk xv zn zz w1 ew -\n po al hm qk jt cd ju nm li rs w9 ev ut ea 2f r4 d6 ey im pa nu wr m4 is bc xz w3 eu tb ha ft p4 ti to hr dy af i6 iz r4 jb x7 wj xg na rf gi at pn gd re wq qz ze bo wc vz sm zo my ye u7 oh dk w5 is yx tw fe dp -\n jl za gk cm wu vq jc zc iu mb oe fo fp ic sc 2l hy qr eb p5 pf dq pa fy lc td sz oo aw u1 rj fl tz nx aq xx oz xb 55 y0 -\n uq wr lh jv ri i7 ss qo gy bt s3 u1 dy ox hg it -\n ps hr lf jx bn qq up eh ab yl pn jg ng bz gd qr yw i9 j8 zi 3v oz at hd cx oj u9 rt uz ro ov -\n sq ga ny se cj id rg r3 pk kv ee sh ek dk sz pp q0 mn az kp ei qi ry em ph p9 gw hc m0 cp ea mn yf t1 5y wx ol e6 ec u2 e7 uh uj uk av ql lw qx zr qv mw qg cq ww wb pw tu w2 mf ut gk af yo ie ob -\n hn um a6 q7 af du r4 up tp ej sk lo le m8 rp eu ei qi ky op of tp ur oj hu tb dy qu gt tf oz wc s7 e7 ua pw ax nb wx wy fj wn 18 wv es yq ok w4 uz yx yc -\n pa qg qh q4 fv qz kx q6 cp gb c6 pr eh id in qw we bk wn qq b6 qy qu es ic s1 og gn wp op qf ic ro os yp rj fj ag oc ay da fv wl qp f1 yx n7 ea w2 ly yj iq iw rm o5 -\n o9 ps d3 lp wr qc md e5 rk w0 pm gx lf ku qt qp to tc pk fb tb qi lh nt yd vt ot ra tg gd zx wx vj rq cr hm ma jp vg u8 rt ei it -\n dx dv h9 rf qf uw a8 qh uv k3 ri is yr r3 eq uu tz yn y6 qc ps jf wq xe wx lc qr j4 ku xx nb 4z sr tr uq p6 uz of i6 s1 fs pj tc hu qu hz f1 hp lj s4 qx tg yp gs ob tz ds sw pm ug hm ip ql le vl wq tb xv eq w2 yg w4 st o6 -\n qd q4 pa z6 qz ia 70 r3 mb iu es r5 gh t9 cj vz qw mb ko vt qr qt gh qo ty eb kq n1 xb ef rp ek gu rg s7 rj sn ai hg o1 uj pr jt fg v0 tq tx ww bj bm ct w1 zi rn ox iw ri -\n al rd w8 vp yd yk r0 pi po se sr qa l0 qk ir e9 hm kc rz aa w6 -\n un pq qd a8 z2 qk z5 ws bi xy qx wg wp t4 mj gv qm rg c6 w7 w9 es y1 g2 ej yz gg qc qn wq qw m9 wx qe kr 27 fp fq m7 xp 3p qr rr tr ij il eh au s1 uc fx ut qu sj j8 j9 ya nr rz wg wh eg x8 sl t7 yu vf ay ds ap re dh qg qh qj hz qk zz qx k3 cy iq ox qv eu nx n6 6r lq n0 y0 uq tb sy iw fm an -\n yv dc qs gm q2 cv ok wt b2 cj wu mr zj kn e5 iu pz r8 pe fp ot tq a9 y5 sz ez cl wq qq wv a7 ln ky jd qe qr yx rm qi ea ln te y9 ev en eh iv tx e3 as tn j8 wf xh co fl nc wk xz es rx ee wh ub aq u1 ar e7 up it iu o2 wl ko jo cu pc wo al hm uq rn ul yz ro -\n pw na wu jd yf oe qr xr sk wa hw ql wg x6 s9 u7 am -\n uv tr ub k7 qg he u6 jt gs z3 by tn bi av z7 jc ck q7 2n ny cx km mk rf pj xi lh sf up yj to ia ab tq fq pm fd qc qv ps su qw fu xu cm zb bc qr qt tn ei rw gl p1 xi qo tt ed ef ri iz yw oh tc uy tv as qu l4 qr t4 wx e5 ae op oa em tz gd dq rw ug dr ux qj be ko cg nl je aj xw q1 vv ax rl w2 yt aa u0 eu ah -\n dc ph sq jt ql un q5 cg lk w9 ur uy pz uo sx qv qq cc ln fu ym ho su pn qa bq pd wj wj yk ou wl rk o2 pt uc km ja wm ry rm ob -\n gb pw qf we q3 ls q4 sy bl lg q8 t3 wl rg ed io ef if oi hp lo kw wy qw ei yz rt es p6 fp hi qo bn qw wg cy np uv yy oa uo ir of em ug x9 qh nj n8 ea u8 er w6 -\n ij dg cd lw gk wu zl dd eb eq sg ia am in wq xt nk wr xj qq p5 pd pk as sd fn lj jw fk l9 nt wl oo fj sb u4 gs fx hg o1 dr fb hj h8 xc yq ch er e2 aa af ah ob -\n a2 o0 hn pd iz hw jg q1 jl qz ip le me wi bb r3 z7 g1 eh td sw g9 qq c9 vy ud qo es ec tj uw dq ur hj dy oe zp lk l5 fl wj ys t2 ej t4 ek rs sl yu oa u3 gd pm rw h1 pr h2 py wl 2p s7 wq 6r mi 10 ox o6 -\n i3 qw ee ur cy nx r2 wj t2 ub ir aj cl qm u0 oz -\n qd qn un qz xy nq an kg hc c6 w8 93 eq ts g9 wy mg w3 rb 3f wf rw kt op es ef at em s6 pc wg bw x1 xl wg hl yk yo eb ud hm hl py wb u4 zp bj bm se sr sy ox am -\n rc ix qs ls qy at ut pk yo ys ec hs lq xv ks -\n yb al zf ws cn ac ih th ww vb kt b3 xo qe qi te ea p8 tn qd ci ix xk pk bg rc tl f4 wb rb ru -\n iy qd a5 jq jw qh sw fv oz cj hc qq ya ee yn pr av or us iv fa qb q9 bh ns d0 qe i1 b0 fh qy qu qi ry os ul hq ri ix e1 ao p0 qt sf qi uh ll ko lx nz sg jz hq sh p8 x3 wg rd sx yo yp u3 pv rq ds tc rr wx lr xb wn ep hh bk yw q6 og yr yg si tq do if -\n hv qa qf jg he q1 kj qz bh lr kn rj th kz ef eh av pp i1 ar gl ur lr bz xp yr ze qt tn es fl hw s5 qa ed t4 wz sx rg sv e9 fz hf al h1 av bg ym ee yg -\n k8 nn jy q4 wd lf xu q9 a1 4v yd mb r6 yh pb ta g6 dn d3 pl j1 jk wc cn wy 26 rr te ti fa e4 uy fb gr hb kd lc qf p5 wh au fa iv xo hf ot eg ra wv tp ec yo ah iu pw hj ac h3 py k2 u1 wb rl rz yt er w6 ru af yo ep -\n qd uq qh qm q3 vg qc c5 rd vp ut eq on yn ii xp up r8 d0 sz qx ue pl lx qe wr qr lm nh qt ha qo ki ri e2 tx iv ao s3 ow kp xf rh ya r2 rk cw nt by wd j8 t1 hk y1 ns t6 wc ev sq rq yf ux aw ch qs u2 zn sm rt wb bk yq dh 8w w3 rc yg o3 yi ox ov ir -\n u0 q7 qb ml or nu b5 1l xb tr tp in qt hz so v6 dq o2 qh wl nb rv fw -\n ss jr zf zh xt oy hy aw y8 js ob wq ny or vy fi en tb qi j9 gt ib ot oy rd e5 y6 tg th pt gq wz rt rl ew fm ie ri ir ro ah -\n o0 qj h9 wy ee g9 gk jd fg qt 3d fu ru iz tl fd tv ad hl wp oo wf nb ez sv tl f4 dr oy rp -\n ak il k6 qh q2 vd k3 zd bo lj k7 km 5c ut rz yd up ua is r0 qn zq wq j1 qe cv pw fu md bw yw qq ra rw qu ex ik at y0 ru ti yw fz ic ao ow gm jc i7 nf p4 fj xg kr br xk bs mb pk hl wl ta ez sv e9 us om rw ap gq wl k2 qz h8 gu kf et ru tq ag uz rp -\n yb az dd fu rf hw qg we u9 o3 q5 q6 ag c2 o7 wa kh w8 vo mc yg tu ua uh ta tw ih hu fj su bg ww bh kw ry ru wy ky wu wi fw 20 b9 qo ik oa ev hw s1 e1 e3 fc uu s5 tn qy hz jc do ou jq gb kf pf xl x3 yv lz iq eb e8 os sn fx dw qg ql wc ka n8 gf ly se tv yk di si o7 r2 rp -\n il mj vi sd ia y6 wq rm p5 ux ho nr ef ej wq iq fn -\n ft cs uo io er ic tw ig mm c9 xk ab ze uw i5 s1 e4 pl ui f2 lj p4 sf x4 kz ej ez eb ov of rw dy av qh f0 h5 ki qx cx eb og gk oz uc -\n ul io zd kn w9 y3 wt qq wp jl i9 jk ca h5 wx wb tm do -\n iy hv cs a2 ee yz y6 gk kq em qy uq ts w0 rq rr vt pb nc q5 -\n qn q3 vt vu yk ej fp tw zm qq qy y9 hh wo wg rh ep x5 wk mr el l9 av hz w5 -\n hq qz wy cx rh ur w9 e8 r4 fq im fj gj dm qn gl jn iz l1 yh mz rw e2 qo wh nt wk zw t7 e5 iq fh eb sn ud az uv fh sv dq q1 ku zs eb ue xq rn o6 do -\n ub lo sq wr d1 mt o7 ts t5 rd xe iu yg ot gg se pp qc js lu xt j3 j4 wt pc vz 5o yr qw zw qr eu db sy eb em fo i0 ad gw m9 ig ih lc od n4 pg rx bi ni kq wl aw e7 az jo mk bo wb ei mi ep wb eq di do -\n q1 ub xt db wt ws ik pl ee or to ej ic is fr jk ls c9 qq yg qt eo rw tp p8 dy pz gm hz or xs bt x8 t4 t8 s7 oj lt wv vx u7 w4 et ox yo -\n po o9 ih dx qa rf qf pd d2 kl ad lh kb bd qm bb b1 z8 ew d6 yg d7 ym ti eh ic iv oi y6 sz dx qn ut qm gz pj zw jj 4d bk wb lm xb ke yx oo qp yb yn en fo yw fp e4 aa fd jz qu gw qa zs nl v9 wf qt qi vg ni wx hk 9f sz tg t0 ga de re io av h2 jt x0 h4 wx wc fg rb rn nc yz iy zp ds ep zw pr xv rz yh yk zp do hc ep -\n hb ty z2 qz qz zh gw mg kb ve zz ti tp py el jp tg qc ar qv gx la qr cn lr nd ng ve qt 6g ml op pd uq uw eh i8 uy dt ho j8 wp wd qe xm w0 x4 qk el e9 pb sm pn tc gt ce oj jr mi ds wb ym ew u8 -\n ij yb hn u7 cd gj co dp lp b2 r5 ed ti pn qx g0 jb jn jj we bl ri ot pi rb yc sv ty oh ph hh e4 hy sd wp ll ft l7 wh ca ys wf wb t7 sv uo sb sn ha pb sw de un qc bz wo en as tb eu af eo -\n d2 k0 wr q4 q5 c2 sj iv pm g8 m1 l1 5s ij aa lb xm vf ej ta ar th od sm cw gy bu qd q1 u8 ry rn -\n qa ux q3 mj ex yu zx rk gi rl ya is py am tw ja js db ps dn qb qn gn lc pe qq vr qr eo qi ec oa ev uz yq of in ho qo jj jk wk wd zp wf lz t8 tk ha pv fz pn ug o2 pe uk kv gq v7 oi qv wv dj tv fn fw -\n dx a3 k5 um uq jd og nn q5 qx cu wp rd ws d6 px ac oe rb up tp ej ek ih ff qc gj qm xk b4 dz jg sq jh eu yx eo re es ul yw tp i6 pj ho qi qf sn og xo yv pk wj wb go ar uo eb ir iy pq uh qg h6 vt wv sn n0 rx af uz hx eo -\n yv ub ty gn gu fu dm ca q2 d4 cn ad iw k6 bf zl zz 2o w7 uo ee yk ix g3 am fw oi jo se ha vs qn iy qq 24 bl j6 g4 cw jv 1l ei qy ke j4 qi ep of ao hh tb gm sh lh vc uf vu wd p6 xm qt kh rk l9 s4 wh mr t4 oi rf iq op ox u4 e9 fk u5 it re uk f0 kb nd qk ce jp lr cy js qd qb sb tq n7 n8 ed ue tn ox o6 id r2 it -\n qa pa jd qn qg jt gh q5 lg ag qv ah qn vr da rh w7 b2 rz rx d6 d7 eg eh yl a9 ek dl tw sc hp ha su gz lo qe le ns kt qy qi 1h kp mz qu es yb yn p6 eh fs ok as im dy px gq qp qs l6 iv rl zw dr 4r hi wj rp t6 go s8 e8 at e9 f3 ak dg f9 qh pt dz ww rv wb oc pv be wq cs q1 xr xx eq yr u9 sr tb yl tq if hc ig -\n a5 co dh bt lw ck lh w7 3e mp r3 rz yf yh uh eh td y8 fg pa ar va dm su q9 d5 qw re vh he jc 1g ib xz qq qw yg vt rn rb cb ry ym em i7 hr ff f2 qp rd lx wg lb kh va jv qi xd wh wc el un sz tf gu oz ae e9 e0 iu dr io dt fb dh jo um wx s5 oa kx ly rn oc zy f3 hb tt wb u9 oz hx if ig -\n ak o0 qd q7 eq g1 y2 pt dk g8 qb vs qe dh 5i pt yh qo ul tp oj sp oq di uh zg xn rx tp tf ie f6 cg rv zm xw zq 5f md sr yk ru ro -\n a2 tt ub rs ij ml ow pe el gd va ue zm sa pq lc yw qi qw lv ep qo uj ym tl ye hj s6 uf qp 82 fk y1 wl oi t8 fk pb tx o1 sk lm oo xv n2 ad fk n6 dp on q6 rv -\n qf jf kk nm oz q7 b2 xo fw kj rh ua oe yl gh vd qe gn wb pt wi z0 se gj 48 of i5 oh so hz wp ae wg nc kg xf ev pv ov au iy az f7 qb q5 eq yr tv yy ol ry o4 oc di ep -\n po o9 dc a5 jd z1 sq ws b7 ti r9 sl ez aw tg zm si ng qe ky b5 pp eb od jl ff oe ce qp gy yv qk r4 xf kw iw sn tx gg uh cq ql qa 2s mt eq rb dp -\n qs qz cd dl se q0 lv eu yi rw qo uh uj ul en tx wo qd e6 pv gg je zx kp qc q3 ye en -\n un qs qh se ws lf so eq yf ef y3 g4 zb hs q0 no qw j2 y0 uu fb di f1 kq oa ul t3 ot fh ak yf fv dt f8 jo sx wx at wn cs lq zc -\n ub qa qs ik pw uq a6 pd dm d1 qm d2 qk cv zd bi wd ne ah qb kg kh ij 1p rk w9 wt r5 d5 px uf eh yk oy pm i2 hp st qn si qm zw we ls px lr ri qr sr db hp qu xk fy os eg en uc ur i7 sa hp vn qs kw dn od rh xj w9 wk ph ap yh el oi oo e7 gp ay s0 f4 gf az jy qk ql qx 1k v9 qc jq zy n5 kg hd ww wv bj hj ur er rn ry eo o7 -\n df a6 dn je ql no q6 ox wo zl bn rh ya mv e0 yn pr gd pi y8 i4 c7 g1 j6 wo rv eu xg eo c0 yx ea sv os wp qw wl ou un t6 u2 os of f6 dt f0 jt wc ja ae qv rm ds pq y7 qk ck aa ux -\n db iz jk zd wy wh c3 zk 2o rj hw vp on ed ac to g2 r0 id ta th qb dm pj m8 np oe pu bb tc gh ml rq uf tu eh ye tx gv pk jv j8 lk xs kd fi mx be wd t3 mr wk wl td eb ie tz dw rw pm re fb dj h6 ql wz wx qx qv u3 vz xe ex 2w ty ew xm oz an -\n ty a6 we wi ro lj bn rh r5 g4 aw jd q0 gz xy m6 wu qq et oo ex qp tr y0 fi au e3 oj gm px lg wk tu ek tg u2 ov em dg uk nd qj cy hp wq mi bj q4 ia fm r1 ei ie ux -\n tr qg h7 qk jl kc jr am mo w8 e8 td gl kw sd jo qr vl gs qe b9 mm fh eo uh ft ik e2 i0 uu ff qu f2 jq v2 wg kg ek aq wm yi yo s8 e8 sq ab cw wt ck pb pn xl bj yq wm ew xq su r2 -\n qk wa q6 jj ws ut gd gf ly ec pj sa pd wl e5 wc da kx zk zz zb wv rm te -\n jq uz nv ql as jx z8 q7 o7 yt rl ea e0 ym y2 pr ia sz sq sr qq qr vk oe pe lr bl ll rm yx y0 eg ti e1 ue uu ui jx zd oq kd rg lv lb r1 fx ro me ts ay f6 fc io qg py qj qk qs ky qh y9 ok o4 am -\n qh dl jt wy a2 yk y2 i4 zq kq we bb dg m6 qq zw rt ta tc ff xs xd qf g0 1d yg du wz iy sw tc dd hj hk mh ov zi wn hk ee yj af -\n ra ak uw q3 cb ji wy fw gw t4 mu ts qw ww rj vo rl yd ug d9 gj i3 zw qw wy md qq bv rn qy pd tl ic p9 hr dr hh ui sf f1 i6 ws cy es ef t5 kr ek oo t7 ec e8 u4 od dq ji ch jr zm jy q1 zp yq 2e og yo tm -\n tr tt qa qd jf pg qh jr sw ao q3 qz za wt js bl vw q9 ws uu w0 ya pl yf rx ee tu r7 gg dv it lo ww up js qq qe lz eu qy rr yb ri ay ye ta tv im sh ss uk qd qf bw ro sl wl t5 e5 um th ha fx re ii fv je hk ot cq km h8 ks bk vl qn xe te tt rl u7 iq ry ag dp o8 -\n sa z1 qj q2 nn wr z5 mq xu q7 gv t6 w7 r4 c1 mb sd ed ym ot ta ht ts tw gd tf g8 se ar gh fh qv qn zm hs qw qe oq wc xj vz xl wo rn i2 sr rq at yq uw s1 tx hy fm pc wo hv gy vu wd lc ul p8 wk wk el oz oa rh gp pn gf fx fc f7 rr dy x9 uk f0 py wl v7 cr ch qs wv nz lv lu 5o xe ym ly er yi ia gl ox r1 dp -\n uv qd hm qf gp k9 kj we lf bs ej 2i el wl t7 rj w0 rz yf ys r8 tp py tq tw dl im qc db qq sd ry c9 oe if qw aw qu uh tt p5 p7 p8 oj zi oe qu qi lk j9 sk zs ka lc wh wk zq mq vh t2 ej r9 mr ez t6 e5 op rk ga pb dq ap f9 py qk qz wc kd pv bd sm dr u7 mf o3 yk di r2 -\n po a3 uw q5 q7 ck kb zj td zz yf jd wq xh ld qr w4 p1 ij fu tp qq sv y2 yt t6 e5 op dw iu pw jp ka qv 4u qf rm vb w6 -\n fy a6 qg cs z3 ql dc jz wy me cj o6 ba kv wp w8 ea r5 uo fw ib ig g7 gg sy bg qr cb cq ro xl xv ex tt ru pd hg im oq gq ao rl pl aq sz t7 e6 os uf ug gg pr ql qz vt mj px wb ci qf ov be bg ww mp mi rz u7 w3 ei yc -\n gh cm ca rg uy pm y7 g8 lx yc qi re uh yn uq eh tz ph wo cr sv fp kh sl oi sx ov ga iu h1 je fd rv qv wi jy yy ry o4 tq si -\n qd iz qh q3 cg lf wy xa ez eq om ug eg yj fo fp yz qx qe wb or jg xb c9 p4 tj y0 iz tc oj tb i6 p1 ka zf qe yp wj mv ra ez rd uh pt zl lm sz wc lr bq oc zq sr af gl ei ux it -\n a2 qa h9 qh q3 fn kx ve wz us sj yz fd g7 vh c9 xq xj ln tz wp wf kg kk by vf j9 5y un yi e6 rh sn tx hj kn rb -\n un qs fi qm jk js bd o8 bx vt eq ya xp yg pz ym dj fp tp ta oy dl qc cx qq m1 rt wn d0 wm yr qw aq qt tb ha p1 uk yn ef tj gv im sd hk pz jx zi wa wf ba l0 wd mq ej wv t5 ek iq pv ov f3 em ak rq hn hh f7 uk qh ot ju ng ji h7 wz cr v9 bj bk rc er ia is iw ei id ov pu hc aj -\n pw gm qd jf u6 z3 jl q4 wt bi lr id wa wz w8 ya ev ew r6 yn ee io r8 ip ej td im si j2 jo d7 m4 pv iv yg qi il ti i6 ta ib ap fb hz wd vu wf wh kt kh og kk nm rp ti ek ns t7 y5 wc ae ir pv hf ub wc ho wb wn mi rn w6 yk tm te rp -\n o9 un h0 a6 pf iq xi tg w8 z7 r5 om oq eg or y4 sr fh zv vw zq tc ws rq db rw eo ym tl fv i9 pz jx j7 hx oi qf x2 l9 x3 qj by to el sx ys yd ao az uj hl dl tj nz wn kg kh on wv w1 w5 gl ei -\n gv az ql nm rc r7 yl ja zn q9 xw no iz qt pk x4 l4 tg u3 of zk wc go qb mo eq u7 tv rm ig fe -\n o0 ik qd um qg k9 mb bu wy bx ny ws hm ea mb iu pe eg ey sh uh g2 ic iv aq td qx ja qb ha 2j lp xr wc vl wn wi sl tx qt rq ec vw of uw sm ic qy j7 ns hb 2q kw wf vp 1f x1 5c zs y1 rg tg oz e7 fh eb ie up e0 ap ve wq zz cr wz h8 wv go ly fk az pr rz h4 ew w2 ok w5 ia si ro am -\n dv d2 cd qc zt 25 xp wd te es sh eo wn f4 wo tv oc -\n uv qd qn hr bj b1 mw lg io sh lo qq xh m6 28 rn xx p7 im qt jn jw bm qf r1 mn ny ed em ii dd wn cz ds vc wb hh q4 yw ur rt ie o6 ux r2 -\n rs dh z1 jr cb vq r3 eq om y1 sg r9 if tw qb qn m2 vy dc b0 ik fa ib aa jz qu sg qs 1s be of w9 oz sv t9 oc pv rw dd o2 dh lq ka lu 1m qk q4 y0 ye yq w2 w6 si ob it -\n o9 db iz fi qn qj mw 3v wp li e3 km zz yy mo ya rz mv yg r7 yh pc or r9 pm a0 td ih db lt pg jf gl re ww qw m4 j5 xi wi yd nh yg qr rm et ey ug re rr y9 y0 sb tu od ay of iv oh i8 ok uu sf gq lg wa nn uj qs cu kd xb wg cs 3k yv hk pn ii hh cw km wz wx wc n3 jr wm qn bd zo mo jo wm q6 w3 rt an hx ah am uc dp -\n ps pt vq kc bs vu vo xu ee ib hv wj x3 nu ud yf qh wb lb gz -\n ra o9 qs ty rd ps db pu qj u6 k9 nb qk oj ql wt jx bo ri xo o0 mk rh bm mj ut mb rc yn et yl pm ih i1 g9 qm xq oq bj wt jp xu pt bc eo ep qi ky sv uk fy ri i5 im dy hk ui tm f2 uf ug qd kf nc s3 fs 12 t2 ro du wk ek yt ej t7 s7 oc ay s9 pv fl s0 gf tx fc ac py v5 qk ce qz cr jp ck hp u3 zm xr ii yv ea cf hj ye w3 tp do tw ux -\n o9 qa ik wt kc q8 wk sp yy w8 w0 ys ea om tu yz pn fe ae g9 ps g0 i4 qb fk qn qm ut j1 d6 4d cb vj vk xy j5 be wi ve qq gf qr j3 ug qo p4 sm s2 ut fd pl qt jz ui qy qu qa nk fj iz xh wk iv qz fb ro x6 ti sl rs hc oi wm us ai dw o1 hh ab qh qj ju ng wl zr vk tw 5a vc hk md yr u7 w2 yt tn eu ul ah -\n uv ra qs ty jh q4 o4 bc vr o9 rg jz mc r4 ui g1 ey g3 sj am sq fj qn it xt ln dl jh b9 g8 dv qt yz ea ue ss w9 wj kk bi ym tg t0 ob ys iu uj qk nf v6 nj ox qb wy mw 1n pq eq w1 rx rp ge -\n a2 pa e4 xy yd sj vs jj xj lm qy qp ri ux p8 pj tv xs wd wf oz gd sw rw uj uk qj k1 xx um eu bx my em ey -\n az h0 qz iq bl kb xp yf y8 qn rv hx oc re gt k2 bo qg cf rl -\n yv uj d2 mq hx ws w7 mv yn r8 ab an ae jn xw al up be qr zr ep re qo ec ur ap hp pn wp i9 rf wf vo qk t8 eb yd uk kv ww wx wt ox kh mi eq yj oz fn ie am rp -\n ik df qg jg k9 wy kc ro wi ve bb rl ew io or eh sq oi qc qe d7 m4 pu gd db oo yv yq ix eh fl pg ib hu pl cr fr xd cy ke mx yh wk ag hf hk qg sj we mz gp u4 ak ma rz rv af ox di yx ob -\n hn pa pw qd il qh q1 z3 wr t3 wo ws vu uu ld pe fo dj ot dx pp vl qq rr ls j4 fs dl ve c6 rq ln xk ec rt ty ik y0 tu yq fz s5 sd sh jn wa uj ws lx qr ca rz wd nu ek yy yi y6 uo os up f4 fz qk h8 qc wr at 18 ca ww rv sy ox o6 -\n dv wu wo uo m2 we rp b6 qe ik e1 bq w8 x5 ez fh u4 iy jy wu li f5 u8 w3 yl te -\n sa ds qd no q5 ra jd qo ru r7 uo ar ud on ak fv dg wl qx qv ye yl ep ge -\n ss rf qn bu gj rj uo yz tf m1 kw zr oo y0 pf tc dy qu v6 xh t4 oo um df je qh dz v8 ho wn wo 0w dj rv o3 du ro -\n ij uj k7 me lg ih hv ws rj pl sd uo y1 yk d0 pt y4 g4 ou tw sq td fj ha qm qq 4a kw d7 xy m5 bx c9 yx nw tr qo uj fu en p6 s1 ht tb qo zp kq x2 wk wj wk yt wz sz ae iw ay fk ao ug pq qg k1 ql xx qc cl qk 56 bn oj yt et ut uy tw ir yc -\n k5 pg cp z5 wr no zd tk ej an qx gj i3 su we up 3q yq fx ib tv qp ik wj yf u1 os rk jt qo qx n9 w1 rb -\n k9 uv gs wr 3b mh km bm we w9 es or yk r0 g5 aq gf nq qv ll m5 yd zq qt qp sv ed p5 of eh i7 pz hl sg jn wa m0 nm kf w8 wj de e7 ar iy pn ly wn fx w3 rb ey am -\n o9 d2 vg gk ex rf rc hy qm j4 ga qw rm ls yl cm en tl tp fp tb i3 qy qo j9 vn zf wf qg mb kj qi jb mq wl rj s8 lw um zt wb f4 xw f9 -\n ra go ls qx wi c6 b0 rw g1 yz fe g8 ow qq ra mz ex oa fu iz tl uc e1 p6 x1 tf rh tk fz ap hl qh k3 xb mw zm yb yw q5 aa rp -\n yu qx sc xe j2 oq gs i6 i9 l0 -\n yv ss tt gu fi qj bt ql ls io nw gk hl up zv gl ni xt wy dz qe ud nw rw qu uw e4 qy px qf zw za ty ek t7 pv dg ho wn uq rx yx ep -\n ga 1w ld wy o7 xr pk r9 g6 hu jg lx sd no xt wr zy ku l2 nw 9r rt i5 to tp tc s6 f1 ud ko xb rj qy es t0 f4 fx ii rr hm hj fb ji oi n1 vk ci 9e mt yc 2r tv gk yp ux -\n hb hn k0 wy m4 w7 rc ts y6 j3 qe ve qy rt so di qo dp lk xf mq wl em f5 pr wl wn 3k ew yt w4 ri -\n qa ss lq wr bx t3 r5 ed eg sx dn we 7n ra qe b9 rm wd rw eo oa ri e1 e2 ut ap hu qo ws uz ai tz nl cu wq ln wn ie aj -\n yb rs un hm dg qm qk ao mw fn kv ur uo pj e9 sf ia tp tw a0 td sx fg su xq m1 om na vk wy xk em l1 z0 nh b0 mz qy p2 ru au iv p9 pz ug lz xn wf xg fk zu wj wd u1 e9 tl ak hf sw o1 pw dt gq v5 lm h7 nn nl wq uq zt o3 ad ry id ig -\n a3 pp dv qs gn u6 jy kk io dt wt ck rg ua yd ya yh ax ac y1 pe pc pv fp fw dc qv zb dn q0 ju jj m1 ui lx qe qr t9 ja he wi vw m7 l1 jn qe wa qt xg rq qy yi qu p3 yb ed en tz so s3 tb ho fm px gw zo lx wf sm mc dq wj yg l4 uv yk tp t5 wz ol fk f4 pe kc dj wz qb zm wi br zk ww ty 6i vm eb lt eq w2 ey yp yz o6 ei -\n fu ga io nt wp jz yf rv oe eh pt dz ih sx qx g0 qm hf xe lz gc d5 bh 2z cn d9 1r sz li bv qe 6d bb er xv yx p2 ea tj p5 ay uq dq pg oh qt s6 px sh ko qa nn oa bq cs kk hh cr wg tu y4 t6 e5 oz th sn ov u5 dw qg dh uk n1 zr qv 3d n4 yx xe wv eb h4 yo ro hx o8 rp -\n ra gu hm a7 jw qm qh jr gs k0 ql xt q5 dt ru wy k5 wh fw lu kb am m6 bx vy qq ev rk 2s mc yd es io pc pv g2 ek tq tw in ih ae qc d5 ui qe wt qq m8 vr nb ee hu rw rr tt ed fi em e1 e3 hh hi sh zp qo wp l4 ws qf qf pg eg to un gu u1 t9 ox e7 u4 od ds de hh py ql h7 gy js vz gf y8 uq se do ro rp -\n qs qg gk ta bf r3 hw r7 r9 sh ua g3 sq td g7 ha lu qw xr wy wu rz ko bb i6 uy as di qi za hv jw rf 1f 2u va ap qi rc du wk yt t7 u2 ob re ax v8 cg qb wy wn kg pn yi rn ru -\n gr ra jq qf go ga jh gs q3 tm q8 k7 o8 mj ym er ip ua ej hy i1 dv qb vg cv m5 wy xk g5 wi ng w3 3w ud rw ug ep hq ta fc fd aa i5 hx qp wp v7 qs l6 l9 l0 jn ty t6 ie rj tk od ys on pq tc zl nh qc xv wc cu ks ei lm vm cj yi ad r1 si sp -\n qa hm a3 ac q9 na rj if qw rr vw tj ib su qu wo dp j0 wf pf 2y wk ym ra wb ae ga gs f8 gq im ar pb ec f9 yu rm -\n t8 ej an y4 td ez ln z9 lj qy sm uw dq us cp nu tg vn -\n qa ds k5 hw k8 k0 ql hl 1r wi fw c6 w7 mz rj xy r4 e0 ym yh eg r0 us fs ib oi qv q0 ww lp gm ln bx nc qi l1 qe wg ea qo eb tk eg to ur jc oe dp hv wa 2q nk at rg wf wg ca xk jn pk yg er ot uv wb aq ol wx e6 ev sv uo vf eb ah ud da pe qg jr kn ju ng ae wv n3 iw ly kf cl pb wv tt vn eb vm u7 ew aa w6 rm gx r2 o8 -\n q4 q6 vk d6 eg pc pv r0 tw i3 q0 we tw sm e4 ow sn kg up hm qx zv nz wm u9 ul ri do -\n po uv dc qs qd hm qg q2 jj sw kl me q8 xa wa xf z5 yd r4 rq sf px ti ia r9 yl dj dk ek qx i2 sr qv qb lb wi nf wu qe tv fh qo rr yb fy eb ri ai ok qt pc ud qi qa ws qs lc zh nc x2 cs t2 di ke wk sz oc yp s9 ys ai ln wz cg wc wv os qb f2 ec y8 dg wm rz yr ee rn sy du su eu fm ei o6 dp hc -\n ft qj q6 wq up ut lg er uw db ll ws of og e4 1i r0 wx fh th vf re hm zi -\n a3 ty pw ph cg uo r7 oi q0 lb c0 vl xx mh hu b9 qy tt sv p5 eh to hh ow tm oe si sk oi gt kq cu vi j5 wf el tf yu u3 ya uj dy qh ql ct wc el y0 o3 o6 if ge -\n a3 dd by wt lf 2v bl 7c bn cf yo go yf ii et ey yl aq aw g8 ho i3 qb dn qn lu vf vg 2k le ml wy t0 4h xk qw b7 bb eu xr qu tt y0 os sn tl pf og tz tx pl ss us xd cu oa qd xn ke qf vp kh ny wk r8 ej rd t7 sc e6 rh ud tx al gg re hj ux qj gw xx zv xm iy ca vb yw en oh u9 aa w5 w6 ul oc an uc -\n qa un dn hq hw d1 jr jy kk kl wt kz zf z7 cm q7 me xp wp mj rh ue e7 ys rz eq ew ed xp ee yj y1 to fw aq po i2 jn li on m2 na vq wu ck er yu db yi gl ty eh uw fp tx e2 fs uu sf jx oe jb qp cy bn qd wg 3z nu j9 mr t6 gp pv ha tl ai fx uf fc kx qh gw xb zt qv qb ir cq vb y9 ct ol fn ah hx sp -\n db wt cm ch jc wi dd ys on td po y8 q0 wq kt eu tc tv or fr s3 na e7 uf gg re f5 tt aa tb ie -\n a5 jw qh q1 qj oj xy my b6 es yg yl y5 zm pv qw qt qo ea ri ao in s4 gv i0 ad lh wa qf gm rk vs oy r0 ez ab lm qs qh ry ox -\n ga ca z6 nr wo rg bm vu uu rj e0 ui io pe eh d9 ab tw fe tf fk wy ln md rk sk qq qw hy kp dc qy y0 p5 p6 p7 ic pg e4 jb ge wp qa bn xf ks zi oy e5 um wx ie yp fv je ng oj ja v9 bp qv er an pu -\n a4 jw a8 o1 q3 un gh le nq q8 ig rg rl ea io er tu fp dk hy sq ae lo qq wt wy 5i xj cw dz oo qo yn ty y9 y0 sb ef tj uw ta ur i8 tb oq af hz qi wo sk zs qs vi wf kt nb y1 oy wk r0 ol ex ec tg t9 eb ap fv qk ji cr s6 et xq bf ep mi ax rt yu iq af am yc -\n gr uv hv tr a3 qs lo u7 jy qz kl z6 gk gz ag kn rg k0 w7 wr rl pj ii yh up ac ot d0 g6 td y6 fr se pp dc g9 cl gx qw pl m1 ii qr vz oy nf eu eo p1 os y0 ri ix au uc ai fx p9 nm jj lz pa kw ul rg gw qh 4m eo qc l5 rp oy ej un yy yu t8 sv ud fx ac dy av gq qj ve sl bu th u3 rn nz n5 zm yz bd tx el ex n9 es rt rz rx ol sy rn yo -\n ph cb jx wu ib vb ih ty oy tl vu -\n df qd k7 z2 q2 ju jz zf cm mw yr gu rx yh ym ef pc qx jd q0 ow pw wt rj xo mf xl qq qw ud tw ku ik oa od ti hk f1 xs qd wf dm s2 ph xo ou sx ae iw t9 eb u3 rk ak hf dw ax oe zl zz wm sm el cx cw lq za tu yw rx yu rn fn yi ei -\n a6 ql wr jx z7 wu xi ym fo if a0 dv ww lx zv dk tu sn hh ff hu zo ws rf wf aa ni kq uv t7 um go e8 ob sm tz hl uc zz ol lr kc n6 bk ry if -\n qs gm tn rp iu pi qe ec to l1 wh ra wl it kx fd vx q1 ri -\n gu pw qg we d4 ws q4 cn q5 me qv zj zl ex wr xo yg r7 eg et ey us iv po aw se cx az lb nz nc qq ew rs rq yx ep tj uq eh fz hg gv jc di wp sa nc ya cs fv qz ti wn aw e7 ox u6 pn re o2 hm fv qg hl dl v9 qv tz 6y rl ye rx ur tn eo -\n gr qj z6 ld tm jw hc ed y5 se ke ht tn jb 12 yt ek ao io wv ew ey fm tw ir -\n gv fr ak o0 gb rd dv gu qf qg qh jg ux qj ph k0 oj wa jz bi ja eg c1 fe qn b5 rs rg b1 vo z7 us d5 r5 ii tu yh y1 or ek sl pm hy dc th sy ww ze vb wt m6 iv mj qe 6f qt gk tq ru yq au ap dr hh qy sf qa ik kt wd rz ej t3 ot ej ub wx oz th s7 t0 ag ga pv em fz sw o1 ip qh nd h5 et ho cu yz tq wq wn et tn yo si ov a1 -\n ij rs rd qd pd qg qh z4 ql ip nq q5 xu bz lh o7 my 3w xe ws 2p w9 rk es er d8 pu y6 qc gl bv qa rv qt rb os ru fn qy qu hx or wa qs at zg mx xo bg yh ec os eb hd rw dw ip vw ki ok qx cu wb sn wm yj o3 tm ei ah -\n tr rd pq qd um qj u7 q3 cf db k4 gl mr gw c3 bs k8 vi 4v kz cg rz et ey tp fq y5 el gd dx qx hp mn cz wq xh m4 av t0 vz m6 qw tv rq ei il sb tk eg uq tc wo qo zs rd nx 2y fo j5 l0 l1 hy vy t3 t4 yt va y5 rg e7 uo ox at ir ys hd uf fx re rr ac kc cq qk cw h6 kp xn zu bd cz ca pn pq w1 rx vc w6 yo is fw ir ov -\n ra ij a3 qs qn jf qm nv cs cv kz q5 um q7 q8 km ya ys rx yn d8 sh pt fe se js ue rk m7 wp et ei qy to tz s4 af 3i lc wk ej hc ex t7 oc sm s0 tl fx re fb jr jp qc kc jr cc w3 yl oc ob ep -\n sa yb qn k8 lf d1 c3 wp vr wl yd iu kb sz g7 mn jm lz sd m3 lv qq j1 ex qo ry ru em pk i3 hi rf fk nc wd vu yt td sc tg s9 tz tx dh x9 qh ku dz my yr w3 oj se ei gz tq hx ah fe -\n w9 rl rc or fq a9 pp db gj hs lc qr ec p4 ph hb x1 ez u5 qx ea 6t tn -\n a3 qa dd qf qn qm qj vj wi ag wo ig e3 wz r6 d7 ax pe rb ey r9 is ot tq oy if hy se qx ar qb vd qw qe np xy nd wi in gj qu y9 ev ti tb qt px ud wo ll cy wd hw kh fp wk wg wh ym vo ub rd t7 iq yo ox eb yp ys au u6 rq ii io pe qh nz vl be n8 wv hk og rc er yu u0 rn yl is do eo -\n dd nn oc el yu tl rc rv r7 y2 hi qc qm wu cq qw xc kp tr fu ib zi qu wp vi ci qj nu zw t1 wl fh ev os f4 f6 f7 cd zc qx zy wu bs qn u0 -\n o9 gr fu a7 qk xu q7 wp el yu fp ou y5 pm pp qm jm st op uc fx tn hl zs kp bq p7 hi ys qj ki qc qa n6 oj ey w6 yk si -\n q7 xo sr he uu sd s6 gy ws iz fk sw al v6 lq fh ie oh uz pu -\n of ch zj rk rx rc g8 i1 jk tv ul fi e1 ic sp in jl jv j7 nm rp r8 go hf wx tb oz tw it -\n se kc tj rx yh eh td pa zb qv c8 j5 ri eq b9 rm ik ev ul ti p6 en ok tn wp jm ws ke br wj rp en gd rq f6 ac ab zc rz ew tb ro -\n qd wr d6 i3 j1 ww if qt yn fd e4 qf j5 yh t8 u1 ev qc wv pw u7 oj ok yz tw o8 -\n un pa jd qh qm dz pi z3 ny gs k0 wt xy z6 cj k5 gl bz d1 fq ye yr rh t7 ot if g6 im pa ps fk zw lx kr lv na wu vw eo cm te qo tr ec ty sb y0 i5 to ye so tc i5 sg ct qs sc ws qr xj 2u n5 rl cw dw ys qj yn qc y1 sl t9 ox sv s8 ya s0 tl ys rw tx fx ds rr cq cd ql qa au qg vg rx yt iq tn yl uz ei si r2 ob -\n rs gm qk gg m3 rj eq mv yf sk gx ve eh iv i7 n3 pb uf gh uj tg ox ww bg oz su o6 -\n ih a3 uj rd qs df h0 jd d2 kj q2 ap wr ol nw bz q7 fq ir ra w0 eq ya r6 d6 eg ej pn py pp sr qb jb wq ni xe we lm be xo w2 qo mj qr hp tr qo qp ef of yw ai e2 i8 fb tm do dp i8 l4 wd p5 sn pf gr vs rx kz vh t2 wj ot ar t9 at ir dw qj nc cw fd sx qc mz lt pv br wv dr q3 yq vn ye yw dk oh rc w3 yt se ov ge -\n ds h0 jw he qh jr jt ql me na ah xa tf wt pj pk om 97 rc yg ym oe yj eg dl fe sz g9 lo qq qw wx rr c8 ns vq m7 xl gs vr qw qr kq qi qo eb tk ue dw e2 i8 i9 hy hh qt f1 pc vv qs bn ij i0 uj xh wk qz ns ej un oi yi rh od ha tl re tc o1 uh ac ip qj we qh lq eb w3 w4 ia oz eu ri uz ep -\n fr ij dl qk z3 qz wt z9 gq mr wo zz rd dd rz ee sw pp g0 sy vg ww iu pz uo cb t9 ld qr ei yx rw es ts zi wp wd gw wj hf r4 tt x8 wl t6 hc gp eb aj ai iu o2 nh qv ey kg dp wq f5 rt cg yw sr tb gj rb fm ro ah ig -\n ss ux q4 ji xa mj mi ld rl pj r4 rx yg ti ix a9 ig gj j1 ww ii qe j3 mz vl qq ye m8 b8 yl qp ik ki eg uq fi ok fb oq fm sf oe hp v4 nk wg mx kt vs j8 yn wc wj ot td wn iw os u4 tl u5 rq de io x9 dl ql n2 ji wb if hx -\n iy jk ql q4 wt kz fb q7 vy w8 ur ax uf ym yl py ou dl pm in sq ho j1 qr ls j6 ic sl ko xq jm qe qr qt yc es ry pf he i8 s3 pj tm oe qo lk wp j9 nb yd bu rs e5 yi ar rh ga ud al hh oe wx s6 do kv be pm w5 fn ey du do pu -\n po rd qs il rf uq of jf nf ih w8 b0 ur pl us ed tz tu ef rb sj tf ff i1 pa dv ue fk m2 qr wt j5 c0 vw xo b7 p7 qt zi wo gt qa oo qd bt nt zq x8 ou e5 u2 fj s0 yd sw re cd qx jp wc ja ga jt bh hm eq rv hx gx -\n iy ij pa gy qd qg he d2 qk d4 qz q5 nw wu am qm ft w7 rq yn ef oe pv ek fq sk y4 ts am fd qx q9 jf ju wy lm zw wp er sy qu oa ta jl ss gq fe wa p2 kq ws 2w dn xz t2 ej rp rs yy e6 iq ag e0 u5 tx dd pq fv jr qj ku oj ql wz fd s5 nj qx zt 2d qb nx pm ce f9 w3 er tw rp aj it -\n ss qa gm dm qk c1 jd k0 t8 mk rk tk om yn tz r7 px ac av ot ts if fw ez y6 se qb ha su qn cl wx we qr zt kr mj rc qo es tj ym iz tk i6 fa i7 s3 pl jx du qu j0 ws v2 wj ys yv wd s4 wf nm dt wv ub ez ta wx e0 fz al ap qg wr ar wb fj n4 cz qg wq fc mp yq ev se eu am gx dp te -\n a2 hv yb nv h7 jt lw xt lu wp yr rg 2o 93 uo pr ej ez jb lz ww az oq bk b5 wi qw rq hs te ea es ed fu ti uz fd tm jc do qi j9 zs j0 wd xv iz hr wx el ns oi t8 sc t9 sb fk hg cd rb mz wn 4i ov ln yr oz tm ro o8 -\n iy pi jt kz st tm rh ya b2 om ef eh tp el in sc qc g0 ps zq nu pq j3 oe a7 ja js ng tc qe pp eo em fc s3 hh i9 jl qy i8 lk wa ae 1p vh ox rk em hf dd jt rn tq is oc o6 so pi -\n qs gm qn gw qb cx w8 ur yp up uy ek ez ar sy qb hd bx rl qt yi nw tt eb fl eh pg oh ib qy qi bp jz lf eo ph wh oy y5 om az tc ab wv wb kg ww eq ok aa uy w6 ag ig pi -\n ra a5 db co qn d4 bu qz kx me nr q8 my lp t8 gu rk yn et y1 ej g7 yq d0 j6 b6 qq rn kw ei yc uq e2 s3 oj s6 jl kf rl ny wg mw t2 co el yy ez eb e0 al qg km k3 n2 zr tk qb n5 n7 et tm ul -\n po uv a2 o0 rd hq dh hw a8 d4 wi z2 vt ww kb d7 pv tq fa ta dl oi y6 im ff ae qv sy si wq pq bn b3 lm b5 wi ku qu ru ul ri tx fb ss or sk wp qd w7 kh nn es hy wh rp um sx e6 rh rk pn sq rr hm dt ip dh pt wl h8 qc vj ly bq zn gd wn q4 hj yq xb mf ok tm ge -\n ub pa fy qf dh qj q4 wt mw cm k5 gw kb el w7 w8 mx ya ii dj dk gd dc gh st qb iu jk qr bz vz ab b5 mf pu qe xd nq eo yb pd i6 ue dq e1 qo wo sp 1o n1 4v at qf fi of xj rj dq ew nm x5 wh na ub e5 um sb ob em pb re ip x9 h7 zv xm bw 1v mp zr w3 xm ee yg rv rt ia is ro ep -\n cm bp hc rx y4 sr q9 jj rt qo uk ev to ff so bg eg y4 l0 go os ay tx qh hl qc wb -\n z3 nn o9 xf fs gd g8 ns ec p0 tb wf uv iw jt wr dq bj u7 e2 -\n da td ta tw tf tt ay dq sf gi ae rl e1 gk af dp -\n un fi dx wt m5 vo ys j3 i5 ad nr wj mn tg ox bs ia -\n hv yv qa qf dg qj do ek w0 is sl ez sr i2 ww we rl vr qw y9 tu p5 uc hj i6 ud ws l5 qf xh kh lg wf wj uv tf t7 e7 dt qz ka xn cx xe fn it -\n iy yv rs qg uw oh q3 lr vq bz ab zm wa ds b1 w9 rl rz uy wy om uo ef fo py tw fe qx i2 qc qb qn ww vg ke wr j5 j6 oy qq ng sl qw mj yh xf yk xg qu te p2 ft y9 uk ym uq so fx ff fn qy du f1 na lh wo qo ge sk v1 wg mc dq wj iv 1h pk 3s ej oy ek td ex ae yi t9 go e8 rk tz ud rq ax hz dk qz kf wm yq cy w4 h6 rt ry tn r1 gz ux pi -\n d2 we aa cb o3 xi tu ti gd wq pl xg wc lm de e4 sj hc ic wc ra wc go o1 dd ip wl in wx js tv rx yi yc -\n ty um hq co ux ql q6 wi bc kn q0 r7 yz ib pm g7 po qv re we bh 8j ru xo ra eu ud qy uh ec ty ry yn hw sm e1 pg p0 dr qy oe lc x1 kt xz pl t4 el t5 ex sn us dq rq ao f8 pr md ql v7 v0 n7 kh vn wm u7 cz et w6 gl yo ei di tw -\n ub jg ph q1 q2 d4 q4 qz kl ld cn ji z9 ro ek gb w7 rh pk ea ax yj pv ot yl an sl y5 po im i1 zb fj qb i4 gl xq si m1 jj lb l2 ul sn ue s1 ta hg zu lh nd j9 ci qu wd bh ef ro ra aq t7 ex t8 od en fz fc df h7 qz n1 v9 zy 4a tc bb ea yw mf ia yp eo aj rp ob -\n o9 qa h9 dn vo a7 qj jt ji ne kc cj zh wo q0 w7 e5 ui vi ya wy c2 r6 ui px yh y2 to pr ab dj pn a9 pm tw sq ig hi bg ni ry lv wy ic bc li qe im dv rq xy ki i5 fa fv uu af do vv za l3 bi kd nx w8 nt lh hu ra ub un ec rj ua fk ir s0 f4 uf dw jt k2 kz ml cl km wv vv es tb yj w5 rn yo af ru ah ig -\n vg wr zh wo on ew ef ae ha id uf eg p9 ef gi al ng 16 rz o3 -\n qs jw qh cv z4 ok wt k8 kg km wa uu us sj oy iv tw jm c9 fo nd 20 qw w3 yi re qo yv rr op qp ue oh s3 uu px jc hx wf v2 br ep wg pz t7 t9 au re zj kn xc bo kg 1v hb wv tt u9 gj yu ry iw dp o8 -\n qd wi ij rh ef fe jm kw xj wh uk ef ti e2 j8 ou xo ny wh rp wj ub s7 pb nb qv ev o6 o7 yx -\n gr dc ft qs gm qd dn k6 lo k9 nb as zg bz lw ui ee g4 dl qv q9 lu jg rx w4 yj ep oo sv uq hq yw ao fc e3 ui dy du sk gc gy qs l7 kz ed ej wl un yu wm oc gq qk qc ks tk ti eq em ly vl er ry sy yo ro eo -\n lq d7 i4 7w y0 qt gw ch o6 eo -\n fr hb dc o0 yb hn gi jh sw kj we o1 vg nm q8 bz zk bf ml ev ed r8 iv ht fg th qv vz d3 ng xj 0h 42 ew vt yg qr qt ha qu hs qp ij yn eg of tl p8 fz oh iv jl ss dy zu or sk uj co kt rp wb wx fg ev t9 rj yp u5 us ys ak rw al io kc dt jr hl ln wl wz gy wy qv qb mu hd ky ku zp ww yw rl oh ee w4 yz -\n fu dg qf pg jg o1 dc by q4 st t3 lj ve jr am 2i rz ea lh pl ed pz y4 g8 i2 db g0 fj q9 qn bl en hr m8 qw rn qt yi ei yk qu xi uh fy yn ix uy gn jx f2 gr fi x2 zo pl vh ek sz u1 s7 ya em u5 da re f7 hl qh ju oz ar zb ci tk ob n7 vh og w1 ok er o5 ri ro tw rp it -\n gv ra fr ub h0 hm pf qj kk zf zh rj eq d7 oe eh ib oi gg i4 jd ph nu gc qw rr m3 vj ry is dk qi rm qy qu ep p3 ed pd ta s3 tc fd sa im ow jc oe qi j0 gt bm vm zf nj rg w7 x2 nr wf hi rp wk co t6 t7 e6 ag eb u3 e9 f4 om o2 dk h4 gq jo cr oz ka kx rn wn do ep wb vn ef rz ew yi r2 ro so ob -\n ft a4 qs pq iz pd u5 cs q3 qz ra rh w7 rk mv kv ee y1 to dj sj ta pn oi tf i2 th q0 vx vf ww 2l cb wt yq ku ye gs qe w4 qy qi xi tt es qp ed ef ti i7 tc pl jz ho zo qi za fy zy rk x2 r3 ht yv ex op ae iq u2 ag pb of dd h4 lq wx cy cu zy wm ry ef dj vx st ia ey te -\n rs al qd uq ga qj sw we pa bi ba e4 yy mo d6 er et ti rb py ek am ib fe y7 fh jv mn qe qr oe c0 l1 qi mh 44 xe ei ev hq ix e1 pg pj ui hp pm fr qs kd nk 1v wj fa wf yt t5 vp ex wx fh pn ug fc pq io gh dg oy nf v6 bt jo qz gu me wm n7 br tx mt q1 su eu di uz am if uc aj -\n da a6 q1 ph uv oj ji mp t5 mi rj cf jl w0 pk ew ii rv oe r9 ic id sl se su q9 vd we j3 ac d9 yw ew w3 y0 tk ao hr in e4 hu du qu jb wp cr qs v9 p5 vi xm kf s1 ea t2 wh y1 co iq yo au iy on ds fx yf qa zv qv f1 y8 wm u8 rc o3 -\n iu r5 el dz rt m9 hb lc x2 zp aw uz -\n k0 px qe qr i2 yz qo ap t1 ou n4 -\n qg q1 wr wt wu 5x ij rg lq eg ia r9 is dl aw g9 xx w2 qt au i7 us jc f2 ge qa gt l7 lb mc x3 3p tz u6 kx f8 fb ku ag hd oj o3 fn tw -\n ds rs k5 go qg ga qj gs by q3 xy q6 k5 4k o8 ws td mo w8 th ys eq pk yf r5 uo rb r9 td y8 tg ho qn gz li m0 oq kw qr g1 wy iv b7 vt qr qu ti to ta ut sa i0 pl oq sd ho qa gy qq l4 ks fu wg qg kj eh ez yu tf s7 os s9 ya em pq tc fv qg ve sx af ci ah qj bj df ry rl wm zy tv ol ey ox ri ie tq ir yc -\n ak ra yb ds gt fy qh d3 ql jk jl ni zs q5 zf lf so wo mu yt wa w8 kl ue e7 2d mb yn tu ac pv id pm sq sw jo dv jd jg qq qw qe wr j5 wu 1h b6 vr yf cx lz rn ho gh qi es ev ty p7 fx fs s5 pl sf lh sh i8 qa xs 1o kq zg qh wk fs vo wl ez iq uo tj u3 gs ii je jr hk ql xx 1j v8 nz kf vz ww yw yt w4 rb ol o4 rn ux ig sp gc -\n yv fr qa rd gm ps jd a8 qh ls vg q5 lg eh z0 vt mi vy rg lp ex ew d6 yg rv oe fs sz g6 sy ha cx qq wy j6 dk hr l1 qe gl ex ln uk sv ty at ru uc ts hi hl lg jv qi vc m0 fy xg qg eo hf mu mo kz ot np oy na el yy wz fh gp up ir e9 s9 f4 gf pw uh uj jr ab qh uc wl ce qz h8 v9 wv ie 37 eu gf yv 1m ma yw wm oh dk sr oc ei o8 -\n qn cd zf y4 oi dv xq q0 lc av cw ki xd lx qi gn bh em uf we ja ox iw qb wn my zs y9 ux -\n qd qf we ls lf k4 eg bc e5 rl ea r4 oq er ip g2 yl ot iv ps gx qr wy xj vz xl bx 3o qr eu qi uj p7 uc ph in pk qt i4 gq wp v6 kw kd xk zw 11 yj wj rd oz th yo eb ya tl au tx qj wl dz wz cg zv qa rb wm 7a zs vj yw ee eo -\n jd go qg d2 ji qn wa bf t8 ys eq ui d6 ed yn r7 is qb q9 lp lz qe c0 wu tx wa te qp 64 uq in qt qy wp j0 lz l5 og ca sz un ec rh pb pw h2 kv aw wy qf 16 rw ew tb aj -\n a2 gr qs fu db qn q1 uc jr qk cn q6 b2 ne lg q7 q8 wi wp b1 ec rk yj pc fo iv sk gk jb qm zw m1 wx zt xy wy em 41 ee gh xg cn yv qp sn od ao pj fs ut s5 tb ad jc j9 xa uj ws kf wg vp nv fa wk mq x6 vh wv t4 ex iq 7r y6 sv ox ev eb rj rk em aj pq gh f8 th os sb mt ak q1 xr yw ti ee tb as ox o5 yo gx uc -\n qj lp z0 aj wp vr wa bb xt w9 ya on ew ym ia ix pt tw dz jo ae cc qe lc qr cn b3 c0 ib ml qi uj qp pf p8 e1 s3 tn ui sg pn i8 hb ij qw pd ld fo ap ty ro 3b r0 sz ie gp rj e9 fk gd pw rr uj cf qz zr rq 4p kp pr vj w5 iq ey rn ie eo ir pi -\n gr rs gy pw qd ga jj z3 kj ql nn bg dm zz uy pl e0 lh ef oe am y5 fd qx hi uw i4 q9 hs jb vd cx ni qw wx zt qr d0 wi 43 w3 cc b9 qa rw oa ev ry p4 en tk ti yq pd i5 og ic ye so tc de pj ff hl oe sj qs wf v9 xn gm wg xm 1f ph dr vg wk ns t6 um oa e8 sb t0 gs sm fx o1 de h1 uk qh zj zk ng ct gp nx xe 3z wm rz yk tn ro -\n qa pt k7 og kl wy rp hx wp wa ui mx eb 95 ac eg dj yz aq in ih i2 q0 cz cb dg xi cq jc qe qt es ed sb en iz fp ta fc tv tn gw ka i0 lz sd il qf 1s iz qf nc xj xk ep r7 rp gu t7 wc t0 en tl iy iu pw kn km ql ct qp ch fl wm n6 rw eo qm vx ty ee ru ig -\n um rf qd db qf od d1 mb u0 le xu wy q6 mt bc qw cm uu us r5 uf or tq ek sx i1 it la cb ax t0 wu ab 1t qq g6 ko g7 mk qr ey ha ea qp y0 en ue tv ho i6 i8 sp xs qf v9 jl kt rk qy ot 14 na ub aq op yo en tk ob on tx f0 qk jp vi iw tj x9 zi n6 wo wb se aa ag oc gx -\n iy ub gy pt pd qf me xp w7 rj tk r4 rx ui ii r7 us pb pt g5 fw gf dm wi w1 eu re tq oo es pd ri tl og s2 fx ap ok i4 di lh f2 1i vm cp bh wj wx of on tx dt h2 hl qk wq qz lr tl f3 ce kp yr yg ro yx -\n k6 qf cp la wp gv es pl uo eg am tf y7 i3 hd jk we d7 rl b8 gg ug es rt p5 eg em tz ow 3y eo wg t1 lc wk ol tj en ak fc f6 df gt ol qc rn tz wv rx di ov -\n pp qd iz qm vk jg r4 pl ym y7 sc qn jf qy rq p8 yr di qu hb wd rf ks gw qg s1 x7 ec ae iw eb ai sq v8 h8 le ea vh yw yp -\n ik a7 cp sq q1 lq ql wa qz lr zh rp ra gb w9 ys ui ym px up r9 pr ek qv qb hs bg wt ku pu dc p1 qo ik uk y9 y0 en hr tx ts pk jl ce lj l5 p6 v4 wk nu vg oy aq aw rg os az uj kc py ql oj qc pc fj jr bf cx es vn q4 y0 og w2 ue u8 is ag ie yc -\n rs dd ik k5 hm dg k7 go q1 qk wt q7 wi ws t6 k0 go ii ee io ym ey sl sz sw jg si d3 qq qw nh lp cc kw xt m3 ip ln nf zm qq tc ex ry at iz p7 ux of he og dq e1 i7 pj sp s4 ok qt gt sd xf ow qr pd hd wj qh x3 yb lx wx um e6 t8 s7 uo u2 it sw pm rr qg h3 aq ze h8 ks zb kb bh ec wb vb w2 oj af -\n ak ds dh jg cp ws q5 nq wy su q7 kb o7 ys sf et r9 ta sq y6 dn sy cx na j6 jc qi qw qe qr rb tn 3g eo uh tr ft ri uw of i5 ue ta fs s3 uy as ss qu ns lj wp zf wg sm x1 ix mc va mi rx ej yy y4 t7 ex u1 y6 u3 up en au ds ap kv qh kn gw k1 zv eu lu kh tx qk dr dh wm ti h5 o4 w6 yk af fq so aj -\n uv sa hb ps q4 as wi ej qm zc yd yn fp y3 td hy ue qw qy es tu uq tx e3 jz ud sv l6 fu xh dq wk wx yi dj qz v0 qd ga mp wm yy tn fn yx -\n qh qz ar qq ma kq rx qa st ei -\n dc df il he c1 jt qn yd yn pe et pn pi d7 ke g2 j6 rl sk ng z0 m8 mh qw j1 eu qu rr es ec uk ev ul pf e4 sg jv m9 qf vd wk gu rh e9 f3 on qv vj dh aa ru ux yx o8 a1 -\n ra qs h0 qh bf q3 dv bl mr if ws df ev b2 pl om tz ax yk ta y7 aw dn zr ax qt m5 xx wp qy qi qo at ti p7 tv i0 fm qu sh so lk qp hb p5 xk ib vd hk t2 np ek yt um u1 ir sm yf ug az qj v5 wr fg zv af qv ck ay cs ww pq wn w1 yh as yk ei -\n tr yb df um qf iz k7 q3 we cb cj ne zg a2 e6 ya r3 ut on rq io ow qx ja qv cx cv bh vj qr lv pc 3a rm ep uk ed ev au p8 so fx p0 ts e4 fb hj qt dy px sf f1 zo vx qa wa sa qs vm wf xg kf fz r3 bu t1 tu ez t7 va e6 fl tz uf gg io qg qj h5 zz nh qz zt et ba lu tq vz xe bb md u7 oh 5k rv rt tb yu tn ah -\n gr da ty qj by we ls av kc qc wi wo xr mx cm yg oe xs pr ua pt dk oy hp qm qq zw vk xi ln he rx ko dz yt qe tv eu qi yz tt y9 ev ry ym ay uq pg oj aa s4 sg hp f1 qu wp qa bn vi os iz hw kt t2 rs wl r0 ez rf pv hs om dd f8 uj dj pt dk km k1 qz qx wc n3 nl wv qn zo vx ww dr yr oj r1 tq -\n ra jg jr ao c1 wh rj fp gz iy lo gc dh qw qr 8p eo ev fu tl i5 uy uu ui qp mb hk yt ou aq oi e9 ip dt k2 qx vb mf id -\n rd h0 qn ql la vg qz lw q8 ra sp ts pr av qc vs vg ku am z0 lo ry ev eh i8 aa pl dt du i4 zs w7 wj xl yg yh ra ex u4 pn lw gu pc on n9 n0 wm em tn -\n gb ik rd ql xi bd yr e3 qq w7 ex rz on ui yg ax fo pv ab ta jp qw xi wi qw qe fh mz eo gk qu uj ed ev en fo ux ye fv jv ws lx kr kf n5 qj ea s4 vh ez um tj ir od ga tk f5 dh uk pr pt in v9 js sv qb zn wb vl zj wm ca mu zs ef rl yw u8 er id uz ah -\n iy ij ub qs lo ql jk dv h0 wy cm q7 wu eh fq w8 hm w9 mv yd rz rx rv r9 eh pr ek dk el hi qc sy i4 qq lp jj we m2 g2 fo j5 wy m6 ve tx yg w3 rv rn rq qy hs tt y9 ry ym eh to e1 ur ff hk do wa kq jw p7 yp ky r2 wx oy uv ra yt t6 yy sz t7 wc s0 of ds om kx ng ql qz vj wt wb ly wm lw dh md ew w3 tv er as yu an gz si ro do -\n o9 k7 q2 dt 1i wa uu t8 ut mv ef uo g3 gj hp jn nt cm rj ms wi b6 im qu eo yc ex qp eg sf do sh i8 ih qa wa wf kd yo xj ql wf ek un wx t7 s8 rj f9 qh qk k1 lq h7 in nj um bu qv ov n7 bh bn 3z w1 yt et o4 gl -\n a2 gt rs ty rd rf qd qn jf qh k8 q1 qj ql d4 cg wt q5 z7 lr wf wu q7 sd yg yh g1 eg to el ih sw tf fg qx dv q0 wq qq uu px vl xi js jd ze la ud qy rr ky ft i5 em p8 p9 i8 hg im as jz tn qo ul wg 8d vs ap mq x6 no t3 ub wl tf iw rh ox ua pv ir us pb tx pw dg h1 uk ux cr sz ko wx jw vl rc tv af du ei -\n gm we cm jx lf vq vw kb wk e3 df r3 r4 ew yf ti id fe fr su xr sl jg rq rw uq tp ss qy ws od nv wg ro t7 ar th ak da yf sw io jt cq v9 kb iy u9 -\n qd ga q1 h8 xt um wt nq wy wp a2 rg w7 hm cf tj ut r3 ch oe r8 pa qb jb zw mm wq pl m2 wr wy mh hi ei qy nw uf yv s1 fx ut sa tb ss hl qu qi zp nf zd ar l5 5h gm vo ix xk wk wf vf el r0 sx e6 uo rj f3 em dd uh qj cf wz n9 ga tc qk mu rt ye w4 o4 ad ag -\n qa jr kz c3 c6 vp e0 ng wu ug ty uk tu to hr sp ud m0 ar pa qf wf kr fi ya kk wl xs ed mp x6 ub gu fh rj e9 ya om wl vj ha ex y0 id -\n qm q2 oh cd q7 kk ld ys yd rv yk id wt qy iz ri fi i5 ic e1 ht 5z iq ha ai sq pn al gh un kt wq mi dr ax u8 u9 gk ru ov hc ep -\n iy sa un h9 rf fi he uc u6 cd q6 wu zl zz rk lf yd rx d7 ef er rb d9 r9 im hu zv ps qb jf qm m8 qq ji g2 kt qq ew la xy qo es ft ik tl ye ur as tb m9 i8 qa ka qs bm zg ix ya kl t1 wj r9 oi um aw yo ie ys yf hg gq nh zc sb nw qf xm bc xr bj es rx w3 yj iq tm di gx o7 pi aj sp -\n il qf pd k6 h0 na is q8 4p zl jl z5 hm ec io sf dk if gd qw 1a ld lf qr yx re tq y9 pd iz yw sa wp bn jq w6 v3 x2 br ta yi ha en o1 io ip pr kp nl lt kd eu kf kn n8 zs rx ux -\n ih db gm jd wr zj xp vp qb c8 pc g7 uf uz p7 sh or xh xm wh mt no fh dh wv tk li qm vb ms if -\n he ql wi bn c1 rc ip ia av or y8 mx yr dx ex gz 1p ic wf aj kn 51 bj wn o6 -\n hb ty dv gu ps qj ls qz ch q8 zh xp bs vt rh oe ot pb y5 y6 fr ih sc q0 re zx lm id xp yy qr ry ay p6 he dq s4 ff qt sd vx jb qo qp gb ws wd sd co fp kg s1 nm rp cu 8l y2 tf ev sn au us fz hj qg wc u4 au qh wv bn eq r1 -\n uw vr eq rx et rb fa ek id qx ui kr wn uf p4 tl au hw tx im sf yd dz bo wb xw -\n uv yb ik qd gm gp k8 qk ao z6 ps mw zf jc eg a1 wa 7c zz rh yi lf pl r7 yh d8 g2 r0 tq su cz pl qe qe wr wv ku ho qt yv uj ij es ec ik yn ym uw tl sm he p8 fa ho wo gy ws zf bw nb 5q ql t1 ro rp ej xg uv el l8 rd wz rg go rh sv fh ya it pn hd ao az tc dr ac dy ot sj nd qz ok um ol sx xb wb wi n8 ji rz yr sr h6 et o3 ru rm pi -\n rs fi ag c3 lw ys ef sg qu qi uq eh e4 gy qt ya ro hx oa f5 1j qa cl wq rl yh pu -\n ub rd qd fi jl zk oq r8 y1 tp sl i2 qn sd cq 6d mj w3 p6 ta fm bo nv qi wh yj e0 ao uh kn h6 r2 -\n pa q1 fm c4 ig ex 2a yi mx ek ez dv jf qw qe 4s xt ld dh qq mg qr yc eh s4 hj yy s9 pv rr uj or qj cd wc ly x0 wv hh ye ew yh rb yk o5 tm -\n pp q3 mw rd up td j2 lv af ih hb ee xh yy ua ug aa tb -\n sa tr ds az qd fi dn hw qg dh qh nt z3 qz ad q7 q8 tf vu ue mx vp lg tz er yj to hy fr sw th qn hf gx jj pz wt lb cm m7 wi b7 vr lo yl qi ry ef sn uq ri fx oh i3 sd i4 ho vb wa qa ik uk ar hw l8 ya cw s4 wg r7 ot wk gu u1 fh th rk en sm u5 iy iu re pr hk qg kn gq cf h8 nj ct gp wb qg hj wm cy ok er tv u0 sy fq o6 gx eo sp ob -\n yv ak ra co wy zj e7 ew tl fo ek ez im q0 jm bj lc tc rm ec ou bn sd os x2 lh wj ot oi y6 e6 yp ob sq p0 js qh el bg rr rc xw pu -\n ih um k9 q4 ls jx ej om sf uh dz oi qx cl zm qw qr zc qe i2 i6 uu qp wp ws qd sd fj mx qk yn wj ub gu ar pn rr qg ln dl al vg mf w6 -\n tr ub ds jd gp qk jk d4 kv xo ws gi yo sj sl el tw i3 ow qe zx nx b4 qq ee eu uf uh ex p2 rr ea ry ef eb y0 en ri eh e1 oh fx fv sj jn xf qd vi l7 wg x7 r9 uv ek yt ns aw sx sc vf tk ud ds o2 pr kv ab gt v6 un qz wr wv rb os ie u4 rm zm rw n8 vc za q3 zu yy o3 yi ag pu -\n ij a4 uj gg jy dt 1w rj a6 r3 ii pe r0 ej ta ts ff i2 ho ov wq kw qt ot m7 qq xv ei lv kr yx yb ri fa ur de pj hi si jq wg r4 x5 hj oy 5i u3 tx tc nd v6 oz wc qv bz qb qj zl dg ed ka vh w3 yt ey w6 -\n o9 ft az ps hq uq a8 ql we wg z8 ye wk bf rs wa c6 dd ys rl wy om pe ix y3 g4 dz gf se tg pa va jn jj al qw sf ma j5 qy wu xo dc rn se eu xb nw qu qi p4 ef ru sm eh im ad gm jv pm zd g0 wg qf ai qz ym qc t8 op iw ox ay tc av k1 ko vj qb zo wq bg q2 n0 rl yt as rn uz -\n o0 jq qf he qh 7k q7 kb wp z5 tl ew yg et or ez jp g9 jv gk lx vk vw qa qo ou qg kk xz rx ro wc oa us ip x0 ku hp jr o3 -\n pp dc gi fi qf ql by we la za un qz q4 jc zh wp kg o9 qm bm wt r6 rw eg ix yl tp am a0 aw i2 fh th xq gc eq xx yr qt xt nw qu ri uq tl ue pj p0 hk vx uf jm kq ws jl 1s p6 ca he x2 wk wd r5 wg bi hk ro wj t5 sx fh sv e9 ya aj e0 pn ao ug ac kn h4 gq nj wr cu qs kf vx xw k1 og yt u0 yk di gx dp -\n ij tt ss dv a6 uz gp qk cv lq ql un kz gj wt 4y fq lh z0 6h w8 vp r5 ee tu sg pv y4 pu a0 tg q9 gx qq qw we la wx rr ls zy qu wi xz wi xp ew qe rn ei qo uj rt fy ik tr tk sn pf i5 tc sp s4 in gv i0 f1 si ks nl kw bq co mu eh oi ec sc wc u3 ga fk sm om kb wx bo zj hv y0 en og q6 er tv tb rb u0 w6 tm -\n rd ik og q3 q5 cn xp c4 ig mi e0 rc ym id tq ou po gg qb sy ob hf pk xr qr up j6 ng xx b0 qt tm eo vw ux yw pg tc e4 gy p4 xv pd wg n5 r3 wk iv rl ht oy uv ub wc ar t9 ga s0 em pw x0 pt bw wm vv vm yg fn ad af do -\n hb gg kl q5 t1 mi a4 b9 r4 ee up pr g8 gl q0 cc kr c9 vq yy wa qy mz ty yn yq ai og tx tn nd ws 1d ky x3 sz td gu t8 op gs tz de av sk cy zm be wv qk og uc -\n ph qj d2 cd q3 q4 bi wp vt oq y1 ps cw kn gz ij p4 sp e4 wa sx nj v1 w7 me s7 e9 tc k1 lw zc vj wb kb tw a1 aj -\n rd a5 hq qg qh q1 la cd kl mp k8 mj vy zz yu ut uo sg yz hi gk sy q0 m1 qw b5 wi dz qw rc aw eu zr uk ti em yr lk kw nz wg fx tt wg x7 rr lm ju th tt eq oc o6 yx ro o8 a1 -\n a2 dm wy ej rh rg pe a9 oi y7 zt vk ga yf pp fh ml tj p5 sn tk im jv v6 lb pf zq ty wh t5 go sv e8 it f7 ac p9 cu bw kg qk f7 w2 ee w5 tq ep -\n vs rg rj 70 ys nq uf ex hh jn kg ep e0 -\n df q3 u9 4j bn rj hw up td i1 dc hi zb wv g3 l1 rz qt qy ty tj ef eg sm tx ap in px af mx r3 r6 t2 t4 rd uo e0 iy ii hh qg zc v8 qc ch px zy zi ye og er tm if -\n gr pq se pp qe lq n1 cy qb wb ey -\n gn q1 ji sc nh pe yh qt ss rw hf kx zm o6 gx -\n ih hq ap bl wi wa 3y er pc eh r0 yl ta ts dl gg fg i3 hp kw lc ls rl ff wg qi uh uk yq yw ok as wp gc jm qf to yy fh e9 pn qk sz nm li q1 vb yq 2e rl tv sy di sp -\n o0 yb rf k6 qm fv q5 wi rp fe dd mz rz ee oe ia yz am ig hp fj su gl li we m2 ls xj md z0 uh yx eb eh ur i8 qy oe i5 jv ce jb jm lx ci kg oy hx e5 u2 tj kv qh qo af pv tz az vg yz ri ge -\n il z2 d2 cs ba wa 1o ys uy ed er d8 sh qx sr qb jd ov xq nu lv g1 ku pu rp qq ee et rm eu xh i5 p8 tx in i3 bn v3 ap r5 oy sl oo dr dg hj lm sk ff vu cu wb kd zi wv mu w3 yt ok rv ol ey yx ah -\n hv qs qf qm ph o2 zg wa a2 rl pz ow uo y2 ey ix us d0 ek fg ww j4 wv a7 wu qq bv te tr uj tp ue ts do qo cy uk j5 ra ou aq iq ev tj u5 tx gg df dg h4 qj nc wz v7 im kv jt y8 rz w2 yy ei ge -\n gt df jg og k0 lq kz zf iw kh gv cn ur ea eq yj ix id ez dc qc st wq c0 lm fa lj qq mj sw qe xb rq qo yn ru at e1 p9 s2 ts i8 s5 i3 sf jc xa hb qq gy wd p5 ow qf wj 1n wd qz yt ex e5 op at sm ud tz yf tc ax f7 dg qg ve sl qv tl wn 2h ky yv f8 rt o8 -\n yv dh kj jl wr bi kc 4j nt c5 lo z3 e8 on or g3 g8 uq sr qm hd ll c0 nh ws qu ug ph tc dy oq qo nj wn t7 ox zj qj km h6 ql zz qz qc hn bk fw ob -\n un qs fu k7 co je gp o1 bg dt vu rj mc rz rx r6 g3 ek qc uw fj li qw lx ku z9 br b6 mz yc ty tl yw yr e4 i3 ud ce i9 rf bq xh ep wf ej rp vu wl u1 os ay of pm ap gg dt hk ab ql lq qs lt nl u3 wq n7 bm ef xm yh w6 ru fq tw -\n qn ql zj rg ed hs we qw re p7 p8 yr tv pc lh gx i8 wp lz cp fv lq 1b di -\n rs q0 is q0 yq rl qq vr qa v3 tu in h7 zy u9 o7 -\n ak pw af z0 wa jh tf to ey r0 ta fe tf th wt m5 xu wu xo l1 b7 bv se w4 qi es dy ge vm l8 nv kh l0 j9 bi ez iw ux f0 gt zc 6w bf cx u8 w6 yo o6 fw -\n gb py gp pg ql um lf bx 6q ra w7 vu rj ui pj tl ii y1 r8 ac eg yl sj tp y3 py im tg zv ll ip wt av fq qw dc yu ei uf qu tw y9 em tl dq fx hk oq zu jx hb i9 bq iz mv wd qi pz lx ek aw fg ag u4 ir tk of pb az f7 qk wz le qs wy wn dd bj mp yq zb cj rc tm yo -\n o0 pa rd qf dn qg nm ji q6 cm wp ec uu ax r8 us aq se lt g1 bl vz jc mj 6h ul en yw e1 ye tc i0 do ge gn v2 r1 7f ed x8 rf oo t8 s9 u5 pb fc ug ab pt uc ce qz h8 u3 ga fl bc yq iq iw id o6 r2 te -\n da qf qn nm wr jw c2 ig rj vi ys pl ed ii ax y1 ua r9 ia ab tq an ek dl sl jo g8 qv gl hs jf d5 c7 kt ix wy wi lm qq dz ko qe ff pp qt eu wd yx ec p3 rt ty ik p6 pg i6 e3 sa fd dr gb jl as hz qo j9 j0 qs qd wf p7 br wd x5 hh t2 wl el rf tg ar rj tk em ud pn rw av iq nl qv x8 ov wq wb ec vm y0 w2 ew 5k w6 fw a1 -\n a4 a5 z1 hr qk q3 mq zf qc wu q8 bd a3 xf es yn rc et d8 pr or yl fa se dv dn uy vz wu en mb qa tj uq i6 pl du jx f1 xs qs qd vm kd wh kh mv ed rp ra co tg oa u2 ie ha ir da pr cq uv oy qb qb eo ye rz rc ei o6 id if do yc -\n a3 qs w0 po gx xo nh uj e1 lz os wj uv ud tx rr gq ve ch cs xz -\n rs rd qs qd hw dh q1 ql dy mj zz ws vy pj ea mv yf om uf sg up ix pb ab ej tq fr tg i3 nq dn gc wx wc qr wr lv t0 cm wy wu ko qe qr ze ug oa ed ym p5 os uq i5 tp pg s1 ok tb fb vx ns p1 wa qs ka qd lx ps sf zy kj pl ro rs rd wz tf ev gp e8 u3 ir od f5 dh qj cw qk h6 ql ad qb fj tl al zs h2 rl eq rx er w5 eu yz id pu hc te -\n pa pw q0 j4 4f vq iv yu ry fa e4 kw xm wj t3 ff ye w1 oj rv ul ep -\n mw di ec wt rx ko i4 qo l3 iq iw py yl -\n dx uv yv qa a3 ij ps qh cg qc 2n mc ut eq rz ea kc pz sj dl in db fk su qm qe m2 we 1q ke bn xy wb yq qq vr qw xc gf tn re oo qo p8 iv e2 tc e3 e4 fv ff pl sd qy qi si pm ws aa zw bu hu fn yj pv hs gh o2 dy ln v5 qv zb tk bq 4p qj zz wv 8n h1 y9 wm yw og mf u7 tq ov sp -\n iy wt gx nd t5 r0 yl tg dc qc th wt ld nd zn tc ke qu qo qp tk fa hh gn qp 5d qd ar rg oz fh at ag 2a kd nx w2 w5 -\n il wp ee ym tu ef dl el qb cl qn ob qe qr m6 mf xq i2 ud fn pc gw m9 l7 fs eh gi e7 fk ys pe ok 30 mf o8 uc -\n db k5 a7 je kj q3 we wt q6 ie ck kg kn gr lf pj io us id in aq jo qx su hd qm li qq jm we lc wc ld d8 lb xj em rn j4 tq oo eg fl sm he ue so hg ok dt qt px j7 qi nz l9 lj x8 wj na wk ez ex rg e7 u5 h4 kn ad cu oa qd do q4 eq w3 yy sy su r1 ri if yc uc -\n fr ij ft db dn gp qh ph ga gg kl ws wt ab q8 lk wp mi w9 tj pk yf ew us yz id gd y7 dc qx hp qv gl jn q0 d4 qw re qe cn cq pt wu lm dk cz yy qr rq te qi rr ea ex ye dq ok qt sg qa nk wh kt bt bd lh wf yb hu mw el rd rg sb em on qg wq oj zx wv n4 bo qf bf re wb ev yq cl yu w5 ad ag id a1 gc -\n qd ga dt ej kn r5 ax sg ix av am pu g5 ez fe qm nu ii zt wr vm qt jd im rb ml yx yv ru hr gv ad dw x4 ub hx wz rd ol oo rg yf f6 ii uk dl wl yw yu ie id -\n yv ga z3 kk pp nq le lr bp qc t1 c2 hc vt lw jl w9 ur r3 ys iu es rc ud yj r9 pb ot ta sk gj jm gx xg sf lc qr ht ve sl g7 qy nw vw fo ta tb oq fn qu hc qp op nz ow wk zo zq yg wg ke yt kr yy sb tk rk tz iu o1 rr pr qh dl dz p0 n2 ci cd vn ms aa yh ry w6 af du gc -\n q1 q2 mw bz k6 xf ec r4 rx yg ed d8 pv is sj qc q9 zw vf gc cv d0 xx rn ex uj ij ts ff ge i8 zg 4m x5 vh oy yy y5 wc tg at fk ob fv f9 ql bo k4 zn be ww ea ry tv w4 ru ov -\n tu g2 tq od pk tm fi y1 uf ku wn ew -\n hn rd um qg qh ph aa zn bc gv w8 rj ea es ui r7 r8 ey fw gh jf qn nu cc la 3u bx ve po et ei eo ea qp ul i7 uu fb dt pz qt m0 zf l8 kl t1 ej wj oy rf th rk gs sm em ap hg o2 ub wx ka hd br q2 hg y8 2e eq tb an oc it ep -\n tt um gi qh lp kl lw q4 q6 ro b1 if y6 qc th g0 q9 qm j2 we xt xi nd is ng bc ku yw sm ye dw e2 f1 lg qo wp zs gy l4 ul lv w9 br xl ql vf as yg y3 t5 wc ec iy hf ii f6 re hk qz jp oo qx xv v0 f2 vx cd vb yq ew zu yx -\n lo dt mt z3 rg av us pa xq wq qe qt yx y9 ev ry tk hu oe oi r1 x7 wk wz td jy ww qc 15 ba hd mo 72 -\n rs wi vt rh we jl ur tz tw ht y8 fh i3 qb qm b3 qy ep op yn tu ay hw fd ug qp qd x7 rs yy td u1 t9 sm uh dz ql 4t rw kn wn rc eo -\n a4 d3 kk q5 q6 wf wg m4 2b vt w7 ur uo rw pc g2 sl if y8 fj va rr ld wn xo qw rn ml la qu ep re rr qo pd eg og e1 i8 ui qt px i4 jc gq oe jj ws ks qd ul zh sm ql tf go e8 os tk rk ay us u6 dw dh pr qh qj oy lm jo cf ff wm br k1 en og aa rb yj o4 te -\n gb dd rd qd a6 qj wt jx z7 xi q7 kv mv uy pk or yk ek el y5 ez aq dx qx th sy jn pe vl fp xz m8 ng jh kq qr la ei ft p9 oh hj tn ho ud xs wa jq vn il zg p7 fp ic qk wd bu e7 go hd sq ug hh ip sl ch qc px wb hs rq qh bk rl ef yw dj yt u9 st yi uz uc a1 -\n al tt pq um uq un z6 wa vu w8 ed sj r0 tq pu dz qx js 2j na ip vz lm qq qe yj ud ei wf qi te p1 tu il tk iv dy i0 xg sf ix gp fk ai qk lm ql ch fg qv vg yw rl yy rv rm pu uc -\n rd ik qd jd ph gs k0 q3 qz ia q6 af q7 6w o8 tg gi sj ou aq po ja qn q0 qq gc nh xt wr fs m8 qw aq wp ho qu rm uh tr qp tj ay fi ao i8 aa i9 tv gv qu wo lj vv wa jm qa qd uk nl g0 qd dn gw ic kj nn wf wg dy ej rp y3 rs yy e6 wc oz fh eb e0 of hd yf uf ab gw qc ww yc cd ji y7 n0 wn cg u0 rn yp ie a1 dp o8 -\n ih h9 qd xy yl ez g9 lt qm on vg rz dx qa kt p1 ex yx od uz e3 in sd oi qa cq dr me e8 ua ya dk wx bw 4p vx tn o7 pu -\n ij rf fu jt gs ld wu q7 wo 2u bd k8 rg ws gi oe yl if sq hi dn jn qw bb cb wt lm sq vr qe wd qi qo tk uc hp i0 pa w6 fo v4 1m x3 t7 u5 sq ai hg ap hm ju fl tz wv w4 ry tn yl fm gl ox -\n ql db ch wu rl ih qc 25 pb qq ty yw fp ao qy sp p1 qa rf iu rw qg uk gq km dz wv at qb jo eq ur rb ad -\n ra fy gm iz qk qz lf bp kc is nr ws mb es tl d6 up pe ey sj fq ek iv pn sx ly qb jd jn q0 lp m2 xl fs xx rs b8 hu rn ey qo yn vr yq ti ai e1 so ts jz du do oi sa i0 oq ow 3r 1a wg qg r4 yv ty r8 wk wl s7 u4 of e0 gd pm yf h1 qg n1 lt sv qb eu wi mt ez w2 yk su dp -\n rd qf dz aa bo pd pm qw rn tf ah wu -\n dv jd he jt ao ql zg eq pz oe dj fw sc fj nt io fi lr xu ns mx qe qt ei rq qy yw em e1 i8 sp in jv wo ci fa x6 fn ej wv oi um yi oc au yf mt te az yv yi ad yo yz -\n uv hv ty qd nv jh qj q1 ql we fv vj q7 gw fe wl vu w8 vp 6k yd r7 yj ia ey ot g6 dx tf im hu ae qx fh g9 lt fk hs su ov lo m1 cn t9 wi ki qq qw xx aw nw es yb vw yn ry pd ix em so ut oj du f2 lj qp jm w5 xh ny wg 13 wc qc ek el sx oo th uo yo at pv hn uh hj qj zk ql lw zx qv cc wv en yr w3 yy st uy iq ox an ah -\n rs pa df il iz qm k9 bu jz bi ji du lo ts yy xf cv gu mc ii rc up d8 ey pv av to yz y5 fd qx sc qv jv qr xt bk m6 ot md qq qt rq ex p2 yn sb xc fp pj qt ce wp i8 j0 wa qd gn ps qf be qt wh ky l9 za wg rp wn fh rh em vn dh vw ng wz k2 h8 f1 nz zh yz q5 zy e1 yh ad tw ep te -\n db wr a1 pk ew uu r7 d0 pn fe g8 la qq b0 ef os oh pk fm wa wk yi ev ua sq wu n5 tw xc er -\n fr yb qa hm d2 q2 o1 oj ox dm oc km kj r5 px rb et r9 y7 vs pj q0 4s lm gs qe eu ep ti tl of jq od pf nr nb ea ej yy rh u2 iu qh dk zl qv xe 2k vc vb zb xn yg gk ox tw -\n hb dc rd fu gu zd js wu xa c5 a5 w9 vo r5 d0 g3 oy ib i3 ha jm nu rr wy m8 b9 ws qu ru eg uz hw eh hg sp sf do jb zp dp nz wj wv fg ae ah ob hd dd gh dz qz xc s5 vi je n4 re bh ma wm as st tn yl -\n wi w7 om ug qa x4 yn r7 gi re n3 n7 -\n ij pa qd qg uw qm d4 z4 q8 t3 mu yp uf ia pm ez ih qn jm nu kw qr j6 qw bb yu qs rw wf re qi ru jz tm gw lh ce xa wa xs bn nl ys t1 wk r9 gu oc u4 hd ku hp yg rv as yz ro dp -\n qa ss dc gu qk cs cv fv nm z7 lf z8 xs a3 rk r5 ys eg y1 po qv cl jf xq vh we wr qr b3 bv yr wf re qo sv tj ti eg dq ic fx jz du qp vb ws nl wd wj zq vg t2 zf wb tf yu ex yi yp tj en ua ud dr pe qk dl lq qz h7 ol v8 cy vi wv ck el gd q2 yw w1 ye xq w5 gk o6 ob -\n k5 hr jk ju k3 jq q6 zg wi id bb wa gb rf rq g1 et ot pn ht dc ww c7 we c8 cm xo mg w1 1l yx tr p2 oo lm ry ao e2 i9 e4 hi tn di lv cp ca 2u t2 no ub ex rk ys pw qg av py ql qo lb pn eo wb er tb yk ie id r2 tw o8 -\n ra qa qd ph jh d2 dx d4 2z jl q5 ld cm wu wi 2t wa dd lg ui to id in uq ww rr g2 wu rl qe 1l qi qo ec yn ed uw p8 ut sj ig p4 zh xm p8 vs vg x7 ot cu l6 sx gu yp t0 gs az pe nf wl qz nj lr cy wr qv tj s7 u2 ly be br ym w2 af ri it ob -\n pp uv nb bu kz wi ah z3 c6 rg la vi oe ia ot pm dv gk 2h xq kq xg bv qr b9 j2 ec od ay p8 qi wp zd ay kg ea mq 6b qc un fh tl u5 av ub ji zx k2 wc zy 1x kc ah rw vc wv yq zv e2 rp -\n a2 iy sa ft pp un qn qz ol lf mg wo fr vu ya rk w0 pj pl el dz i3 jd su ob c8 pb id b9 ep yn ru tk s3 sh sj xa l3 wa nj ke kr ic xl bd ej rg yo f3 al f5 sw re uh h2 av cq bo vk kf bd mu wq wm ew ue tv ol tb o3 ul ov -\n iy a5 gu q2 se ls dt zf o4 dm ez jj uu ik ue w0 ya ea on ui tu rv y1 et r9 tq y5 ht dc fg i2 vs q0 av iv ku in il en ri p7 uc e2 ut sp fv qt gn f2 wo qa op v7 ws l6 wh ys zq t2 wc y3 sx yi t9 t0 ys of rq ug o2 av kn h5 ju ji ko v0 nz wn kf te dw u8 yt fn r1 ie yc it -\n qj lw ji eq oe g7 jf jc yr qo v7 p7 wd ma xg wz qb u7 w3 -\n un ol eh g5 px b8 rr og gn mx yf wv sl on jo uz -\n qg qm q5 wy eg ri bm mz d5 rx pt ek fs pi td ez ho gh q0 ll pl kq wr d0 l1 qq ko er qt wf ei p2 ru uq ye tx s4 hc zd vn ps ix zo wk t4 y3 xh ez rf u3 up ys ou xx zv qa wb at rm eu qj wv za zs eq zy rv ry tn tq yc ob -\n ss qa pp rd jf a7 lp h8 um kc q7 wl rg r3 w0 wy tl a0 ih ly qm qq m1 xg qr up ja b8 yh dc lx rw ep ea ev ay ux to p7 tp tb i3 qu gq do gr za l8 rj og oy ub e5 ae tg t0 sq tx hj ad gs it vc bh yb eb w2 yg u9 w5 fn iw si di ah hc -\n ub o0 ik ps qd q1 ga lp cf kl m3 z7 q8 ue ee ud ix g5 ib gd aq fh th pa qc pg ue ur xw ww qr vj m5 jd ng in tx ff xc er qs eo qi p2 ky pd eg e1 yr ut ib oj tb hi hk ho gm qu qi hc ou gn wg sn wh ix wj wj wg ot ra wl un e5 rg s7 t0 oc sm tz hf fx pw x0 wz th qv 1z zt n4 qb cl wn xq xr y8 rt y9 rz tb iw vb -\n qn lq bu eg iw wi 2u 3q t6 k0 yd sd ed rb eh ek yz if pu y6 in fr qc qm ob il ma b4 en wu dk nh b8 qe bb mj ws qt ho yl ug qi ea tw uh p5 eg tl yr i9 pl lh ce i7 wp qa xs dn 7p kr p7 eo vs mb pk ni yh ef rp wj ej y2 iq y6 u5 em e0 ii md jy lw nj fh bq pc xm km q2 wv k1 rx u7 ut et gj tb iw gx fw yx -\n qa pw k6 qn qg qh as u0 dy q7 wp hv 4z 4c w0 d7 et aw wr bl mx md j6 an wi qt lc yx ec tj ri fx ht in gm ua qo qa ik ys eq n7 wh rs wz wx e7 eb ak gg ip sj py ka rl su ag -\n gb uz q2 qz wr q4 z7 ia ad je am my vi zc mx ym r7 yk ua pt g6 hy y7 sx ih qx pa hp jd sy gk nh no qq yg rc 3s qy ep p2 yb oa tr eb p5 en ic yr dw tc in hk qt zp i8 lz ks ci lg x3 wd xa x5 zf yt y5 op u1 oa iw fh oc rk ay pb pw uh qg zj qh h5 nf cd nv qx kp qs qb 6q cl kh xe u7 ew tv sr as rt o4 ey tn is -\n hv qj bo ru z9 t3 lj q9 rg vi rj sd r8 g2 sj yl aq fe po pa qv jf dm qq re we la wt wu qq vt gj ei yz rr xk uk pf so pk im zu ua sg j8 sk zd sd xz zw kl wk ol um yi rh sv u5 pb tz dl oi wz h7 s8 qf wn cx f4 mo wb ed oh ee er ry eu ei oc fw -\n hn a7 cv q6 cj q8 fs jv rl qq qi od dt l5 co qr zq ex u2 ah on pr wx kp wb yh gx -\n gv qg je zg jc q8 fr r6 yn ii g1 pe sj ta el jo sr jv ni jj zr bj ns qr qi ur hz vu wh cs ep s3 hu ez rh u2 t0 dw uj oi wx n5 18 bf wb yq oh ov -\n gb dc um jr mn we bl t6 vi pj c4 d7 rb ia yz tf qn dm ke xb ft au ix tv xd qq xg rx x6 vg r8 wz op h3 qj qx lr xv qc kc wp lq ea rn rm ri eo yx -\n ra gt qs dv ik gn co qg qm qj cb qz z6 wt ji q6 dy qc b4 ws ds vu cf on yg d8 eh py hu tg qc hp qn d3 wq c9 pv pr or qq ml eo ug tw es il os fi uw to em ic oj ho px wo qp m0 qa 1o ks 7o cp wh wk wg x5 ee yn bi ef wj ns r0 ez um u1 iw eb ir fk ov s0 fl hn h1 pr x0 ux cd wz aq jp im k4 qv bp wm n0 vm u7 w4 gj tm uz te a1 -\n gv il ps db he nb wr ql kc zh tf mp lw ab us pn a0 tg pa th ps hf wc 1a yw l1 fs eq wp qr rw yv tr eh so i0 qf wf l7 wg na ou ah ay f4 io ip f8 cd h7 nj rn wb qb qn wp oj w3 w6 di id pu eo -\n hm fu pd qk bi wf q6 wu b2 q7 q8 oc lj c3 o7 6s a1 jh rg rj 2s z7 ya id ez fr gh vl cl zq hd jh xh ru c0 bz wu dl qw km kp b9 rn eu yc yc p4 ru tk ux fo ue p9 iv tv s5 do l4 cu rg w6 os fi 4b uz l7 ld l8 fx jb ee wx rp ek tg e8 uf de qh hz h4 qj gq nb wx qc sv go wm zi zo tc 3k ez ec rz ye oh ck w2 sy ia gk rm ei si dp -\n gi go z5 qz wj mg kl yh g5 y6 g9 xt p6 eh ap sa qu dw j8 ql yg aw t7 ir zj v5 v7 ba tw yq cz gc -\n ps qn z3 sw gs q4 ie gx ye wz r3 us ef d9 pb y6 tg y8 qb gc ww az c8 cb lv wy a9 qq qw l2 c8 qu uf yx qo ic de ut e4 uu tb fn oe dp wa uj bq sg mx lv v3 ya xk wd by n7 ra cp gu va yo u2 sv rk ir ya hf kc kp bo qb gp qb yc ku q3 dj o3 ey ad si o7 tw ge uc -\n dx yv ij pw a8 qm ph k9 dz q1 q3 cn wo wp my el bb uo on eh id yz am fe hy sw ha m8 vg wt vl wm qq w3 ls gj yx eo ef en ta e3 i3 zu hl m0 wd co zy l9 nn ea yj e5 rg gi fg gp u4 ir tl tz pm dd kc p9 zx sx qc qv kf ln on qm lw vn vm ew yg se as is di ro gc -\n al tt gu qf qj xo q8 c4 ws e5 ur vp ea rz g3 fw sx th db kq wt sv tb ad hv 1u gt ss xk wj qj pk rp e7 ha fk f6 dr rr hk dk nf qo lr ka ie fk cz yz q3 ym ks gl -\n gb q1 qk we q3 q4 t1 ox di ny wa ws gi ea rx yg r6 io ow y1 d8 ey ab g3 is ek pu ez dx qx th i3 jv fk io xh wt oe kr nd md pb vz wi ro se b9 tr yb p4 i5 p7 ux fp p8 sp in ok hj qy hz wa qq zd qd 3t wj aa dy 5u el yi uo go t0 u5 tl dq gd rw uf gg kb ux dj qj go wb zm lu tx vc es ev ry zt w2 tp w5 tn o6 -\n ra ps hm qf qg 4q we ql q4 z6 d1 wp vt xs tg 2s e7 r3 ys oq ef c4 av dj pn aw sr th hf gx uy wr ac zv m6 wn ko c5 qt qy yl yc uh od ri uq fz dq i7 tx fc aa uu qt oe i5 ge ce wa gb vn xg wh og ya xk fs ea yf zw wh ub 8x th iw rj ah ya e9 tl yd tx yf ii fc kx hl zj or qj mh ww kf zm lb ob qn wp ww wn ym u7 rv ie pu -\n uj by wo ml dl qx m3 8i 2y r1 u4 hj h6 qa xv rn rm -\n qa gm qh ql u9 ls e3 yk fa ts wr vj ac en id ud ke ye i7 fn tn f1 ks at me l8 kl hk lx rp ek l6 ek oi e6 wc u4 2h pn 9y zq qk ec cf yq oj vz tb o4 iw ox gx te -\n gt ub hn qd qf hq dh q1 lp qk by ql lq we wr sy wy lh z0 ge k9 w8 th vp pz yg ti fo tp r0 g4 yz ig sx im i1 jv qn q0 wq nu 5t pw wm id qe gg qt xg wh en uc tz pj e4 tv i0 ff qt gm dp jb qp cr gr qa nk ws os wf ne wd mm wf t1 vu wz t8 e7 t0 od hs dq df av km v5 kl we fg cx al ax yq y9 rx yh ul hc -\n we cf q4 cj bf ws ww yd tk ef ek y8 qv fk wt ko qe ep rt ik ut op mr j0 ej t3 s8 ir pt qk km ww cg wc gi lu n6 yr rc oc -\n ak ft gy rd hn a6 uq q2 q4 lr ia eg d1 eb on sj dj pn pp qv i4 hs gx ww xj m8 ko im rt fi tc uy tb pl qy pc uf kf kt mv l0 qj x7 oi um tf ap uk wl ql zb vj wv tk re y7 de q4 rc ad rm ul is fq yx r2 -\n dx gb he dl k9 z3 qk lf ad ch js o5 vq zl rj wr th r4 tu uo r8 fp ic g4 fs g6 im fr wq bg no wt dg ru ln rp wi yd qq xz ew i1 kq qt rq wg sb pj hk qu vx oi jm pa vi x5 wf ni ro ot oy di un y6 yo rh sb us tz ac f8 av ve h5 ji mj n2 ci rm yx ep rt 5f yq u9 rb hx aj -\n ih a3 pi mt do w7 zc nh qe wv rs xc qr ts ut pj im hp xa lv x3 ph tt sc od rr qh km oc rq xl vv jp ef st tw -\n ft ik a7 lp jz jx k4 hz wo bv q9 6t ur rl qx qv js dm fw wd re ea fd hu jc qu zo p3 lx pf wk vf fj 2o wx sb gs it ol yp di eo ro -\n tr ih yv h9 k5 qj qk nb wy gk q8 c6 mj a5 yi ur rl uy eq up yj r8 xs sh a0 ez oi y8 ly lp lx fu il ke zy cj sk xq 7u ey p2 uh yv qp rt y9 eg pf so ph tx tc uy e4 tn j0 gy ik vm ul mt nm mm x6 wj rp eh sb u4 ov of tc jt pt qj jy k1 s5 qs 7z do zi cx wq wb ma wm uw sr w5 o3 o5 su r1 yx fe -\n h9 gm cp z2 fb zd gk ve o7 mt bc wp bd rg w8 rk kx mv uu rb or yk eh r0 y5 ht pu tf se ar fj hp su m2 cb c9 c0 b3 ns qq qw rv gg ij y9 oa od of pg i7 hk do pm 2n sj wa vm zw vg yy om qx nj v9 f3 ee w5 w6 iw sp ep -\n da jq iz z1 ls z5 cg nt zk 1i gt w7 yi r3 xi yn pc fa ta ez in i2 qc uw si qw d5 kw il b4 fa ib 6c ud rq yl wg tr qp p4 ry sm ut s5 hi qi do j7 jn j0 qs iz p7 wg aq ex go ax ku nc h8 n3 v0 oc ah wm li zp rl h5 is eu o6 -\n gr sa a4 ik dm gp q1 wy m5 fw a2 t6 rj mx e9 et ej q0 ot wu em fa mj qe yg gh j2 te tj p5 eg tk em ao i7 di lh ce m9 wa wf ys eq l1 t3 ej el tf t9 rk u4 ay rw gg dr hj ac qg zk h6 dz ok zy kc mr fz iu hk yj oz ey ag id r2 ov -\n ds fu ps vo qf qn pf a8 ph q4 cb le cn h0 jg gy b9 rk r3 pj yd oe ht pu ig ez hu q0 qm nu ww qw ow xh b4 is a0 qq hu bb cn xh oo qo y0 fz ue e2 yr fa pj in sa hj ui sp nj zg wj ge wk xg ra ex vs oz eb pv tl iu x0 ln cq xx iq s7 wv qs zn tl wn lr yr r2 -\n az qf fi qn u5 we jq zh wh c4 sd is y5 po oq ki sz qe rm qy yv p4 ye tb ho 1u gq r3 pl td ov u4 hg ax zj wz wb vl vv se 5k eo -\n tt gi pd jk lq q6 wi gx mj ut ax av g7 qv zm lo 5y dh cw xo ve vy xg yv iv i0 qq xg jv l2 yi sc ga pv pn iu ug gy k3 cl oj tv yl di -\n qk se pd gt rz uu d6 io d7 tq gf em ym tu ib oe v3 si wa nm wf qf wg rk kz yd hl wx cy bp mx et -\n uv yb dd k9 ph q4 me o6 nr xa mu ld r5 rb g2 or fe pa fj hp db lu qn nr j2 bk kt xl rn wf qp tl uc dq i7 tx fv ar sf xm mx x1 zw yh lz mr t6 l9 s8 ah u6 x7 yw ol tq eo gc pi -\n ia ys jh wy sb i0 cp u3 ql kl kh -\n po al qg qm d2 gs q2 ap qz q4 q7 kv ah o8 rs 2o ex zx qw z5 r4 r8 y1 is ts y8 qc dm ll we wr sg lb jx wu jv qe ee qt qy es ed ym hw to tx hr s2 oh dt dy af di do qo oo qa w5 uz wh kh x1 t1 hk no r7 rp na sl ej op ev tj eb sb ya pb u5 tx ds o1 hz v6 lw jp k4 wv do wn aj bc wn yw to w4 as ey yk is ig rp o8 -\n rs ty q1 wt wy xp e3 wa yd d7 ht ts sz fk su kw xg bw cw qw oo fu od ix sd zu jn qd ci fi xh mo cp ev th ua e0 em kc lm cu u3 n8 xr yq ti yj fm yx tw -\n dx z2 ga kb yo sf sk fd gf i2 vs qw vj vw qe eu mz tu sp xs qs es t0 eb ak uh hl n1 v9 wv kd o8 rp -\n ds qn qj qz cb kz bo wi o6 z9 fq wq ml cv cb lf eq r6 r8 ic y4 am sz sx po jp g8 ze we wu en ew qw 3q lz kw tt ty ti e1 fx ut tc uu s6 ow gm sh qp vn l4 uj op xn wh qk wz rc wh uv um ar e7 uf az uh py h5 lq vt nz lu li lm dd rl er rt yu o4 eu yz if -\n iz ld me z1 y2 dj ar qb b4 l1 mz ij ry to ad xs sd wf eo hj wl ex ie u5 pr zj gt oi wc kg my ex zt ks yg eu aj -\n gr iy ft pq um qj dz wt gj cn ru kx q8 ws ue rk eb ee fo jb jf la ji ke qq qi qw rq yb qp il eb y0 iv ff zp l3 xm fi x4 r5 xd r0 ol wc t8 ae iw ox fk of pb qj ku xc ct wc ie xn zi wm rz w2 tb u0 su pi -\n a3 ss je un zf vq zg wo mx d7 pm gd vn eu yk tq ik fu ai qt qf j0 to yy at ii qk wz lw n7 ly -\n ub dz rv qt rm wg ea pc j9 qa mr h8 -\n a4 wp td ur px qq ki yx go wc tm an -\n po gn rf a5 uw qn q1 nn we is z8 wj ca t5 ij eb tz ef pr ix g3 ek ta a0 y6 sq pa wq cx kq qw we rt c0 mz pv py wi cw mj qt qu 0e oa tj ux pf to hr ao tx yr ts fd fv s5 ui qu j7 gw ug ss cy ks qf xm fk wg vp kt mv qj mn lk vh yt ol rf th os e8 tj ua rk on pq dg kv km wq kp ad bp os bw pb qh wp zs q6 u0 gj yu o5 if a1 -\n a6 he pu vd cd q4 jz z6 qc jc bz eh wi b1 ed ym eg fo us ib td y8 gj zm pk lc qr wu mh qw sw fi ue j0 xm kz y2 ev aj df h3 qk qc tx rr rl rc ut ad so ro tw -\n a2 pa we kk eg q7 lh zj wp 4z gi yd yg rc io ix r9 jb xe rr iz jd ij tz p9 qi l4 pa g9 s4 vi tj pb hd f4 qh qk lq qs hn ro -\n gn k9 qz aw wu ki yf e2 pk v8 xk wg 5y t3 sl u4 ya gd hn ql zr oj ig -\n fr qn qh ph k0 q2 nq wi zz rh rx ee ef uo d7 ix el fh qv dm vg px wi m8 qq gh ud qy ec fu yw uw sa tb lg us sk wf 5h qf sh vp wk qk zw qz wg aq of ys ak al f5 re f8 pr ku qx wc u1 lr qs wb f4 cw k2 ka hk mf yr w3 ro ir -\n gr qh ql wh kv e4 r4 oy lu qw pb et uj tb tn xs kr dt td t8 e8 uc xn eq yi af -\n q5 dt ed y1 am qv ut gx m7 yt rr yq yr dy sh mt wd wm th bv ym -\n tr qs ca lp uv q3 wu o5 c1 rx om ee er ta ou i1 jb rt ry os ti fc ss px jn gy jz vp ea tg ay rq u6 al de qc zt wn ez rz eq aa rm ox -\n uv ds h9 fy rf jq he qh h8 d4 wr wf du ck wi km yp ut rv io g1 rb av y4 tw a0 hy sz qx gh ha q9 qw ze 1a bz bx bv qw po ee wa qi xk ri i6 ic he tm hl sj jb qp wp jk qr kf sm l8 be x3 qu ql t1 x6 yy fg rj ua ug qg kv k1 wl v7 xx bj ae wc n3 zy tl tz zk wq re n0 yw oh yr oz eu fm do ux uc -\n hb pa h0 pg q3 mw q6 mg ls lh am sc gz al j2 wt t9 lm qe j2 rm re rr ry fl yw ux i0 2e eo bt vh ra ys sm pb on tx re ff wn wv tu rt ox ul ge -\n al fw zb p6 hi qy ay ou rg sx ag rz uy -\n bx wi kv t5 3w e7 sh ht ff nu la xo qi s3 uy jb 1o vm vi l6 be x3 ny pk aw u1 rk fx km qc be rw yn ey eo ro -\n dn q3 jf w0 e0 lh rv zv js j2 xg ld hr qe mk s3 dr kw kc dh h2 ql cg zv n3 ym yt aa as -\n ft ty qh pi d3 qz ip wu wi q8 wj a1 mg mj ut wt r3 om ua y3 ou fd dc zw lp xe cn dh ng qe kp qt mz xy ef ay od tz p8 i0 hu hz qo kw jw qf kf 3y v3 qj w0 ib ew t4 fg oc e9 ua ov hs u6 f9 h6 vj qv li wq iu yv xv rc w6 rm r2 -\n d2 d4 av jc si rs ut 5q pa mm s4 e5 tc km -\n qa uk uw qh vd d3 q4 xo wo c3 wl wa w7 w9 mc r4 y2 fp r0 tw fr g8 ae qn lp sd bk en vr gd hu j1 xv xg rw ep wh ed fu ul eb fl fz i7 ht jx ns v3 ll zs j0 op xf qf l6 l7 sn wk zw wg ej ti wb wz t6 oz rh rj uf je av dj h6 ql wl oi im v8 zr qv wn ku wb bj ef o4 yl r1 ei so if uc -\n k7 qg q5 kx oz mu wl ws rh b2 rk yh qn qe 6o 1y mj ei pf ye e1 dw hj hx do qo gc rh v2 zw x5 t1 t3 yu th e9 em au qh f0 qk km ql kp wc 5p vx bg ea ev wn wm w2 rx o3 yk ru -\n a2 gr qa az dd gm d1 k9 hr we bz lg mg ny wp xd mp yo pj uy xs ua pt g4 tw ez jv q9 qn wr nd md nf qq ng pi bb j2 eu yl ij ty sb os eh hw i6 hg m9 nj wa qs w5 qf nz zh hw wh be zo fs rz me yk y3 ub t7 t8 u2 u3 en ha fl yd hf qh qk wc 1x ze w1 oj ee w3 ey du uz id ah pi -\n o0 hn qz q5 du 2b bn a4 ex rj y1 dj yz y5 ig tf th js qv 5e gc j3 ls d8 yw bc cz tx mb wf ij ty ai as hi lh l3 qd n1 7o wf wh qh wg ot ra l6 el e5 s8 dg vm 3f 7o wn yw gj tb et -\n fu a5 cp ch hx hc rd eh tg g4 li sw sf il eg eb zj 2o cg ew uc -\n qs uc rp ml eb yd if pa c7 oq vk wu ot yq rl uz tv gb zu vm w0 sc tf ud qk wm ko yy er tw -\n ak tt ub pa pq il jq q1 hr k0 uv ql q3 kk zs q5 z8 is lh q8 w7 qw es ii av yz dl ht td g6 vs jm zr px bj no g2 g7 la c7 xt mz yx tt ym os to s1 ur ta s3 gv pl qy pc qi sh jv j8 gr l3 bi oa wd fy v1 s1 zp hg 5e t1 rp wj ms wl t5 el wm at fl e0 sq h3 dl qz ka ox tj qb wb wq re xr rx em ee yu ri do uc a1 rp -\n uv rs un qs um il ul q2 jy kl wo a1 rj tj pk ys r5 yn uo oe y4 ou y5 ar zb g0 qn gx zt lb 2v rm qy nw yz lm eg og i7 ht ss qy qi ge wf bw lv lb wh cs wk hf 7g yb zw hk ns ol rh th yo f4 rq dt uj qo fd wx nk rv ka fl mu rr q3 w2 oj ee rt w6 ru ul -\n qs hq qf qn d1 k0 q2 kk o5 na si bv mx e9 tz d8 tq dl ez qv jf zw ww wu xo b8 w3 i2 te p3 ry iz s1 ut as s5 hk wo wa l6 wh bq xk wj wd ra gu yi u1 t0 ak ai tc ax ip uj nf zb wv bd wo fz qm qj pe md ew rv gj ol yk tn iq yl is si ie r2 -\n d3 ni wr ws li mj ds sh sl qx vs rp ft ik e1 sd af ho xn wg zh 6b rp eb f3 u5 uf df py k1 wz vk vx k2 dg wm er rv rb -\n gv iz qz wo o7 k0 oq ti r9 us ib ps g0 jm jb tq ue iv pj sa cr kh t1 ot wc at e9 ys o2 ab qj ww za rn yi -\n ty qn qh nt ql la q3 kl wt q5 mp mg o9 ls lh g2 id ez sw hu qb cx nu pl kw wt vz v2 1t wi mh qr eu rm qs xb ei ij uj yb sn ai iv pj oj de hy gv ka lz pa nx wg wj kj cw zp wk eq wj t5 ns rf e6 rh ev t9 eb ir e0 sm gs gd ds f5 dd de fc f8 x0 lm qz wz xc wc kx cu wv ks lv kv wn pv ei wm ju ww yv zy yt e2 rb w6 oc o6 tq ig -\n q2 o2 gj q6 zh mg li mo vo ch tl ax ip ho wt ln ro wo qr tr tt os fo e1 de hg gb sk w5 fp kg mm l6 yi u3 fl hs u6 fx re rr dl wb jr el rw pm rt to rx w4 gx -\n yv a3 qa uz k9 q2 o2 bp ny zl mj vo tk rx ui g5 pu qx nr xt ls lm rv qs yv ik fz tv wp nk gn qh qi yf ek e5 pb au tc ac kc br qz 4t qc mx ly wm kb ez w2 u8 ei o8 -\n rf vo q1 o2 wt q5 oc 5l a2 es oe sc cx wq qw ky em tx rm p2 qo ft ed uq fs i9 ok lz v3 au xj v4 wf l3 eg ej ex wl rv qv ak mi hm cj yr w4 si -\n a2 tr qa fy qd qh oh kk lq dy bz oc jf wa k0 ip pm po qx wq cx we wr bj j5 yq qe gg rq rr oo ru od ix qt af or sj qo jn gr sk wd nc xo xl wk hh yf eh ek aq ex ar en tl ys rq pm ug ql qz wb wn yw og xw an if it -\n ak kk wu ig df w0 rb y4 fr gg i2 qb qw yf j1 ij ue s2 qt ad i5 yp ai l9 wk km ql zn yb ee rb ir te -\n hb q2 ld wt q7 qm km ws w7 vi iu yf rc g1 pr ot a9 pn dk ib qx hi qc jd jg hd q0 lo jj cn wb mz ec qp uj y9 tu yq au tp hg pl jx vx j7 wd 3y ca au rq kv qg dh k2 ok cf qa wv bp dw iu de k2 rt hj wm rc er o3 ey fn si if so -\n hq q1 qj d3 ws as ld mu rj ut d8 ey ou ib ez gf y7 qx qn vz qm vd zw ww d8 xu v1 av b6 mg gs bb g8 rm qy yv rr ry oa tb dt jb qa j0 qs l5 nz qg wj t4 td t6 eb ua s0 pn ii ac x9 qj k4 wc v9 s7 cj zy wo 10 hn yq vz u0 fm uz ux -\n ra pp qd d2 vg wj qn rh we ht st jm bv wu wi qy y9 de gw wa sb uz r1 qu pz ot td rg go e8 sn iy zr wc s0 ww ea pw ac w1 h4 w6 rp -\n fr uv pa jt qk q4 ls wu wi mt xa vu tk ed rb pe fp am sr hp qv m1 gc en yw qq qt ud ey eo p3 tj tu en ix ux ye tp ic s3 ad hz qi uf qa kr wh vd lk yn 5t u1 t0 od rr x9 f0 zj kn nk wp zs se rv ei pi -\n yb dc qs py qk nb jj ql q5 m3 mg zz e5 i1 zr lc zx xu vq hr xp by ei yx gl qi qp ij eg ai ph ap tn dy zp qp bp kf j5 ib vd eg el yy td yo ie ag em fc kx zx h8 wv sv q1 te wv vm yq ol if a1 -\n ak rd rf qg lo nq xy z6 q6 mw c1 cu z8 q7 vw wp a2 zz w8 yo uu ee yn r8 av yz y6 pp uq dv i3 db jv q9 2l xg rp ib lj sq tx tc mj mk qr rw te p3 ik eg pj e3 i9 im tb tn fm na j7 lj wa ct rg v4 he x3 kl bi r7 wj y1 l6 wk el 9f t6 gu tj od e9 tz re uh o2 zk ki cf lw jp sc s6 qs qb yn yw ms md w3 rv as yi ox du yz ir yc dp hc -\n yv gy ik k5 db gm ux qj gc w8 ea g6 dx po jb 5t pv wi rz qp jv v1 ea en al ii dt qj py w4 if ux -\n iy pd yg qq p4 in qa y1 yy ta fb zk s6 lu -\n ql ws rv yj jk ke lm ff lb fx s4 av uv wl n1 rv dp -\n qh d3 rs ih rc aq we 7y ud t3 h2 zt cu oc -\n fr hn k6 je q3 k4 tm zh lj aj li a4 t7 w7 kx ut pl rc ih th hp wq pl ls ma oe lf wu l1 ve lp qt qy fi ti he oh hi ow tm cu p7 nr va r3 tt wk wc s8 s0 hs al o2 hk x0 qj lm v5 wl qz 7u iw os lu ah wm hk e1 o4 rm fq ro so -\n gr sa rd um pf ca ga ql qz wt ld z6 vw kv my xt cn wr eq tk rw fe qx qc qr qr rk qa qi ex p3 p5 dq ff pc sh cy oq v4 wg s7 yo ya od rq dt un bw zm da bg q5 ru ah -\n iy qd k6 dm oj qz zd vr w0 r7 d8 et y1 eg yj gz qq p3 il i8 ge wp sx s1 wj t4 lm jo qp pw xc vm sr uz ig -\n qf u8 iq rg rk g4 im ih oq fp a0 ib tc uj tn nc kz ll u1 un qv ck lv vv -\n a4 df um jg z2 lq wr tn xu id wo ez rk rz ew d5 ti ix to r0 sk fa fe fr hp jk wr v1 ms wu jm rc qt tr ex uk vr to tz ut hl sg hb qd xn rj jc 6x mo wz rp ek y4 oi oo ae sc e9 od pn hg wz js qv ln ju rx yg as rn gk o7 so it -\n dd qs qd py k9 lw wt db gk zf nw wh t2 nf zx w8 rj ue w0 tl ew r6 ui ef g1 or ej pn an tq ou ff qv gj jv q0 kq 2l uo oe ku wi w1 tx qe rv 1l ws eu lv p4 ru fo tz ph ib fv uu i3 qu oe pc gw wp sx zd kw w6 bo w9 cs cw my qz zf rg fh e8 ay yd fz rw fc ng qz cg wx qp oz qv ly ha cx al iu rt mp e1 ee w4 tn ul ru o6 ag aj -\n qa kv e5 bm yj j4 m5 rj qe ri ht oi qf qe t6 e5 aw t8 wc dw un yb -\n iy jg jj d4 ju ol wy bs wk wq t8 a9 y4 ta dx jp qc q9 su si ut q0 m8 qq 4a zy qq zq sw po qe qt la sn p6 ht oj hy hh qi gc bn w7 au ya kz na oa ox ov je fb or wl qx ze cg we fk tz tv cg uq w6 -\n dx sa qa qs db go lo z3 lf ox jc wj 93 tj tk yf ii ef fo ua sk g5 fs el oi fr sx fg se q9 xq qw j3 m6 4h qq l2 eu rq qu qi hs uh p6 ix fx qa i0 wf ke bq ne wh xl ms un ex sx yi ua pb s0 rq ak ao fc pr qj cw wq vy wv u2 3h wq re yw eb gl eu -\n hv gv il jd go qh d3 we q4 q5 ej lk my gv a2 ds ex e7 rk yf fq pu ff qx ho js gx j1 qe kw gm ja ns wy ln d0 cz xt te xu tt sb em ix ic p0 i8 im ui di gt ws os r1 qy pz l6 e5 e6 op sb pv sn ii rr v6 ql le zy pv mt da yq ol w6 yz ag it -\n ds gy dg jf qg uw qj uv q5 q8 q8 q0 qm e5 rk yd tz pv if qn ju cv xy ki qw mj ls yv ru of tz so yr oq qi m9 sc kw qf zh jx wh kg l0 t1 pz un fj os ha sn f3 e0 om ab cw ct nj zy wn fl ww vn fn tn ie ov -\n co qh jr jj cb bi wt q6 ra qm zx ur r5 an fw tg jm re j5 vl em sl xz qe wa 45 nq ex yb ed ef ph e4 tb s6 qy lz cu gm pd gq mc ca 85 yf wf hu sb eb fk fv dt cq lq qz ww wv xr n0 eq ok er et iw r2 o7 fe it -\n ra dx og wd wt o9 i4 it pk qo ic dt hj jl oq sf lz wd ca hi fg f5 ap x9 gq nd iy q6 ep -\n o9 gb a5 z5 q6 wu w0 tl r9 dj if ts ig it zq ll qw qy ep ed ry p5 ut hh dr i0 hl qp qa zs wd ya ot xk s7 e9 om io dl ki k2 wv q1 5g er rb rm -\n qs w0 om ed tk ta th gf ii av og 6o ee o8 -\n po ty rf qf he qc hz bv c5 mi rh ew tu ef sh ix r0 d0 pb tq fw ig g8 fh i2 uw hf qw qr sf cn wi fh qt hp nq yv fy tj od ux ut fb hu tn qt oi qs oa xm dm fa nm qx yy oo ec ev ox sb ya rk ys ud jy dz zv qc zb qb 17 tb lt yt u9 w4 rb st et ry yl o5 di ux -\n gb gt az uk gm qh d3 bt qz wd ld o3 bl cm q7 ck k7 we b2 yd ua ew tl rq yg us tq js fk jb gz jn jg qq in qr rq qy 3h c0 qp p3 yn ef sb ym jl sf sh hv qd p4 fj od ix kz ni wj wz tg t0 tj e0 sm om kx ku cw nb zc aw cy qv bq kg wq pn zz wv cd wn yr se o6 pu eo gc -\n gy rf qf k6 qh qj cd q3 kk cj fw b4 fr mj w7 bm wr ya z7 wt w0 r8 ip ti is pn am y5 qb hs jf qw uu wr np qt wi 1t bx qq qw aw er cv qy rw eo oa iz fp iv qi jm nk kr qf xm eo nr w0 qj bi t3 uv wk ek wn ex e5 rf rh ga it f5 pm hm f8 qj gy jp le wc qc lt s9 zu lb q1 ju w1 uw w3 oj tn tq ir r2 te -\n ak gn pw a5 qh k9 qk nb 2l qz wr kv mt gt w7 zx ii oe ug ix sz qx qc ar sr zb su vg qe np yg qt yj sv uk pd uq pf of eh jb sk gy ws ke kd be og kh x6 me wz e6 yo iw ah rw pm rr qg pt lm ql qz wz qs ly wb wn q3 ry rx gj ia o5 ge -\n gy qm d3 q3 ia c1 ta ex e5 e8 eg sy cl jf qe nj nh m9 qa w6 ek iw kv qg ab n4 w5 iq do -\n uv gv qa un az jd qm eg iw nr q8 zj ny c5 vu rl rx yn et ia ua is ot pt hu im gj qv vv xe xu 1g wo vt qt st qy rw qi wh ft es uk hw to p8 fn f1 hp qu sk p2 l4 zf qf g0 fi l7 be ky iv mn xp nn dt 6b ro kw uv ra tp e6 sv s8 sn tl fz iy qh hz ve v8 h8 th wc wy qb xm s9 hs wq zs yq tu en zt w4 dp yc -\n sa ak gi hw qm gp pp qz fm id lh 6w 9h xr w7 ui ow rb oe ia us fq g5 y5 ig oi y6 im gk ze qe gn 3o ye xz jh qe db qi p1 ep re op te y9 os pj e3 p0 zp qa ih l4 cu zg wg sn rh lf fz ic kh ni wh vh wx th ag u3 f6 uj jy k3 2a wc kv lu wo hb eq w1 rb xw yo ei o7 gx -\n hw wd r0 g4 sz b7 pi sn tc in qt zs rg eu -\n iy hv hb hq qf z3 q2 xy ia tm zf jw wq a3 rk w0 d6 eg et is id po tg gh ob jj wr np no wt ja wy xl l2 yr bt bb tc cx qr rn qt lc rw sy ex y0 ru od to tz og ye hr pj de tv e4 qt ad oo qa jq fj l7 fo wh nt pl ro wl vp yy tf va e6 fg th ar s7 os at s8 re df pe hj f0 qx qc x7 lu nc zo tx bf ww pq cg w1 rx id pu it fe -\n qf fi ld 4o ge da t5 zz mo zx vi ui w9 pj rl rz r4 uy r5 sf av ot fq tw sc zv g9 qv i4 ut iy m1 wy xo b0 ud cb qy rq ug wg sn fo ix uc aa i9 ss sd di sh j8 qp xa ep w0 7h wl t7 iw tj ya hs e0 fz hd u6 hh dr dh uk qj qk wx ol xb qv wb s8 15 wy zm jr nx it eo fx ww yv zy to ee yu yi iq ey iw rm uz yz ob -\n po ra ik qd qf je jr lp wr ji ne wu 2b nt wa e7 rx xo ia yl ta ig ff hp gk jf q9 vd si gc qe rk wu by yg rq sb os fu eh em ux ic ao pj hy im du qy cr 2e l5 qw v1 lf mv wk rx dy rp ra rg gi eb hs au ap bo oa sn zi f6 h1 zt ey yz do -\n lj vu y2 if qr wr ta so kg 3k ol u1 f5 -\n yb cv mq lf zz ue ui dx qe tv qu ex tz hh tb dy ds wi rb -\n hb gt qa h9 rf qf qg k9 q1 lo q3 ql z8 gl zg q8 1t wo vr rg ez ws mo w9 yo r3 yd e9 ea sf a0 im tg y8 ar qb ni wr qr wv m4 ix d9 nz yq fa if yr bt qr mz qi ea rr xx at ic pk qi za hv kq w7 co xj wd x4 zs wh y3 rf ec u2 e9 ah s0 uh io un h8 iq zb bs nr be zo fz vb wm pr yw md rc ur er ia yl ox ei ux eo tw o8 -\n qs rd rh yf px ow d8 tq ig ih cl qr yw qq in qy wc ek rh ya qg cd x9 qm lq to o3 ul a1 -\n a2 cp as gk kc 4u zj z2 t5 zz rb eh sh sx fg fh g9 hp vd gx oq cv pe wv b8 qe cc nw tr il tl e3 qu l4 mt wk wh aq td e9 gf lm qz bu jq my wp vb dg y0 ye yq w6 r1 -\n ij az qn ph qv bv bf mz iu is y8 ar fh q9 hd qq ji sf ld up qo p2 at sp in qi nk l5 e0 rw px 5o ew oc te -\n qd jr pa q5 w9 hw hu y8 dn qn zw wr ma ei xl dq i7 i9 vb sx wf wh na wl um e7 s0 h2 nh nk fj yl wn iu u7 as ad ey so uc -\n la up ic g5 ay ic x8 u2 ar eb wb yr aj -\n po da rd un qg uw lq m2 4r wg q8 z9 t3 zc d9 ae st q0 li qw wt kr qu ry en sm qf kf kh ny yt gi rh u5 em tc kv h8 qx lr jq ef -\n a2 dh qh q1 h8 qz z6 kx z8 bv 3q df pk tl d8 tq tf g8 zb qw 5p zm qe cv yb ec uz iv e2 gq wp uh kq ws lc wk x3 t8 rj fc io je dk lr lt wv wt bw be eo q4 ye yy rv ok yo yp ir ig -\n rs ij ty ps ul wr bh kb rs z4 z8 er px uo up y1 rb fo jo gg dv ph q0 jn xw ww d8 rp yd yf tx b0 op yn of jl tm px fm jc zf qd pk wh rp uv tp t9 ir yf ug qg v5 ku qz fd k4 cu mw zn iu bg lq ly rv su -\n ik dm cm or tw pu lp eh qd kk j0 em ng tw -\n ra ds qk cg q7 k6 4p t6 yu lq go yd eq r8 fw am dm xy cm v1 cz eo qi ij yn eg hq tc sj qa i0 oa l6 p6 wj vd wf mr yt ex e6 yo t9 ev s8 en rr bq kg hb lm re bj ms w1 et du -\n o9 gy bl wz t8 hq iu ix av y5 y8 jn j1 np xt t9 vw qq 43 xv 9w yi ft es hy op lg vs hg wd ef wx ou ox sw dr ze xr st fm ah -\n a3 wr fb jc c2 w8 rx fe q9 hd xq qq wi te y9 e1 qt qi qs nl ca bh u2 md tv hx -\n ra jh q3 aa t3 1o eq lh rv fo us pt dj pm pi qn zr bj xj cm ix a0 ra hi eu nw yc p3 ru ri ue e2 jl hi wo g9 xn qh wl wx go yp rr dj nd ch u3 fj bd jy vn w1 ia ox tm uz -\n ma y5 bl qi g7 ri fl ap 7a yo ko rp -\n um dh pg wq r5 sf ia ta an hs ne q9 wt sh rk yi ym of tb oq do hv wj ic oi sc pe sc wq wb wm tq -\n hq qm gg q4 xu k4 k7 uo wt kb et fo ey aw g0 xi am in qy eo qi eb ay ue og nt kl wx s9 df qg f9 v6 rv sv pc tl my bj wb eq h4 o3 ri -\n tt uq qh nb qz wt jx ya on om io ow ha qp e2 fd e4 hp hx p2 vm xg xn ra l8 iu yf jr qh k4 1l oa tk zp yw rz sy ul yx eo ep -\n ij yb qs fi ul qk by ql jl wr bi q5 bl tm q7 xp k7 vy gi tj rl rx yf ym tu er r8 pe ip ej y4 fd jn gx zt vj xt xh rj kt cm ri nh zq pp eu rw to pg e1 ue dw e2 so tb gm qi jb nk gy pa v1 xj fl kh 3k kl ed wx wc ek yy ez wc iq yo u4 it tz ak hn f8 h2 hl uv wz h8 gi nk ch zt wt bw kn yq e1 tv zp ag it -\n qs rf jq go qh a8 jj xt le wu qq yd d6 d0 ff qc su c7 lc wp ty y0 tu ti pf ta aa ug vb rf vi rx no un yu e5 7r up rj ag ha hs fc wz bo qv bp tv ki er -\n yb qa rd lo ok q6 o6 ba r6 ow or yl am aq gd dc ho cx c0 wu g4 ib c5 ep re qo ed yw iv ta hy jc pn xs oq xn bo zg ps kr iz yp wh wj xl xo zq me na ek wl wx wc rg uo ir tk aj da rw hm io uj fb jt qj dk nh zx jp qx wc zr zy zu nc xe ww wv vn h2 q6 en ew ad yl af eo if r2 -\n yb o0 dn z1 q1 sw qk po xt wr ls z8 ox wu jd ro q8 lh mh wa rg ea c3 pz tu g2 is a9 pn tw po qc sa jj vh ax xh kt wy jv mg nh kp b9 qi od ht e4 uu ij qf sf nc wk ap wd qz rd yi s7 tk pb it f7 o2 f9 kv qh h4 ln wz gi qd zn xm sn 39 yn rz u7 yr yj is ie ag ir tw -\n po dx uv pq jd dm d2 hr cs gs d3 q4 wr np ab di mh vt w7 w0 on tl ia ta aq dz y7 i1 qc pg q0 wq j2 c7 la cb il wr lv na ru 4g vz nf bc sl qq qp qt ud rq wg ex uw he tz ye p9 ui ou vu at ix w0 vf bi ed yh wh ra y6 wc u1 t8 fj ov iy tz kx h1 hj jy qj h6 ng jo wz cj ie mt rq te n9 mp ma q5 8w rb o7 sp ob -\n po ra dg ca qj q2 is kn rd ws lq tu ym yl y5 tg pp qw we cn a7 1t jd m7 wo yr sz qa hp ei qy ec p4 hw p7 to au iv ht qt qy qo l4 lz xm wd yf wg ez s7 en f3 tx yf rr f8 cw ji qv yz ry ew w2 oj w4 w5 st rn oz ri o6 dp aj -\n gm jg ju q5 np lf q7 xo 1y qn k0 jo pp qx th q9 4f or ro pu bv eu nw uq ao dy jb j9 gr rd nz wj wj r9 co ta rk od dd gg hm df pr km ng oj qc sb qd wb tz cq ex wb vb ty eq tv iw -\n fu uq co qk jl cg ld lg wo vr gc bd rj r3 yd rz iu ew d6 io to sh y7 jp db dn qn qm si xg qr ls jo lr wy rk wn m7 qu bb es op qp ru en ta e3 in hy dy hl vc gc gt jw ke 2t wh rk lj hg oy e6 yo ev em fz rw pq re dg qk ku oi qz k4 qv li rq n8 ec 4d yb wb e1 iw id o6 ir do ux pi ep -\n a2 wu jd ef dc mn 5e qp pl xd ag ay -\n yv o9 al a5 uq qg jw pi z2 jt cd q5 3m zl ez vu rg jl rz yf ix sj fp d0 tq ff ha hs zw om ni m0 xg c8 a7 ki qw cc ei xg j3 tt tu il p6 ix tp tx ib sp hg p0 fc pj su qu jv sj lk qp ws qs gm 1x mx lv wj qu l1 dt wh wv un aq fg rg e6 uo ar ie up it sw tx f6 o2 h3 qc qa ho vj u3 kd zy n6 my ww vc lr em w2 se rt o4 yc a1 te -\n qs dv pa ty iz uw qh jj z3 tn jc eg e4 qq w7 ut r3 uu kb up g1 fo iv if fd gd sc qm qe xg ia wb he ky hu tv rw qu rr es p3 ue s2 as i0 dt qt hz jm j0 gy ci fi hw nv ea kk vf rx 68 ti rp wl oi vp at om io uk pt qh qj dl cf lr cx wq ku ki w2 yh af ul sp yc it -\n ub yb dc ty gm dm go nv we ql by la o1 ju o3 jx fm aj wa rg e4 vi a6 r4 xo tz oe ip pv dk tq a0 tf fg tg i4 pz sd ry ky mg hy g7 eu qy yi rw qp eg yw hw sm uc i7 dw fx s3 sf zo m9 xs vn rf ci nz kr qt 9y pj lk ee pz ef rk e0 fx uf az fc qg jr oy lq cg qp um ad wc zn bw n6 my xr mp tu en o3 iq ir ro -\n da a3 d1 jr dz ca ql nu q3 cf o6 nr mt lk yr rs lp w7 a5 pj ys ym r8 ey to fs dz im ih sw qx qv zn gl j1 xe lc zc vw 6a mh b9 qt rm re oo qp p4 tk ix p7 og tz yr sp aa hk ih lx qd mx 4n kk vf el oo td ae yo fj uf pr hl qj qk wr qc qv kf yz my wq hn zs dr ee u8 rv et ru ie ag tw -\n gt ph z0 zl mu ui av zm om ui vh qr he qr es fl ws w6 nc ra rk kp ol wm yu it -\n dd df jq jd ux ql el 3r ya uu iu ee eh g3 sj us ib pp qc jv hd bh zt uo d8 b3 xu bc rq te uh ex tt eb il qu pc ge sj qp ih xf 3r gr yh qx tu wl wn sz up ay it ab jt qz v7 wn li za 6o w3 fn yk eu ie gz ro -\n yv o9 qf eg eh mh jh rh r3 rv ix y3 a0 sr qc qq qr wr qe bx ki m8 mk qi lm uk eb ai ur e2 xd nc ca eo mb ed uv rs up ya of hn lw wz qz et qh wm zr rc o3 r2 -\n ss qg qj ph qk q2 cs z4 bi qc cj q8 qn w7 rj ys ea r4 uy om rc ii fp sj ej yz el qx qv zn gk q9 hs m2 ii d7 nk c8 j4 qq dl gg pp ei qo yc od fo eh fp ta hy ok tv uu us dp qf sb zg ks sg n3 wh x2 nr cs wk kh wk wf ew 7h 7k oy t6 gi rg yp s9 ya e9 pb tc dt dh hk h2 pt qh h7 wz n1 qv kd pm cc xr kp yk tm ge -\n gr qa ft tt gn qs pw pu ca ph ls cg cn zf bz q7 z0 c3 qn gv w7 rg ut e8 ii er ip sg y3 oy ek ht gd qx g0 qv db su jn qq lz uu jo ru an wi kn sq nh qr qy yx eo qi xz y9 ru pd au p7 dq he ut ok fd jz ui hc j9 l3 hb xd jq gy kh wf xs sl rs aq ez y4 ts um yi e0 gh dk py v5 qk ql ko jq wc nk v0 wb qv br iu wm 6p sr gk yp pu -\n o0 pa pf z3 jt jy z7 cm ne w8 yz fd fg zn qq ll vg wr wb ia xx yj ty eh e1 so ts tc s4 i0 tn wo wp wa op va wk x3 vg qx rs sn au f3 tz sq hn rr o2 fv un k2 vj ey dj -\n iy ra ij ty a4 un rf qg dm jr kj uv we cv gk wy z8 oc wp mo jl mz ev ch rv tu ax y2 g3 oy y6 im uq qv hp qb hd iy lp nk w2 bb ho ep tr os en sm p8 p9 hy ss ui gm qi oo vn ae qd w6 ps dn wd wg ro mr yt ol oz rg s7 u5 tl yd rr ax f0 cq ku qx ze n4 wn kt ca jy bg yc zs yw rz w1 eu rm r1 -\n hv fu ca q8 mt la r3 pl yh to sy yh tv x4 tg yp ov wn ze sp -\n o9 qa az gm qd pw hq pd ga qj cd q3 jk pd du c2 zk xf t8 eq om es rc ua y3 pu ig qx se qv db st qn ii lx qe wt xk nx ku br qe qr qt rm eu xf xb rn qu qi ep qo rr ex xk p5 ym fi uq to ux ix ai hj gn zi oq qf kd wf xn kr w8 rl kk mq rp rf u1 s7 oa fh e7 yp e0 pr ql sx ck ag kg kt mp eb rl em ee w6 du rm yz if ep -\n qs pi am 6a ut r4 ii sd ua ib y6 pa kt pb wm qq dz qt qp y0 he p8 ue tb qu or qo wh 40 8j t4 sl rf iu gh hh qc iq bf rl wm mf oh ew is dp -\n da ps a7 jr z4 q3 bu xt ip jx q6 np z7 bp lg bz ye wo ig bb ww rf om uu ef r8 ey pt ta pn y7 gg th dn pg qb ri qq 42 qw zw b9 b0 xb qt qy yl wh xk ft at yw i7 sp de pz fn qy si m0 ik wf sg cq ql hk er eg lc ek na wc iw ir rk ua e0 ak iu sw ap uj av ab hl uv zc qa wc jw vj qv vk ay kg xw on rw 1b wn rl eb vm eq h4 yt oz eu uz -\n a2 qa rd cp qh ub vg ws u0 4g bp da yo ev kz eq uu ee ef yk pr sj sk fe oi lt uy j2 gn io vk ns 27 ln wu ve yr l2 qu qi ry il ul tj eg ux i5 yr tx ph oj gq or zp wa qs gy iz v0 qt wj ic ca lh yb np ej sl td l8 yi iw s8 e8 ys yd sq al dt pt tg te yb eu tq r1 ir fe -\n tr h9 go qj b1 wu q7 zh el tg mb ys ed ii er r8 xs pe r0 sw db ov m7 d3 re no nu zr pq ji wr lc or qw ee yy qr rn rm re qi te ea qo yb yn y0 uz uq iz tl yr i0 fm wp qs qd he wk kl ew as hl ez oo ox ie s0 f4 dl wq wl ww lr qc qv vk mt my kn ep em yt sy am so rp sp -\n ak ft qg bg ji q6 bk xi tf mo ur pj yk ua qv wq gc qe ke ef eb tk uq i6 oh iv gb qs rx el yo rr pe wz wx ho xq tc mo yk du r1 tq oc hc te -\n ra ub qj jh jt dx ql q6 da tf r3 ew iu sg tp yl el gc 6n tt ry pd ye ff lz kt yp fl yf dl rn -\n o9 hb h9 qd dh qg q1 qj jy se q5 wt nr qv ge c5 el 6y uo rv ax pe et r0 fe y6 dx qx ha qq lo we zy v1 wy dl vr wa qr rm qi qp yn tz pg ph de p0 do qp wp wf bw xh ky xz wh hl to ek rd sv rj rq re h1 qg qh kl f1 zm 18 ez xe vm en 5j o3 rn fw fe it -\n db c2 bb o0 w8 kl kc y4 qx zm pk cw id ve mh lp rq jb fl x1 qi wd lx f3 cy bq dd ye fn ig diff --git a/contrib/tsearch2/expected/tsearch2.out b/contrib/tsearch2/expected/tsearch2.out deleted file mode 100644 index e84e4d27f2..0000000000 --- a/contrib/tsearch2/expected/tsearch2.out +++ /dev/null @@ -1,2969 +0,0 @@ -CREATE EXTENSION tsearch2; ---tsvector -SELECT '1'::tsvector; - tsvector ----------- - '1' -(1 row) - -SELECT '1 '::tsvector; - tsvector ----------- - '1' -(1 row) - -SELECT ' 1'::tsvector; - tsvector ----------- - '1' -(1 row) - -SELECT ' 1 '::tsvector; - tsvector ----------- - '1' -(1 row) - -SELECT '1 2'::tsvector; - tsvector ----------- - '1' '2' -(1 row) - -SELECT '''1 2'''::tsvector; - tsvector ----------- - '1 2' -(1 row) - -SELECT E'''1 \\''2'''::tsvector; - tsvector ----------- - '1 ''2' -(1 row) - -SELECT E'''1 \\''2''3'::tsvector; - tsvector -------------- - '1 ''2' '3' -(1 row) - -SELECT E'''1 \\''2'' 3'::tsvector; - tsvector -------------- - '1 ''2' '3' -(1 row) - -SELECT E'''1 \\''2'' '' 3'' 4 '::tsvector; - tsvector ------------------- - ' 3' '1 ''2' '4' -(1 row) - -select '''w'':4A,3B,2C,1D,5 a:8'; - ?column? ------------------------ - 'w':4A,3B,2C,1D,5 a:8 -(1 row) - -select 'a:3A b:2a'::tsvector || 'ba:1234 a:1B'; - ?column? ----------------------------- - 'a':3A,4B 'b':2A 'ba':1237 -(1 row) - -select setweight('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd zxc:81,567,222A'::tsvector, 'c'); - setweight ----------------------------------------------------------- - 'a':1C,3C 'asd':1C 'w':5C,6C,12C,13C 'zxc':81C,222C,567C -(1 row) - -select strip('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd'::tsvector); - strip ---------------- - 'a' 'asd' 'w' -(1 row) - ---tsquery -SELECT '1'::tsquery; - tsquery ---------- - '1' -(1 row) - -SELECT '1 '::tsquery; - tsquery ---------- - '1' -(1 row) - -SELECT ' 1'::tsquery; - tsquery ---------- - '1' -(1 row) - -SELECT ' 1 '::tsquery; - tsquery ---------- - '1' -(1 row) - -SELECT '''1 2'''::tsquery; - tsquery ---------- - '1 2' -(1 row) - -SELECT E'''1 \\''2'''::tsquery; - tsquery ---------- - '1 ''2' -(1 row) - -SELECT '!1'::tsquery; - tsquery ---------- - !'1' -(1 row) - -SELECT '1|2'::tsquery; - tsquery ------------ - '1' | '2' -(1 row) - -SELECT '1|!2'::tsquery; - tsquery ------------- - '1' | !'2' -(1 row) - -SELECT '!1|2'::tsquery; - tsquery ------------- - !'1' | '2' -(1 row) - -SELECT '!1|!2'::tsquery; - tsquery -------------- - !'1' | !'2' -(1 row) - -SELECT '!(!1|!2)'::tsquery; - tsquery ------------------- - !( !'1' | !'2' ) -(1 row) - -SELECT '!(!1|2)'::tsquery; - tsquery ------------------ - !( !'1' | '2' ) -(1 row) - -SELECT '!(1|!2)'::tsquery; - tsquery ------------------ - !( '1' | !'2' ) -(1 row) - -SELECT '!(1|2)'::tsquery; - tsquery ----------------- - !( '1' | '2' ) -(1 row) - -SELECT '1&2'::tsquery; - tsquery ------------ - '1' & '2' -(1 row) - -SELECT '!1&2'::tsquery; - tsquery ------------- - !'1' & '2' -(1 row) - -SELECT '1&!2'::tsquery; - tsquery ------------- - '1' & !'2' -(1 row) - -SELECT '!1&!2'::tsquery; - tsquery -------------- - !'1' & !'2' -(1 row) - -SELECT '(1&2)'::tsquery; - tsquery ------------ - '1' & '2' -(1 row) - -SELECT '1&(2)'::tsquery; - tsquery ------------ - '1' & '2' -(1 row) - -SELECT '!(1)&2'::tsquery; - tsquery ------------- - !'1' & '2' -(1 row) - -SELECT '!(1&2)'::tsquery; - tsquery ----------------- - !( '1' & '2' ) -(1 row) - -SELECT '1|2&3'::tsquery; - tsquery ------------------ - '1' | '2' & '3' -(1 row) - -SELECT '1|(2&3)'::tsquery; - tsquery ------------------ - '1' | '2' & '3' -(1 row) - -SELECT '(1|2)&3'::tsquery; - tsquery ---------------------- - ( '1' | '2' ) & '3' -(1 row) - -SELECT '1|2&!3'::tsquery; - tsquery ------------------- - '1' | '2' & !'3' -(1 row) - -SELECT '1|!2&3'::tsquery; - tsquery ------------------- - '1' | !'2' & '3' -(1 row) - -SELECT '!1|2&3'::tsquery; - tsquery ------------------- - !'1' | '2' & '3' -(1 row) - -SELECT '!1|(2&3)'::tsquery; - tsquery ------------------- - !'1' | '2' & '3' -(1 row) - -SELECT '!(1|2)&3'::tsquery; - tsquery ----------------------- - !( '1' | '2' ) & '3' -(1 row) - -SELECT '(!1|2)&3'::tsquery; - tsquery ----------------------- - ( !'1' | '2' ) & '3' -(1 row) - -SELECT '1|(2|(4|(5|6)))'::tsquery; - tsquery ------------------------------ - '1' | '2' | '4' | '5' | '6' -(1 row) - -SELECT '1|2|4|5|6'::tsquery; - tsquery ------------------------------ - '1' | '2' | '4' | '5' | '6' -(1 row) - -SELECT '1&(2&(4&(5&6)))'::tsquery; - tsquery ------------------------------ - '1' & '2' & '4' & '5' & '6' -(1 row) - -SELECT '1&2&4&5&6'::tsquery; - tsquery ------------------------------ - '1' & '2' & '4' & '5' & '6' -(1 row) - -SELECT '1&(2&(4&(5|6)))'::tsquery; - tsquery ---------------------------------- - '1' & '2' & '4' & ( '5' | '6' ) -(1 row) - -SELECT '1&(2&(4&(5|!6)))'::tsquery; - tsquery ----------------------------------- - '1' & '2' & '4' & ( '5' | !'6' ) -(1 row) - -SELECT E'1&(''2''&('' 4''&(\\|5 | ''6 \\'' !|&'')))'::tsquery; - tsquery ------------------------------------------- - '1' & '2' & ' 4' & ( '|5' | '6 '' !|&' ) -(1 row) - -SELECT '''the wether'':dc & '' sKies '':BC & a:d b:a'; - ?column? ------------------------------------------- - 'the wether':dc & ' sKies ':BC & a:d b:a -(1 row) - -select 'a' < 'b & c'::tsquery; - ?column? ----------- - t -(1 row) - -select 'a' > 'b & c'::tsquery; - ?column? ----------- - f -(1 row) - -select 'a | f' < 'b & c'::tsquery; - ?column? ----------- - t -(1 row) - -select 'a | ff' < 'b & c'::tsquery; - ?column? ----------- - f -(1 row) - -select 'a | f | g' < 'b & c'::tsquery; - ?column? ----------- - f -(1 row) - -select numnode( 'new'::tsquery ); - numnode ---------- - 1 -(1 row) - -select numnode( 'new & york'::tsquery ); - numnode ---------- - 3 -(1 row) - -select numnode( 'new & york | qwery'::tsquery ); - numnode ---------- - 5 -(1 row) - -create table test_tsquery (txtkeyword text, txtsample text); -\set ECHO none -alter table test_tsquery add column keyword tsquery; -update test_tsquery set keyword = to_tsquery('english', txtkeyword); -alter table test_tsquery add column sample tsquery; -update test_tsquery set sample = to_tsquery('english', txtsample::text); -create unique index bt_tsq on test_tsquery (keyword); -select count(*) from test_tsquery where keyword < 'new & york'; - count -------- - 1 -(1 row) - -select count(*) from test_tsquery where keyword <= 'new & york'; - count -------- - 2 -(1 row) - -select count(*) from test_tsquery where keyword = 'new & york'; - count -------- - 1 -(1 row) - -select count(*) from test_tsquery where keyword >= 'new & york'; - count -------- - 3 -(1 row) - -select count(*) from test_tsquery where keyword > 'new & york'; - count -------- - 2 -(1 row) - -set enable_seqscan=off; -select count(*) from test_tsquery where keyword < 'new & york'; - count -------- - 1 -(1 row) - -select count(*) from test_tsquery where keyword <= 'new & york'; - count -------- - 2 -(1 row) - -select count(*) from test_tsquery where keyword = 'new & york'; - count -------- - 1 -(1 row) - -select count(*) from test_tsquery where keyword >= 'new & york'; - count -------- - 3 -(1 row) - -select count(*) from test_tsquery where keyword > 'new & york'; - count -------- - 2 -(1 row) - -set enable_seqscan=on; -select rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); - rewrite ------------------------------------------------------------------------------- - 'foo' & 'bar' & 'qq' & ( 'city' & 'new' & 'york' | 'nyc' | 'big' & 'apple' ) -(1 row) - -select rewrite('moscow', 'select keyword, sample from test_tsquery'::text ); - rewrite ---------------------- - 'moskva' | 'moscow' -(1 row) - -select rewrite('moscow & hotel', 'select keyword, sample from test_tsquery'::text ); - rewrite ------------------------------------ - 'hotel' & ( 'moskva' | 'moscow' ) -(1 row) - -select rewrite('bar & new & qq & foo & york', 'select keyword, sample from test_tsquery'::text ); - rewrite ---------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' ) -(1 row) - -select rewrite( ARRAY['moscow', keyword, sample] ) from test_tsquery; - rewrite ---------------------- - 'moskva' | 'moscow' -(1 row) - -select rewrite( ARRAY['moscow & hotel', keyword, sample] ) from test_tsquery; - rewrite ------------------------------------ - ( 'moskva' | 'moscow' ) & 'hotel' -(1 row) - -select rewrite( ARRAY['bar & new & qq & foo & york', keyword, sample] ) from test_tsquery; - rewrite ---------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' ) -(1 row) - -select keyword from test_tsquery where keyword @> 'new'; - keyword ----------------- - 'new' & 'york' -(1 row) - -select keyword from test_tsquery where keyword @> 'moscow'; - keyword ----------- - 'moscow' -(1 row) - -select keyword from test_tsquery where keyword <@ 'new'; - keyword ---------- -(0 rows) - -select keyword from test_tsquery where keyword <@ 'moscow'; - keyword ----------- - 'moscow' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; - rewrite ---------------------- - 'moskva' | 'moscow' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; - rewrite ------------------------------------ - ( 'moskva' | 'moscow' ) & 'hotel' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; - rewrite ---------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' ) -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; - rewrite ---------------------- - 'moskva' | 'moscow' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; - rewrite ------------------------------------ - ( 'moskva' | 'moscow' ) & 'hotel' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; - rewrite ---------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' ) -(1 row) - -create index qq on test_tsquery using gist (keyword gist_tp_tsquery_ops); -set enable_seqscan='off'; -select keyword from test_tsquery where keyword @> 'new'; - keyword ----------------- - 'new' & 'york' -(1 row) - -select keyword from test_tsquery where keyword @> 'moscow'; - keyword ----------- - 'moscow' -(1 row) - -select keyword from test_tsquery where keyword <@ 'new'; - keyword ---------- -(0 rows) - -select keyword from test_tsquery where keyword <@ 'moscow'; - keyword ----------- - 'moscow' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; - rewrite ---------------------- - 'moskva' | 'moscow' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; - rewrite ------------------------------------ - ( 'moskva' | 'moscow' ) & 'hotel' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; - rewrite ---------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' ) -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; - rewrite ---------------------- - 'moskva' | 'moscow' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; - rewrite ------------------------------------ - ( 'moskva' | 'moscow' ) & 'hotel' -(1 row) - -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; - rewrite ---------------------------------------------------------------------------------- - 'citi' & 'foo' & ( 'bar' | 'qq' ) & ( 'nyc' | 'big' & 'appl' | 'new' & 'york' ) -(1 row) - -set enable_seqscan='on'; -select lexize('simple', 'ASD56 hsdkf'); - lexize ------------------ - {"asd56 hsdkf"} -(1 row) - -select lexize('english_stem', 'SKIES Problems identity'); - lexize --------------------------- - {"skies problems ident"} -(1 row) - -select * from token_type('default'); - tokid | alias | descr --------+-----------------+------------------------------------------ - 1 | asciiword | Word, all ASCII - 2 | word | Word, all letters - 3 | numword | Word, letters and digits - 4 | email | Email address - 5 | url | URL - 6 | host | Host - 7 | sfloat | Scientific notation - 8 | version | Version number - 9 | hword_numpart | Hyphenated word part, letters and digits - 10 | hword_part | Hyphenated word part, all letters - 11 | hword_asciipart | Hyphenated word part, all ASCII - 12 | blank | Space symbols - 13 | tag | XML tag - 14 | protocol | Protocol head - 15 | numhword | Hyphenated word, letters and digits - 16 | asciihword | Hyphenated word, all ASCII - 17 | hword | Hyphenated word, all letters - 18 | url_path | URL path - 19 | file | File or path name - 20 | float | Decimal notation - 21 | int | Signed integer - 22 | uint | Unsigned integer - 23 | entity | XML entity -(23 rows) - -select * from parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf <fr>qwer jf sdjk<we hjwer <werrwe> ewr1> ewri2 <a href="qwe<qwe>"> -/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 -<i <b> wow < jqw <> qwerty'); - tokid | token --------+-------------------------------------- - 22 | 345 - 12 | - 1 | qwe - 12 | @ - 19 | efd.r - 12 | ' - 14 | http:// - 6 | www.com - 12 | / - 14 | http:// - 5 | aew.werc.ewr/?ad=qwe&dw - 6 | aew.werc.ewr - 18 | /?ad=qwe&dw - 12 | - 5 | 1aew.werc.ewr/?ad=qwe&dw - 6 | 1aew.werc.ewr - 18 | /?ad=qwe&dw - 12 | - 6 | 2aew.werc.ewr - 12 | - 14 | http:// - 5 | 3aew.werc.ewr/?ad=qwe&dw - 6 | 3aew.werc.ewr - 18 | /?ad=qwe&dw - 12 | - 14 | http:// - 6 | 4aew.werc.ewr - 12 | - 14 | http:// - 5 | 5aew.werc.ewr:8100/? - 6 | 5aew.werc.ewr:8100 - 18 | /? - 12 | - 1 | ad - 12 | = - 1 | qwe - 12 | & - 1 | dw - 12 | - 5 | 6aew.werc.ewr:8100/?ad=qwe&dw - 6 | 6aew.werc.ewr:8100 - 18 | /?ad=qwe&dw - 12 | - 5 | 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 - 6 | 7aew.werc.ewr:8100 - 18 | /?ad=qwe&dw=%20%32 - 12 | - 7 | +4.0e-10 - 12 | - 1 | qwe - 12 | - 1 | qwe - 12 | - 1 | qwqwe - 12 | - 20 | 234.435 - 12 | - 22 | 455 - 12 | - 20 | 5.005 - 12 | - 4 | teodor@stack.net - 12 | - 16 | qwe-wer - 11 | qwe - 12 | - - 11 | wer - 12 | - 1 | asdf - 12 | - 13 | <fr> - 1 | qwer - 12 | - 1 | jf - 12 | - 1 | sdjk - 12 | < - 1 | we - 12 | - 1 | hjwer - 12 | - 13 | <werrwe> - 12 | - 3 | ewr1 - 12 | > - 3 | ewri2 - 12 | - 13 | <a href="qwe<qwe>"> - 12 | + - | - 19 | /usr/local/fff - 12 | - 19 | /awdf/dwqe/4325 - 12 | - 19 | rewt/ewr - 12 | - 1 | wefjn - 12 | - 19 | /wqe-324/ewr - 12 | - 19 | gist.h - 12 | - 19 | gist.h.c - 12 | - 19 | gist.c - 12 | . - 1 | readline - 12 | - 20 | 4.2 - 12 | - 20 | 4.2 - 12 | . - 20 | 4.2 - 12 | , - 1 | readline - 20 | -4.2 - 12 | - 1 | readline - 20 | -4.2 - 12 | . - 22 | 234 - 12 | + - | - 12 | < - 1 | i - 12 | - 13 | <b> - 12 | - 1 | wow - 12 | - 12 | < - 1 | jqw - 12 | - 12 | <> - 1 | qwerty -(133 rows) - -SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf <fr>qwer jf sdjk<we hjwer <werrwe> ewr1> ewri2 <a href="qwe<qwe>"> -/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 -<i <b> wow < jqw <> qwerty'); - to_tsvector ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - '+4.0e-10':28 '-4.2':60,62 '/?':18 '/?ad=qwe&dw':7,10,14,24 '/?ad=qwe&dw=%20%32':27 '/awdf/dwqe/4325':48 '/usr/local/fff':47 '/wqe-324/ewr':51 '1aew.werc.ewr':9 '1aew.werc.ewr/?ad=qwe&dw':8 '234':63 '234.435':32 '2aew.werc.ewr':11 '345':1 '3aew.werc.ewr':13 '3aew.werc.ewr/?ad=qwe&dw':12 '4.2':56,57,58 '455':33 '4aew.werc.ewr':15 '5.005':34 '5aew.werc.ewr:8100':17 '5aew.werc.ewr:8100/?':16 '6aew.werc.ewr:8100':23 '6aew.werc.ewr:8100/?ad=qwe&dw':22 '7aew.werc.ewr:8100':26 '7aew.werc.ewr:8100/?ad=qwe&dw=%20%32':25 'ad':19 'aew.werc.ewr':6 'aew.werc.ewr/?ad=qwe&dw':5 'asdf':39 'dw':21 'efd.r':3 'ewr1':45 'ewri2':46 'gist.c':54 'gist.h':52 'gist.h.c':53 'hjwer':44 'jf':41 'jqw':66 'qwe':2,20,29,30,37 'qwe-wer':36 'qwer':40 'qwerti':67 'qwqwe':31 'readlin':55,59,61 'rewt/ewr':49 'sdjk':42 'teodor@stack.net':35 'wefjn':50 'wer':38 'wow':65 'www.com':4 -(1 row) - -SELECT length(to_tsvector('english', '345 qw')); - length --------- - 2 -(1 row) - -SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf <fr>qwer jf sdjk<we hjwer <werrwe> ewr1> ewri2 <a href="qwe<qwe>"> -/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 -<i <b> wow < jqw <> qwerty')); - length --------- - 53 -(1 row) - -select to_tsquery('english', 'qwe & sKies '); - to_tsquery ---------------- - 'qwe' & 'sky' -(1 row) - -select to_tsquery('simple', 'qwe & sKies '); - to_tsquery ------------------ - 'qwe' & 'skies' -(1 row) - -select to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); - to_tsquery ------------------------- - 'wether':CD & 'sky':BC -(1 row) - -select to_tsquery('english', 'asd&(and|fghj)'); - to_tsquery ----------------- - 'asd' & 'fghj' -(1 row) - -select to_tsquery('english', '(asd&and)|fghj'); - to_tsquery ----------------- - 'asd' | 'fghj' -(1 row) - -select to_tsquery('english', '(asd&!and)|fghj'); - to_tsquery ----------------- - 'asd' | 'fghj' -(1 row) - -select to_tsquery('english', '(the|and&(i&1))&fghj'); - to_tsquery --------------- - '1' & 'fghj' -(1 row) - -select plainto_tsquery('english', 'the and z 1))& fghj'); - plainto_tsquery --------------------- - 'z' & '1' & 'fghj' -(1 row) - -select plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); - ?column? ------------------------ - 'foo' & 'bar' & 'asd' -(1 row) - -select plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); - ?column? ------------------------------- - 'foo' & 'bar' | 'asd' & 'fg' -(1 row) - -select plainto_tsquery('english', 'foo bar') || !!plainto_tsquery('english', 'asd fg'); - ?column? ------------------------------------ - 'foo' & 'bar' | !( 'asd' & 'fg' ) -(1 row) - -select plainto_tsquery('english', 'foo bar') && 'asd | fg'; - ?column? ----------------------------------- - 'foo' & 'bar' & ( 'asd' | 'fg' ) -(1 row) - -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca'; - ?column? ----------- - t -(1 row) - -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B'; - ?column? ----------- - t -(1 row) - -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:A'; - ?column? ----------- - t -(1 row) - -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:C'; - ?column? ----------- - f -(1 row) - -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:CB'; - ?column? ----------- - t -(1 row) - -CREATE TABLE test_tsvector( t text, a tsvector ); -\copy test_tsvector from 'data/test_tsearch.data' -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; - count -------- - 158 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; - count -------- - 17 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; - count -------- - 6 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; - count -------- - 98 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; - count -------- - 23 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; - count -------- - 39 -(1 row) - -create index wowidx on test_tsvector using gist (a); -set enable_seqscan=off; -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; - count -------- - 158 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; - count -------- - 17 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; - count -------- - 6 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; - count -------- - 98 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; - count -------- - 23 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; - count -------- - 39 -(1 row) - -select set_curcfg('english'); - set_curcfg ------------- - -(1 row) - -CREATE TRIGGER tsvectorupdate -BEFORE UPDATE OR INSERT ON test_tsvector -FOR EACH ROW EXECUTE PROCEDURE tsearch2(a, t); -SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - count -------- - 0 -(1 row) - -INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); -SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - count -------- - 1 -(1 row) - -UPDATE test_tsvector SET t = null WHERE t = '345 qwerty'; -SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - count -------- - 0 -(1 row) - -insert into test_tsvector (t) values ('345 qwerty copyright'); -select count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - count -------- - 1 -(1 row) - -select count(*) FROM test_tsvector WHERE a @@ to_tsquery('copyright'); - count -------- - 1 -(1 row) - -select rank(' a:1 s:2C d g'::tsvector, 'a | s'); - rank ------------ - 0.0911891 -(1 row) - -select rank(' a:1 s:2B d g'::tsvector, 'a | s'); - rank ----------- - 0.151982 -(1 row) - -select rank(' a:1 s:2 d g'::tsvector, 'a | s'); - rank ------------ - 0.0607927 -(1 row) - -select rank(' a:1 s:2C d g'::tsvector, 'a & s'); - rank ----------- - 0.140153 -(1 row) - -select rank(' a:1 s:2B d g'::tsvector, 'a & s'); - rank ----------- - 0.198206 -(1 row) - -select rank(' a:1 s:2 d g'::tsvector, 'a & s'); - rank ------------ - 0.0991032 -(1 row) - -insert into test_tsvector (t) values ('foo bar foo the over foo qq bar'); -drop trigger tsvectorupdate on test_tsvector; -select * from stat('select a from test_tsvector') order by ndoc desc, nentry desc, word collate "C"; - word | ndoc | nentry ------------+------+-------- - qq | 109 | 109 - qt | 102 | 102 - qe | 100 | 100 - qh | 98 | 98 - qw | 98 | 98 - qa | 97 | 97 - ql | 94 | 94 - qs | 94 | 94 - qi | 92 | 92 - qr | 92 | 92 - qj | 91 | 91 - qd | 87 | 87 - qz | 87 | 87 - qc | 86 | 86 - qn | 86 | 86 - qv | 85 | 85 - qo | 84 | 84 - qy | 84 | 84 - wp | 84 | 84 - qf | 81 | 81 - qk | 80 | 80 - wt | 80 | 80 - qu | 79 | 79 - qg | 78 | 78 - wb | 78 | 78 - qx | 77 | 77 - wr | 77 | 77 - ws | 73 | 73 - wy | 73 | 73 - wa | 72 | 72 - wf | 70 | 70 - wg | 70 | 70 - wi | 70 | 70 - wu | 70 | 70 - wc | 69 | 69 - wj | 69 | 69 - qp | 68 | 68 - wh | 68 | 68 - wv | 68 | 68 - qb | 66 | 66 - eu | 65 | 65 - we | 65 | 65 - wl | 65 | 65 - wq | 65 | 65 - wk | 64 | 64 - ee | 63 | 63 - eo | 63 | 63 - qm | 63 | 63 - wn | 63 | 63 - ef | 62 | 62 - eh | 62 | 62 - ex | 62 | 62 - re | 62 | 62 - rl | 62 | 62 - rr | 62 | 62 - eb | 61 | 61 - ek | 61 | 61 - ww | 61 | 61 - ea | 60 | 60 - ei | 60 | 60 - em | 60 | 60 - eq | 60 | 60 - ew | 60 | 60 - ro | 60 | 60 - rw | 60 | 60 - tl | 60 | 60 - eg | 59 | 59 - en | 59 | 59 - ez | 59 | 59 - rj | 59 | 59 - ry | 59 | 59 - tw | 59 | 59 - tx | 59 | 59 - ej | 58 | 58 - es | 58 | 58 - ra | 58 | 58 - rd | 58 | 58 - rg | 58 | 58 - rx | 58 | 58 - tb | 58 | 58 - wd | 58 | 58 - ed | 57 | 57 - tc | 57 | 57 - wx | 57 | 57 - er | 56 | 56 - wm | 56 | 56 - wo | 56 | 56 - yw | 56 | 56 - ep | 55 | 55 - rk | 55 | 55 - rp | 55 | 55 - rz | 55 | 55 - ta | 55 | 55 - rq | 54 | 54 - yn | 54 | 54 - ec | 53 | 53 - el | 53 | 53 - ru | 53 | 53 - rv | 53 | 53 - tz | 53 | 53 - un | 53 | 53 - wz | 53 | 53 - ys | 53 | 53 - oe | 52 | 52 - tn | 52 | 52 - tq | 52 | 52 - ty | 52 | 52 - uq | 52 | 52 - yg | 52 | 52 - ym | 52 | 52 - oi | 51 | 51 - to | 51 | 51 - yi | 51 | 51 - pn | 50 | 50 - rb | 50 | 50 - ri | 50 | 50 - rn | 50 | 50 - ti | 50 | 50 - tv | 50 | 50 - um | 50 | 50 - ut | 50 | 50 - ya | 50 | 50 - et | 49 | 49 - ix | 49 | 49 - ox | 49 | 49 - q3 | 49 | 49 - yf | 49 | 49 - yl | 49 | 49 - yo | 49 | 49 - yr | 49 | 49 - ev | 48 | 48 - ey | 48 | 48 - ot | 48 | 48 - rc | 48 | 48 - rm | 48 | 48 - th | 48 | 48 - uo | 48 | 48 - ia | 47 | 47 - q1 | 47 | 47 - rh | 47 | 47 - yq | 47 | 47 - yz | 47 | 47 - av | 46 | 46 - im | 46 | 46 - os | 46 | 46 - tk | 46 | 46 - yy | 46 | 46 - ir | 45 | 45 - iv | 45 | 45 - iw | 45 | 45 - oj | 45 | 45 - pl | 45 | 45 - pv | 45 | 45 - te | 45 | 45 - tu | 45 | 45 - uv | 45 | 45 - ux | 45 | 45 - yd | 45 | 45 - yx | 45 | 45 - ij | 44 | 44 - pa | 44 | 44 - se | 44 | 44 - tg | 44 | 44 - ue | 44 | 44 - yb | 44 | 44 - yt | 44 | 44 - if | 43 | 43 - ik | 43 | 43 - in | 43 | 43 - ph | 43 | 43 - pj | 43 | 43 - q5 | 43 | 43 - rt | 43 | 43 - ub | 43 | 43 - ud | 43 | 43 - uh | 43 | 43 - uj | 43 | 43 - w7 | 43 | 43 - ye | 43 | 43 - yv | 43 | 43 - db | 42 | 42 - do | 42 | 42 - id | 42 | 42 - ie | 42 | 42 - ii | 42 | 42 - of | 42 | 42 - pr | 42 | 42 - q4 | 42 | 42 - rf | 42 | 42 - td | 42 | 42 - uk | 42 | 42 - up | 42 | 42 - yh | 42 | 42 - yk | 42 | 42 - io | 41 | 41 - it | 41 | 41 - pb | 41 | 41 - q0 | 41 | 41 - q7 | 41 | 41 - rs | 41 | 41 - tj | 41 | 41 - ur | 41 | 41 - ig | 40 | 40 - iu | 40 | 40 - iy | 40 | 40 - od | 40 | 40 - q6 | 40 | 40 - tt | 40 | 40 - ug | 40 | 40 - ul | 40 | 40 - us | 40 | 40 - uu | 40 | 40 - uz | 40 | 40 - ah | 39 | 39 - ar | 39 | 39 - as | 39 | 39 - dl | 39 | 39 - dt | 39 | 39 - hk | 39 | 39 - iq | 39 | 39 - is | 39 | 39 - oc | 39 | 39 - ov | 39 | 39 - oy | 39 | 39 - uf | 39 | 39 - ui | 39 | 39 - aa | 38 | 38 - ad | 38 | 38 - fh | 38 | 38 - gm | 38 | 38 - ic | 38 | 38 - jd | 38 | 38 - om | 38 | 38 - or | 38 | 38 - oz | 38 | 38 - pm | 38 | 38 - q8 | 38 | 38 - sf | 38 | 38 - sm | 38 | 38 - sv | 38 | 38 - uc | 38 | 38 - ak | 37 | 37 - aq | 37 | 37 - di | 37 | 37 - e4 | 37 | 37 - fi | 37 | 37 - fx | 37 | 37 - ha | 37 | 37 - hp | 37 | 37 - ih | 37 | 37 - og | 37 | 37 - po | 37 | 37 - pw | 37 | 37 - sn | 37 | 37 - su | 37 | 37 - sw | 37 | 37 - w6 | 37 | 37 - yj | 37 | 37 - yu | 37 | 37 - ag | 36 | 36 - am | 36 | 36 - at | 36 | 36 - e1 | 36 | 36 - ff | 36 | 36 - gx | 36 | 36 - he | 36 | 36 - hj | 36 | 36 - ib | 36 | 36 - iz | 36 | 36 - lm | 36 | 36 - ok | 36 | 36 - pk | 36 | 36 - pp | 36 | 36 - pu | 36 | 36 - sp | 36 | 36 - tf | 36 | 36 - tm | 36 | 36 - ay | 35 | 35 - dy | 35 | 35 - fu | 35 | 35 - ku | 35 | 35 - lh | 35 | 35 - lq | 35 | 35 - o6 | 35 | 35 - ob | 35 | 35 - on | 35 | 35 - op | 35 | 35 - pd | 35 | 35 - ps | 35 | 35 - si | 35 | 35 - sl | 35 | 35 - sx | 35 | 35 - tp | 35 | 35 - tr | 35 | 35 - w3 | 35 | 35 - y1 | 35 | 35 - al | 34 | 34 - ap | 34 | 34 - az | 34 | 34 - dc | 34 | 34 - dd | 34 | 34 - dz | 34 | 34 - e0 | 34 | 34 - fj | 34 | 34 - fp | 34 | 34 - gd | 34 | 34 - gg | 34 | 34 - gk | 34 | 34 - go | 34 | 34 - ho | 34 | 34 - jc | 34 | 34 - oa | 34 | 34 - oh | 34 | 34 - oo | 34 | 34 - pe | 34 | 34 - px | 34 | 34 - sd | 34 | 34 - sq | 34 | 34 - sy | 34 | 34 - ab | 33 | 33 - ae | 33 | 33 - af | 33 | 33 - aw | 33 | 33 - e5 | 33 | 33 - fk | 33 | 33 - gu | 33 | 33 - gy | 33 | 33 - hb | 33 | 33 - hm | 33 | 33 - hy | 33 | 33 - jl | 33 | 33 - jr | 33 | 33 - ls | 33 | 33 - oq | 33 | 33 - pt | 33 | 33 - sa | 33 | 33 - sh | 33 | 33 - sj | 33 | 33 - so | 33 | 33 - sz | 33 | 33 - t7 | 33 | 33 - uw | 33 | 33 - w8 | 33 | 33 - y0 | 33 | 33 - yp | 33 | 33 - dh | 32 | 32 - dp | 32 | 32 - dq | 32 | 32 - e7 | 32 | 32 - fn | 32 | 32 - fo | 32 | 32 - fr | 32 | 32 - ga | 32 | 32 - gq | 32 | 32 - hh | 32 | 32 - il | 32 | 32 - ip | 32 | 32 - jv | 32 | 32 - lc | 32 | 32 - ol | 32 | 32 - pc | 32 | 32 - q9 | 32 | 32 - ds | 31 | 31 - e9 | 31 | 31 - fd | 31 | 31 - fe | 31 | 31 - ft | 31 | 31 - gs | 31 | 31 - hl | 31 | 31 - hs | 31 | 31 - jb | 31 | 31 - kc | 31 | 31 - kw | 31 | 31 - mj | 31 | 31 - q2 | 31 | 31 - r3 | 31 | 31 - sb | 31 | 31 - sk | 31 | 31 - ts | 31 | 31 - ua | 31 | 31 - yc | 31 | 31 - zw | 31 | 31 - ao | 30 | 30 - du | 30 | 30 - fw | 30 | 30 - gj | 30 | 30 - hu | 30 | 30 - kh | 30 | 30 - kl | 30 | 30 - kv | 30 | 30 - ld | 30 | 30 - lf | 30 | 30 - pq | 30 | 30 - py | 30 | 30 - sc | 30 | 30 - sr | 30 | 30 - uy | 30 | 30 - vg | 30 | 30 - w2 | 30 | 30 - xg | 30 | 30 - xo | 30 | 30 - au | 29 | 29 - cx | 29 | 29 - fv | 29 | 29 - gh | 29 | 29 - gl | 29 | 29 - gt | 29 | 29 - hw | 29 | 29 - ji | 29 | 29 - km | 29 | 29 - la | 29 | 29 - ou | 29 | 29 - r0 | 29 | 29 - w0 | 29 | 29 - y9 | 29 | 29 - zm | 29 | 29 - zs | 29 | 29 - zy | 29 | 29 - ax | 28 | 28 - cd | 28 | 28 - dj | 28 | 28 - dn | 28 | 28 - dr | 28 | 28 - ht | 28 | 28 - jf | 28 | 28 - lo | 28 | 28 - lr | 28 | 28 - na | 28 | 28 - ng | 28 | 28 - r8 | 28 | 28 - ss | 28 | 28 - xt | 28 | 28 - y6 | 28 | 28 - aj | 27 | 27 - ca | 27 | 27 - cg | 27 | 27 - df | 27 | 27 - dg | 27 | 27 - dv | 27 | 27 - gc | 27 | 27 - gn | 27 | 27 - gr | 27 | 27 - hd | 27 | 27 - i8 | 27 | 27 - jn | 27 | 27 - jt | 27 | 27 - lp | 27 | 27 - o9 | 27 | 27 - ow | 27 | 27 - r9 | 27 | 27 - t8 | 27 | 27 - u5 | 27 | 27 - w4 | 27 | 27 - xm | 27 | 27 - zz | 27 | 27 - a2 | 26 | 26 - ac | 26 | 26 - ai | 26 | 26 - cm | 26 | 26 - cu | 26 | 26 - cw | 26 | 26 - dk | 26 | 26 - e2 | 26 | 26 - fc | 26 | 26 - fg | 26 | 26 - fl | 26 | 26 - fs | 26 | 26 - ge | 26 | 26 - gv | 26 | 26 - hc | 26 | 26 - hi | 26 | 26 - hx | 26 | 26 - jj | 26 | 26 - jm | 26 | 26 - kg | 26 | 26 - kk | 26 | 26 - kn | 26 | 26 - ko | 26 | 26 - kt | 26 | 26 - ln | 26 | 26 - mx | 26 | 26 - pg | 26 | 26 - r4 | 26 | 26 - t6 | 26 | 26 - u1 | 26 | 26 - u4 | 26 | 26 - vi | 26 | 26 - vr | 26 | 26 - w1 | 26 | 26 - w9 | 26 | 26 - xk | 26 | 26 - xs | 26 | 26 - zf | 26 | 26 - bb | 25 | 25 - dm | 25 | 25 - dw | 25 | 25 - e8 | 25 | 25 - fb | 25 | 25 - gw | 25 | 25 - h8 | 25 | 25 - hf | 25 | 25 - hg | 25 | 25 - hn | 25 | 25 - hv | 25 | 25 - i0 | 25 | 25 - i3 | 25 | 25 - jg | 25 | 25 - jo | 25 | 25 - jx | 25 | 25 - kq | 25 | 25 - lw | 25 | 25 - lx | 25 | 25 - o3 | 25 | 25 - p7 | 25 | 25 - pf | 25 | 25 - pi | 25 | 25 - pz | 25 | 25 - r2 | 25 | 25 - r5 | 25 | 25 - t9 | 25 | 25 - u7 | 25 | 25 - ve | 25 | 25 - vu | 25 | 25 - y5 | 25 | 25 - y8 | 25 | 25 - zt | 25 | 25 - an | 24 | 24 - bj | 24 | 24 - dx | 24 | 24 - fm | 24 | 24 - fz | 24 | 24 - gb | 24 | 24 - gi | 24 | 24 - gp | 24 | 24 - hr | 24 | 24 - hz | 24 | 24 - i5 | 24 | 24 - jq | 24 | 24 - kb | 24 | 24 - ke | 24 | 24 - kf | 24 | 24 - kp | 24 | 24 - lv | 24 | 24 - lz | 24 | 24 - o8 | 24 | 24 - r1 | 24 | 24 - s7 | 24 | 24 - sg | 24 | 24 - u3 | 24 | 24 - vj | 24 | 24 - vt | 24 | 24 - w5 | 24 | 24 - zj | 24 | 24 - be | 23 | 23 - bi | 23 | 23 - bn | 23 | 23 - cn | 23 | 23 - cy | 23 | 23 - da | 23 | 23 - e6 | 23 | 23 - fa | 23 | 23 - js | 23 | 23 - ki | 23 | 23 - kz | 23 | 23 - li | 23 | 23 - mt | 23 | 23 - mz | 23 | 23 - nu | 23 | 23 - o2 | 23 | 23 - p5 | 23 | 23 - p8 | 23 | 23 - r7 | 23 | 23 - t0 | 23 | 23 - t1 | 23 | 23 - t3 | 23 | 23 - vm | 23 | 23 - xh | 23 | 23 - xx | 23 | 23 - zp | 23 | 23 - zr | 23 | 23 - a3 | 22 | 22 - bg | 22 | 22 - de | 22 | 22 - e3 | 22 | 22 - fq | 22 | 22 - i2 | 22 | 22 - i7 | 22 | 22 - ja | 22 | 22 - jk | 22 | 22 - jy | 22 | 22 - kr | 22 | 22 - kx | 22 | 22 - ly | 22 | 22 - nb | 22 | 22 - nh | 22 | 22 - ns | 22 | 22 - s3 | 22 | 22 - u2 | 22 | 22 - vn | 22 | 22 - xe | 22 | 22 - y4 | 22 | 22 - zh | 22 | 22 - zo | 22 | 22 - zq | 22 | 22 - a1 | 21 | 21 - bl | 21 | 21 - bo | 21 | 21 - cb | 21 | 21 - ch | 21 | 21 - co | 21 | 21 - cq | 21 | 21 - cv | 21 | 21 - d7 | 21 | 21 - g8 | 21 | 21 - je | 21 | 21 - jp | 21 | 21 - jz | 21 | 21 - lg | 21 | 21 - me | 21 | 21 - nc | 21 | 21 - p4 | 21 | 21 - st | 21 | 21 - vb | 21 | 21 - vw | 21 | 21 - vz | 21 | 21 - xj | 21 | 21 - xq | 21 | 21 - xu | 21 | 21 - xy | 21 | 21 - zb | 21 | 21 - bv | 20 | 20 - bz | 20 | 20 - cj | 20 | 20 - cp | 20 | 20 - cs | 20 | 20 - d8 | 20 | 20 - ju | 20 | 20 - k0 | 20 | 20 - ks | 20 | 20 - ky | 20 | 20 - l1 | 20 | 20 - lb | 20 | 20 - lj | 20 | 20 - lu | 20 | 20 - nm | 20 | 20 - nw | 20 | 20 - nz | 20 | 20 - o7 | 20 | 20 - p6 | 20 | 20 - vh | 20 | 20 - vp | 20 | 20 - vs | 20 | 20 - xb | 20 | 20 - xr | 20 | 20 - z3 | 20 | 20 - zv | 20 | 20 - bq | 19 | 19 - br | 19 | 19 - by | 19 | 19 - cl | 19 | 19 - d2 | 19 | 19 - f1 | 19 | 19 - f4 | 19 | 19 - gf | 19 | 19 - hq | 19 | 19 - k9 | 19 | 19 - ka | 19 | 19 - kd | 19 | 19 - kj | 19 | 19 - md | 19 | 19 - mi | 19 | 19 - ml | 19 | 19 - my | 19 | 19 - nj | 19 | 19 - ny | 19 | 19 - o1 | 19 | 19 - s4 | 19 | 19 - s8 | 19 | 19 - t5 | 19 | 19 - u0 | 19 | 19 - xl | 19 | 19 - zg | 19 | 19 - zi | 19 | 19 - a5 | 18 | 18 - b9 | 18 | 18 - bh | 18 | 18 - bx | 18 | 18 - d3 | 18 | 18 - fy | 18 | 18 - g2 | 18 | 18 - i4 | 18 | 18 - i6 | 18 | 18 - i9 | 18 | 18 - jw | 18 | 18 - lk | 18 | 18 - mb | 18 | 18 - mv | 18 | 18 - nd | 18 | 18 - nr | 18 | 18 - nt | 18 | 18 - t2 | 18 | 18 - xf | 18 | 18 - xv | 18 | 18 - zc | 18 | 18 - zd | 18 | 18 - a7 | 17 | 17 - bc | 17 | 17 - bd | 17 | 17 - ce | 17 | 17 - cf | 17 | 17 - cr | 17 | 17 - g9 | 17 | 17 - j0 | 17 | 17 - j5 | 17 | 17 - mp | 17 | 17 - mr | 17 | 17 - mw | 17 | 17 - nk | 17 | 17 - no | 17 | 17 - o0 | 17 | 17 - o4 | 17 | 17 - s0 | 17 | 17 - s1 | 17 | 17 - t4 | 17 | 17 - u9 | 17 | 17 - vf | 17 | 17 - vx | 17 | 17 - x3 | 17 | 17 - xi | 17 | 17 - xn | 17 | 17 - xz | 17 | 17 - zl | 17 | 17 - zn | 17 | 17 - a0 | 16 | 16 - bu | 16 | 16 - bw | 16 | 16 - ci | 16 | 16 - ck | 16 | 16 - d0 | 16 | 16 - d4 | 16 | 16 - d6 | 16 | 16 - f5 | 16 | 16 - g1 | 16 | 16 - gz | 16 | 16 - h4 | 16 | 16 - jh | 16 | 16 - l4 | 16 | 16 - lt | 16 | 16 - mg | 16 | 16 - mh | 16 | 16 - mo | 16 | 16 - ni | 16 | 16 - nl | 16 | 16 - nq | 16 | 16 - p2 | 16 | 16 - u8 | 16 | 16 - v9 | 16 | 16 - vl | 16 | 16 - vo | 16 | 16 - xp | 16 | 16 - y3 | 16 | 16 - y7 | 16 | 16 - z7 | 16 | 16 - za | 16 | 16 - zx | 16 | 16 - bf | 15 | 15 - bp | 15 | 15 - cc | 15 | 15 - g0 | 15 | 15 - j2 | 15 | 15 - j9 | 15 | 15 - l6 | 15 | 15 - le | 15 | 15 - ll | 15 | 15 - m8 | 15 | 15 - ma | 15 | 15 - mu | 15 | 15 - nf | 15 | 15 - r6 | 15 | 15 - s5 | 15 | 15 - vd | 15 | 15 - vk | 15 | 15 - xa | 15 | 15 - xw | 15 | 15 - y2 | 15 | 15 - z8 | 15 | 15 - ze | 15 | 15 - zu | 15 | 15 - a6 | 14 | 14 - bk | 14 | 14 - bt | 14 | 14 - c0 | 14 | 14 - f8 | 14 | 14 - g3 | 14 | 14 - g4 | 14 | 14 - g7 | 14 | 14 - h6 | 14 | 14 - h7 | 14 | 14 - h9 | 14 | 14 - i1 | 14 | 14 - k1 | 14 | 14 - k2 | 14 | 14 - k6 | 14 | 14 - k7 | 14 | 14 - mc | 14 | 14 - nn | 14 | 14 - p9 | 14 | 14 - u6 | 14 | 14 - xd | 14 | 14 - z6 | 14 | 14 - zk | 14 | 14 - a4 | 13 | 13 - a9 | 13 | 13 - bm | 13 | 13 - cz | 13 | 13 - f2 | 13 | 13 - f3 | 13 | 13 - f6 | 13 | 13 - g6 | 13 | 13 - h2 | 13 | 13 - j1 | 13 | 13 - k5 | 13 | 13 - m1 | 13 | 13 - mf | 13 | 13 - mq | 13 | 13 - np | 13 | 13 - nx | 13 | 13 - o5 | 13 | 13 - p0 | 13 | 13 - p1 | 13 | 13 - s6 | 13 | 13 - s9 | 13 | 13 - v6 | 13 | 13 - va | 13 | 13 - vc | 13 | 13 - xc | 13 | 13 - z0 | 13 | 13 - c9 | 12 | 12 - d1 | 12 | 12 - h0 | 12 | 12 - h1 | 12 | 12 - j8 | 12 | 12 - k4 | 12 | 12 - l5 | 12 | 12 - l9 | 12 | 12 - m2 | 12 | 12 - m6 | 12 | 12 - m9 | 12 | 12 - n7 | 12 | 12 - nv | 12 | 12 - p3 | 12 | 12 - vq | 12 | 12 - vy | 12 | 12 - x1 | 12 | 12 - x2 | 12 | 12 - z5 | 12 | 12 - c1 | 11 | 11 - c3 | 11 | 11 - ct | 11 | 11 - f9 | 11 | 11 - g5 | 11 | 11 - j6 | 11 | 11 - l8 | 11 | 11 - n1 | 11 | 11 - v7 | 11 | 11 - vv | 11 | 11 - x5 | 11 | 11 - x8 | 11 | 11 - z2 | 11 | 11 - b0 | 10 | 10 - b2 | 10 | 10 - b8 | 10 | 10 - c6 | 10 | 10 - f0 | 10 | 10 - f7 | 10 | 10 - h5 | 10 | 10 - j3 | 10 | 10 - j4 | 10 | 10 - j7 | 10 | 10 - l7 | 10 | 10 - m0 | 10 | 10 - m7 | 10 | 10 - mm | 10 | 10 - mn | 10 | 10 - n8 | 10 | 10 - v1 | 10 | 10 - x0 | 10 | 10 - x6 | 10 | 10 - x7 | 10 | 10 - x9 | 10 | 10 - a8 | 9 | 9 - b1 | 9 | 9 - b4 | 9 | 9 - b5 | 9 | 9 - b6 | 9 | 9 - ba | 9 | 9 - bs | 9 | 9 - c5 | 9 | 9 - d5 | 9 | 9 - k8 | 9 | 9 - l0 | 9 | 9 - m5 | 9 | 9 - mk | 9 | 9 - ms | 9 | 9 - n3 | 9 | 9 - n4 | 9 | 9 - n6 | 9 | 9 - ne | 9 | 9 - v0 | 9 | 9 - v3 | 9 | 9 - v5 | 9 | 9 - v8 | 9 | 9 - b3 | 8 | 8 - b7 | 8 | 8 - c2 | 8 | 8 - c7 | 8 | 8 - c8 | 8 | 8 - d9 | 8 | 8 - k3 | 8 | 8 - l3 | 8 | 8 - m3 | 8 | 8 - m4 | 8 | 8 - n0 | 8 | 8 - n5 | 8 | 8 - v4 | 8 | 8 - x4 | 8 | 8 - z1 | 8 | 8 - z9 | 8 | 8 - l2 | 7 | 7 - s2 | 7 | 7 - z4 | 7 | 7 - 1l | 6 | 6 - 1o | 6 | 6 - 1t | 6 | 6 - 2e | 6 | 6 - 2o | 6 | 6 - c4 | 6 | 6 - h3 | 6 | 6 - n2 | 6 | 6 - n9 | 6 | 6 - v2 | 6 | 6 - 2l | 5 | 5 - 2u | 5 | 5 - 3k | 5 | 5 - 4p | 5 | 5 - 18 | 4 | 4 - 1a | 4 | 4 - 1i | 4 | 4 - 2s | 4 | 4 - 3q | 4 | 4 - 3y | 4 | 4 - 5y | 4 | 4 - 1f | 3 | 3 - 1h | 3 | 3 - 1m | 3 | 3 - 1p | 3 | 3 - 1s | 3 | 3 - 1v | 3 | 3 - 1x | 3 | 3 - 27 | 3 | 3 - 2a | 3 | 3 - 2b | 3 | 3 - 2h | 3 | 3 - 2n | 3 | 3 - 2p | 3 | 3 - 2v | 3 | 3 - 2y | 3 | 3 - 3d | 3 | 3 - 3w | 3 | 3 - 3z | 3 | 3 - 4a | 3 | 3 - 4d | 3 | 3 - 4v | 3 | 3 - 4z | 3 | 3 - 5e | 3 | 3 - 5i | 3 | 3 - 5k | 3 | 3 - 5o | 3 | 3 - 5t | 3 | 3 - 6b | 3 | 3 - 6d | 3 | 3 - 6o | 3 | 3 - 6w | 3 | 3 - 7a | 3 | 3 - 7h | 3 | 3 - 7r | 3 | 3 - 93 | 3 | 3 - 10 | 2 | 2 - 12 | 2 | 2 - 15 | 2 | 2 - 16 | 2 | 2 - 19 | 2 | 2 - 1b | 2 | 2 - 1d | 2 | 2 - 1g | 2 | 2 - 1j | 2 | 2 - 1n | 2 | 2 - 1r | 2 | 2 - 1u | 2 | 2 - 1w | 2 | 2 - 1y | 2 | 2 - 20 | 2 | 2 - 25 | 2 | 2 - 2d | 2 | 2 - 2i | 2 | 2 - 2j | 2 | 2 - 2k | 2 | 2 - 2q | 2 | 2 - 2r | 2 | 2 - 2t | 2 | 2 - 2w | 2 | 2 - 2z | 2 | 2 - 3b | 2 | 2 - 3f | 2 | 2 - 3h | 2 | 2 - 3o | 2 | 2 - 3p | 2 | 2 - 3r | 2 | 2 - 3s | 2 | 2 - 3v | 2 | 2 - 42 | 2 | 2 - 43 | 2 | 2 - 4f | 2 | 2 - 4g | 2 | 2 - 4h | 2 | 2 - 4j | 2 | 2 - 4m | 2 | 2 - 4r | 2 | 2 - 4s | 2 | 2 - 4t | 2 | 2 - 4u | 2 | 2 - 5c | 2 | 2 - 5f | 2 | 2 - 5h | 2 | 2 - 5p | 2 | 2 - 5q | 2 | 2 - 5z | 2 | 2 - 6a | 2 | 2 - 6h | 2 | 2 - 6q | 2 | 2 - 6r | 2 | 2 - 6t | 2 | 2 - 6y | 2 | 2 - 70 | 2 | 2 - 7c | 2 | 2 - 7g | 2 | 2 - 7k | 2 | 2 - 7o | 2 | 2 - 7u | 2 | 2 - 8j | 2 | 2 - 8w | 2 | 2 - 9f | 2 | 2 - 9y | 2 | 2 - foo | 1 | 3 - bar | 1 | 2 - 0e | 1 | 1 - 0h | 1 | 1 - 0p | 1 | 1 - 0w | 1 | 1 - 0z | 1 | 1 - 11 | 1 | 1 - 13 | 1 | 1 - 14 | 1 | 1 - 17 | 1 | 1 - 1k | 1 | 1 - 1q | 1 | 1 - 1z | 1 | 1 - 24 | 1 | 1 - 26 | 1 | 1 - 28 | 1 | 1 - 2f | 1 | 1 - 30 | 1 | 1 - 345 | 1 | 1 - 37 | 1 | 1 - 39 | 1 | 1 - 3a | 1 | 1 - 3e | 1 | 1 - 3g | 1 | 1 - 3i | 1 | 1 - 3m | 1 | 1 - 3t | 1 | 1 - 3u | 1 | 1 - 40 | 1 | 1 - 41 | 1 | 1 - 44 | 1 | 1 - 45 | 1 | 1 - 48 | 1 | 1 - 4b | 1 | 1 - 4c | 1 | 1 - 4i | 1 | 1 - 4k | 1 | 1 - 4n | 1 | 1 - 4o | 1 | 1 - 4q | 1 | 1 - 4w | 1 | 1 - 4y | 1 | 1 - 51 | 1 | 1 - 55 | 1 | 1 - 56 | 1 | 1 - 5a | 1 | 1 - 5d | 1 | 1 - 5g | 1 | 1 - 5j | 1 | 1 - 5l | 1 | 1 - 5s | 1 | 1 - 5u | 1 | 1 - 5x | 1 | 1 - 64 | 1 | 1 - 68 | 1 | 1 - 6c | 1 | 1 - 6f | 1 | 1 - 6g | 1 | 1 - 6i | 1 | 1 - 6k | 1 | 1 - 6n | 1 | 1 - 6p | 1 | 1 - 6s | 1 | 1 - 6u | 1 | 1 - 6x | 1 | 1 - 72 | 1 | 1 - 7f | 1 | 1 - 7j | 1 | 1 - 7n | 1 | 1 - 7p | 1 | 1 - 7w | 1 | 1 - 7y | 1 | 1 - 7z | 1 | 1 - 80 | 1 | 1 - 82 | 1 | 1 - 85 | 1 | 1 - 8d | 1 | 1 - 8i | 1 | 1 - 8l | 1 | 1 - 8n | 1 | 1 - 8p | 1 | 1 - 8t | 1 | 1 - 8x | 1 | 1 - 95 | 1 | 1 - 97 | 1 | 1 - 9a | 1 | 1 - 9e | 1 | 1 - 9h | 1 | 1 - 9r | 1 | 1 - 9w | 1 | 1 - copyright | 1 | 1 - qwerti | 1 | 1 -(1146 rows) - -insert into test_tsvector values ('1', 'a:1a,2,3b b:5a,6a,7c,8'); -insert into test_tsvector values ('1', 'a:1a,2,3c b:5a,6b,7c,8b'); -select * from stat('select a from test_tsvector','a') order by ndoc desc, nentry desc, word collate "C"; - word | ndoc | nentry -------+------+-------- - b | 2 | 3 - a | 2 | 2 -(2 rows) - -select * from stat('select a from test_tsvector','b') order by ndoc desc, nentry desc, word collate "C"; - word | ndoc | nentry -------+------+-------- - b | 1 | 2 - a | 1 | 1 -(2 rows) - -select * from stat('select a from test_tsvector','c') order by ndoc desc, nentry desc, word collate "C"; - word | ndoc | nentry -------+------+-------- - b | 2 | 2 - a | 1 | 1 -(2 rows) - -select * from stat('select a from test_tsvector','d') order by ndoc desc, nentry desc, word collate "C"; - word | ndoc | nentry ------------+------+-------- - a | 2 | 2 - foo | 1 | 3 - bar | 1 | 2 - 345 | 1 | 1 - b | 1 | 1 - copyright | 1 | 1 - qq | 1 | 1 - qwerti | 1 | 1 -(8 rows) - -select * from stat('select a from test_tsvector','ad') order by ndoc desc, nentry desc, word collate "C"; - word | ndoc | nentry ------------+------+-------- - a | 2 | 4 - b | 2 | 4 - foo | 1 | 3 - bar | 1 | 2 - 345 | 1 | 1 - copyright | 1 | 1 - qq | 1 | 1 - qwerti | 1 | 1 -(8 rows) - -select to_tsquery('english', 'skies & books'); - to_tsquery ----------------- - 'sky' & 'book' -(1 row) - -select rank_cd(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea&thousand&years')); - rank_cd ------------ - 0.0555556 -(1 row) - -select rank_cd(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('granite&sea')); - rank_cd ------------ - 0.0238095 -(1 row) - -select rank_cd(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea')); - rank_cd ---------- - 0.2 -(1 row) - -select headline('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -', to_tsquery('sea&thousand&years')); - headline --------------------------------------------- - <b>sea</b> a <b>thousand</b> <b>years</b>,+ - A <b>thousand</b> <b>years</b> to trace + - The granite features of this cliff -(1 row) - -select headline('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -', to_tsquery('granite&sea')); - headline -------------------------------------------- - <b>sea</b> a thousand years, + - A thousand years to trace + - The <b>granite</b> features of this cliff -(1 row) - -select headline('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -', to_tsquery('sea')); - headline ------------------------------------- - <b>sea</b> a thousand years, + - A thousand years to trace + - The granite features of this cliff -(1 row) - -select headline(' -<html> -<!-- some comment --> -<body> -Sea view wow <u>foo bar</u> <i>qq</i> -<a href="http://www.google.com/foo.bar.html" target="_blank">YES </a> -ff-bg -<script> - document.write(15); -</script> -</body> -</html>', -to_tsquery('sea&foo'), 'HighlightAll=true'); - headline ------------------------------------------------------------------------------ - + - <html> + - <!-- some comment --> + - <body> + - <b>Sea</b> view wow <u><b>foo</b> bar</u> <i>qq</i> + - <a href="http://www.google.com/foo.bar.html" target="_blank">YES </a>+ - ff-bg + - <script> + - document.write(15); + - </script> + - </body> + - </html> -(1 row) - ---check debug -select * from public.ts_debug('Tsearch module for PostgreSQL 7.3.3'); - ts_name | tok_type | description | token | dict_name | tsvector ----------+-----------+-----------------+------------+----------------+-------------- - english | asciiword | Word, all ASCII | Tsearch | {english_stem} | 'tsearch' - english | blank | Space symbols | | {} | - english | asciiword | Word, all ASCII | module | {english_stem} | 'modul' - english | blank | Space symbols | | {} | - english | asciiword | Word, all ASCII | for | {english_stem} | - english | blank | Space symbols | | {} | - english | asciiword | Word, all ASCII | PostgreSQL | {english_stem} | 'postgresql' - english | blank | Space symbols | | {} | - english | version | Version number | 7.3.3 | {simple} | '7.3.3' -(9 rows) - ---check ordering -insert into test_tsvector values (null, null); -select a is null, a from test_tsvector order by a; - ?column? | a -----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - f | - f | - f | - f | - f | - f | - f | - f | - f | - f | 'a':1A,2,3B 'b':5A,6A,7C,8 - f | 'a':1A,2,3C 'b':5A,6B,7C,8B - f | 'bar':2,8 'foo':1,3,6 'qq':7 - f | '345':1 'copyright':3 'qwerti':2 - f | '7w' 'ch' 'd7' 'eo' 'gw' 'i4' 'lq' 'o6' 'qt' 'y0' - f | 'ar' 'ei' 'kq' 'ma' 'qa' 'qh' 'qq' 'qz' 'rx' 'st' - f | 'gs' 'i6' 'i9' 'j2' 'l0' 'oq' 'qx' 'sc' 'xe' 'yu' - f | 'ap' 'i2' 'k0' 'n4' 'ou' 'px' 'qe' 'qo' 'qr' 't1' 'yz' - f | 'cb' 'ib' 'ih' 'jx' 'oy' 'ph' 'tl' 'ty' 'vb' 'vu' 'wu' - f | 'cp' 'i0' 'ia' 'jh' 'kh' 'kl' 'ql' 'sb' 'u3' 'wy' 'ys' - f | 'cy' 'ey' 'gr' 'lq' 'n1' 'pp' 'pq' 'qb' 'qe' 'se' 'wb' - f | 'ja' 'k2' 'ko' 'o6' 'ob' 'ps' 't9' 'tz' 'uk' 'um' 'vv' - f | '5e' 'a2' 'ag' 'ay' 'dc' 'ef' 'jd' 'mn' 'pl' 'qp' 'wu' 'xd' - f | '7a' 'ap' 'bl' 'fl' 'g7' 'ko' 'ma' 'qi' 'ri' 'rp' 'y5' 'yo' - f | 'a4' 'an' 'go' 'ki' 'px' 'qq' 'td' 'tm' 'ur' 'wc' 'wp' 'yx' - f | 'aa' 'ah' 'bo' 'dz' 'pd' 'pm' 'qf' 'qw' 'rd' 'rn' 'tf' 'wu' - f | 'aj' 'ar' 'ay' 'eb' 'g5' 'ic' 'la' 'u2' 'up' 'wb' 'x8' 'yr' - f | 'aw' 'dz' 'el' 'hb' 'iu' 'lc' 'm9' 'r5' 'rt' 'uz' 'x2' 'zp' - f | 'dz' 'ea' 'h8' 'j9' 'mr' 'pc' 'qa' 'qt' 'rm' 'rv' 'ub' 'wg' - f | 'ew' 'fi' 'g2' 'ku' 'od' 'pk' 'tm' 'tq' 'tu' 'uf' 'wn' 'y1' - f | 'gi' 'n3' 'n7' 'om' 'qa' 'r7' 're' 'ug' 'w7' 'wi' 'x4' 'yn' - f | '3k' 'f5' 'if' 'kg' 'lj' 'ol' 'qr' 'so' 'ta' 'u1' 'vu' 'wr' 'y2' - f | '70' 'e0' 'ep' 'ex' 'hh' 'jn' 'kg' 'nq' 'rg' 'rj' 'uf' 'vs' 'ys' - f | 'ag' 'al' 'ay' 'fw' 'hi' 'ou' 'p6' 'qy' 'rg' 'rz' 'sx' 'uy' 'zb' - f | 'cm' 'dm' 'eh' 'em' 'ik' 'j0' 'kk' 'lp' 'ng' 'or' 'pu' 'qd' 'tw' - f | 'di' 'ec' 'i4' 'iq' 'iw' 'ko' 'l3' 'mw' 'py' 'qo' 'rx' 'wt' 'yl' - f | '5q' 'av' 'd2' 'd4' 'e5' 'jc' 'km' 'mm' 'pa' 'rs' 's4' 'si' 'tc' 'ut' - f | '6o' 'av' 'ed' 'ee' 'gf' 'ii' 'o8' 'og' 'om' 'qs' 'ta' 'th' 'tk' 'w0' - f | '7y' 'aq' 'cu' 'd3' 'h2' 'ih' 'oc' 'qh' 'rc' 'rs' 't3' 'ud' 'we' 'zt' - f | 'b7' 'eu' 'g4' 'hw' 'in' 'pi' 'qt' 'r0' 'rg' 'sn' 'sz' 'tc' 'wd' 'zs' - f | 'fb' 'in' 'iy' 'lu' 'p4' 'pd' 'qa' 'qq' 's6' 'ta' 'y1' 'yg' 'yy' 'zk' - f | 'at' 'ec' 'hs' 'ix' 'ks' 'lq' 'ls' 'pk' 'qs' 'qy' 'rc' 'ut' 'xv' 'yo' 'ys' - f | 'az' 'bl' 'dl' 'ec' 'gg' 'jq' 'o0' 'oj' 'q8' 'qq' 'ta' 'un' 'wb' 'wo' 'xu' - f | 'c0' 'd4' 'dw' 'ef' 'em' 'j2' 'kj' 'ql' 'rn' 'ta' 'uk' 'vv' 'wf' 'y9' 'zq' - f | 'gn' 'gx' 'hf' 'ji' 'kx' 'nh' 'o6' 'pe' 'q1' 'qt' 'rw' 'sc' 'ss' 'yh' 'zm' - f | 'h7' 'in' 'is' 'o7' 'q0' 'qa' 'qq' 'rl' 'rs' 'tu' 'u9' 'v3' 'vr' 'yq' 'zy' - f | 'ae' 'af' 'ay' 'da' 'dp' 'dq' 'e1' 'gi' 'gk' 'rl' 'sf' 'ta' 'td' 'tf' 'tt' 'tw' - f | 'aj' 'cl' 'cy' 'ee' 'i3' 'ir' 'nx' 'oz' 'qm' 'qw' 'r2' 't2' 'u0' 'ub' 'ur' 'wj' - f | 'av' 'dp' 'ff' 'fx' 'jk' 'ke' 'lb' 'lm' 'n1' 'ql' 'rv' 's4' 'uv' 'wl' 'ws' 'yj' - f | 'b8' 'eh' 'g5' 'gn' 'jo' 'mx' 'og' 'ol' 'on' 'px' 'rr' 'sl' 'un' 'uz' 'wv' 'yf' - f | 'bt' 'dy' 'gy' 'hg' 'i7' 'it' 'jv' 'lh' 'ox' 'qo' 'ri' 's3' 'ss' 'u1' 'uq' 'wr' - f | 'ef' 'ej' 'fn' 'ho' 'ia' 'il' 'iq' 'mj' 'nr' 'p5' 'rm' 'sd' 'ux' 'vi' 'wq' 'y6' - f | '25' 'cd' 'd2' 'dv' 'eo' 'es' 'f4' 'oc' 'qc' 'sh' 'te' 'tv' 'wd' 'wn' 'wo' 'xp' 'zt' - f | '2y' '8i' 'by' 'dl' 'h6' 'hj' 'm3' 'ml' 'qa' 'qx' 'r1' 'rm' 'rn' 'u4' 'uj' 'wo' 'xv' - f | 'ad' 'bs' 'dx' 'fi' 'i5' 'ia' 'j3' 'm5' 'mn' 'nr' 'ox' 'tg' 'un' 'vo' 'wj' 'wt' 'ys' - f | 'am' 'hw' 'jd' 'na' 'oe' 'pw' 'ql' 'qr' 's9' 'sk' 'u7' 'wa' 'wg' 'wu' 'x6' 'xr' 'yf' - f | 'de' 'mc' 'q1' 'q8' 'qp' 'qt' 'rq' 'rv' 'sa' 'sn' 'u8' 'vs' 'w9' 'wo' 'wp' 'ww' 'yl' - f | 'aa' 'af' 'ee' 'hb' 'ih' 'j2' 'lv' 'mw' 'pp' 'q3' 'rd' 'tb' 'td' 'ua' 'ug' 'up' 'xh' 'yy' - f | 'an' 'cp' 'dq' 'ej' 'ez' 'lj' 'ln' 'nu' 'qy' 'sm' 't8' 'td' 'tg' 'us' 'uw' 'vn' 'y4' 'z9' - f | 'ca' 'do' 'h5' 'i9' 'io' 'jk' 'jl' 'kn' 'qq' 'tm' 'ul' 'w9' 'wb' 'wp' 'wt' 'wx' 'y3' 'zd' - f | '16' 'ae' 'al' 'ef' 'eg' 'ew' 'gi' 'ha' 'id' 'ng' 'o3' 'on' 'p9' 'rz' 'uf' 'vg' 'wo' 'wr' 'zh' - f | 'ca' 'fu' 'hv' 'la' 'mt' 'ov' 'pl' 'q8' 'r3' 'sp' 'sy' 'tg' 'to' 'tv' 'wn' 'x4' 'yh' 'yp' 'ze' - f | 'cv' 'ds' 'dx' 'dy' 'ex' 'hh' 'lf' 'mq' 'qe' 'qu' 'rb' 'tb' 'tv' 'tz' 'ue' 'ui' 'wi' 'yb' 'zz' - f | 'ec' 'fd' 'gm' 'it' 'iu' 'kx' 'l1' 'pi' 'q1' 'qe' 'qs' 'ra' 'ri' 'rp' 'tn' 'to' 'vx' 'wh' 'wl' - f | 'eq' 'g7' 'jc' 'jf' 'ji' 'lw' 'ma' 'oe' 'p7' 'qb' 'qj' 'qo' 'u7' 'v7' 'w3' 'wd' 'wz' 'xg' 'yr' - f | 'a2' 'cs' 'ee' 'em' 'gk' 'hv' 'iy' 'kq' 'nc' 'pb' 'q5' 'qy' 'rq' 'rr' 'ts' 'uq' 'vt' 'w0' 'y6' 'yz' - f | 'al' 'fh' 'fk' 'gy' 'he' 'ie' 'iz' 'lq' 'oh' 'pu' 'q7' 's6' 'sd' 'sr' 'sw' 'uu' 'uz' 'v6' 'ws' 'xo' - f | 'aw' 'bm' 'dw' 'e5' 'ht' 'j4' 'kv' 'm5' 'oi' 'qa' 'qe' 'qf' 'ri' 'rj' 't6' 't8' 'un' 'wc' 'yb' 'yj' - f | 'az' 'bl' 'bo' 'cf' 'gt' 'h0' 'hx' 'iq' 'k2' 'kb' 'oc' 'qg' 'qn' 'qz' 're' 'rl' 'rv' 'xp' 'y8' 'yf' - f | 'bj' 'dq' 'e2' 'ec' 'fs' 'g8' 'gd' 'iw' 'jt' 'nn' 'ns' 'o9' 'p0' 'tb' 'u7' 'uv' 'wf' 'wr' 'xf' 'z3' - f | 'bs' 'ee' 'gz' 'hv' 'ib' 'kc' 'lb' 'nu' 'ps' 'pt' 'qh' 'ud' 'vo' 'vq' 'vu' 'wb' 'wj' 'x3' 'xu' 'yf' - f | '2o' 'a5' 'cg' 'ch' 'cp' 'eb' 'eg' 'eh' 'ew' 'fu' 'g4' 'hc' 'hx' 'il' 'li' 'rd' 'sf' 'sw' 'tg' 'uc' 'zj' - f | '6d' '8t' 'bl' 'gu' 'iu' 'kd' 'kj' 'kq' 'nw' 'nx' 'o6' 'oa' 'qk' 'ql' 'rd' 'ri' 'uc' 'vi' 'wy' 'xq' 'z2' - f | 'a3' 'ch' 'cs' 'e1' 'gq' 'gx' 'lz' 'nh' 'os' 'po' 'qs' 'rr' 'tx' 'ud' 'uj' 'uv' 've' 'w0' 'wj' 'xo' 'xz' - f | 'aa' 'al' 'e9' 'hm' 'ir' 'kc' 'l0' 'pi' 'po' 'qa' 'qk' 'r0' 'rd' 'rz' 'se' 'sr' 'vp' 'w6' 'w8' 'yd' 'yk' - f | 'am' 'bv' 'dt' 'dy' 'ed' 'gx' 'm7' 'mt' 'q5' 'qv' 'rr' 'sh' 'th' 'ut' 'wd' 'wm' 'y1' 'ym' 'yq' 'yr' 'yt' - f | '4f' 'e4' 'ep' 'fa' 'ff' 'iv' 'j4' 'kw' 'oj' 'pa' 'pw' 'q0' 'rv' 'ry' 't3' 'ul' 'vq' 'w1' 'wj' 'xm' 'ye' 'yu' - f | '7a' 'a3' 'dl' 'fo' 'hb' 'ki' 'kk' 'lo' 'pl' 'q5' 'qy' 'r1' 'rj' 'sy' 'tv' 'w3' 'wm' 'wn' 'yj' 'yr' 'zi' 'zq' - f | '1l' 'b5' 'dq' 'fw' 'hz' 'in' 'ml' 'nb' 'nu' 'o2' 'or' 'q7' 'qb' 'qh' 'qt' 'rv' 'so' 'tp' 'tr' 'u0' 'v6' 'wl' 'xb' - f | '5y' 'aw' 'e2' 'gd' 'gn' 'hn' 'ig' 'k9' 'ki' 'oj' 'pk' 'ql' 'qz' 'sl' 't3' 'u4' 'v8' 'wg' 'wu' 'xk' 'ya' 'yf' 'zr' - f | '6t' 'a9' 'db' 'ea' 'ec' 'ez' 'fq' 'gj' 'hb' 'hs' 'lc' 'or' 'p4' 'ph' 'pp' 'qr' 'qx' 'rc' 'rl' 'tn' 'u5' 'w9' 'x1' - f | 'bt' 'c5' 'd1' 'd7' 'g6' 'gk' 'ib' 'iv' 'ml' 'om' 'qd' 'qg' 'ql' 'r3' 'rc' 'u4' 'uh' 'uo' 'wh' 'y0' 'yn' 'yz' 'zo' - f | '1b' 'cp' 'di' 'ed' 'fv' 'gx' 'hs' 'i8' 'lh' 'lq' 'lz' 'p7' 'p8' 'pc' 'ql' 'qn' 'qw' 're' 'rg' 'tv' 'we' 'wp' 'yr' 'zj' - f | '1i' 'db' 'e4' 'er' 'fh' 'ft' 'hm' 'lg' 'll' 'of' 'og' 'q6' 'qj' 'r0' 're' 'th' 'up' 'ut' 'uw' 'vf' 'wq' 'ws' 'wx' 'zi' - f | 'a0' 'ck' 'fp' 'g4' 'ib' 'ih' 'im' 'iq' 'kz' 'll' 'lv' 'nc' 'oq' 'qf' 'qv' 'rg' 'rk' 'tc' 'tn' 'u1' 'u8' 'uj' 'un' 'vv' - f | 'ak' 'ar' 'dg' 'ds' 'ep' 'fv' 'ge' 'jd' 'no' 'on' 'q5' 'qd' 'qo' 'qv' 'qx' 'r7' 'ra' 'ru' 'sa' 'ud' 'uo' 'wl' 'ye' 'yl' - f | 'av' 'es' 'fl' 'gt' 'he' 'it' 'kp' 'mu' 'nc' 'ol' 'om' 'ph' 'qr' 'ra' 'rk' 'ui' 'vh' 'w6' 'wm' 'ws' 'yu' 'z0' 'zl' 'zm' - f | 'da' 'e5' 'ec' 'gd' 'gf' 'jj' 'kx' 'ly' 'pd' 'pj' 'q6' 'qk' 'rm' 'sa' 'te' 'ut' 'wa' 'wc' 'wl' 'ws' 'wv' 'zb' 'zk' 'zz' - f | '12' 'ao' 'ed' 'ek' 'ew' 'ey' 'fm' 'gr' 'hc' 'ht' 'io' 'ir' 'jb' 'jw' 'ke' 'ld' 'qj' 'se' 'tm' 'tn' 'tw' 'wv' 'y5' 'yt' 'z6' - f | '1p' '51' 'aj' 'av' 'bj' 'bn' 'c1' 'dx' 'ex' 'gz' 'he' 'ia' 'ic' 'ip' 'kn' 'mx' 'o6' 'or' 'ql' 'rc' 'wf' 'wi' 'wn' 'y8' 'yr' - f | 'av' 'ej' 'el' 'ep' 'fp' 'hh' 'hz' 'l9' 'mr' 'q3' 'qn' 'qq' 'qy' 'rh' 'tw' 'vt' 'vu' 'w5' 'wg' 'wk' 'wo' 'x5' 'y9' 'yk' 'zm' - f | 'b6' 'bq' 'dv' 'e1' 'ez' 'f5' 'fh' 'ik' 'iy' 'jy' 'li' 'm2' 'qe' 'rp' 'te' 'u4' 'u8' 'uo' 'w3' 'w8' 'we' 'wo' 'wu' 'x5' 'yl' - f | 'cq' 'er' 'hy' 'ie' 'jg' 'ke' 'lw' 'mf' 'p0' 'pe' 'pv' 'qk' 'qt' 'qy' 'sh' 'th' 'ti' 'ue' 'w5' 'wl' 'y4' 'y5' 'yi' 'yy' 'za' - f | 'a3' 'bh' 'c2' 'ca' 'e1' 'fb' 'fe' 'hd' 'hx' 'jc' 'md' 'nl' 'q9' 'qi' 'qq' 'qs' 'qt' 'rx' 'te' 'tv' 'u2' 'w8' 'wi' 'wr' 'xq' 'y9' - f | 'af' 'dt' 'e4' 'e8' 'eq' 'et' 'gr' 'kr' 'kv' 'lu' 'oy' 'pb' 'qh' 'ql' 'qw' 'r4' 't8' 'tb' 'td' 'tn' 'uc' 'uj' 'wh' 'xn' 'xs' 'yi' - f | 'au' 'bo' 'dz' 'ek' 'eq' 'et' 'fa' 'hw' 'id' 'im' 'kr' 'p4' 'qx' 'rb' 'rx' 'sf' 'tl' 'tx' 'uf' 'ui' 'uw' 'vr' 'wb' 'wn' 'xw' 'yd' - f | 'ay' 'bg' 'bp' 'cm' 'eg' 'ev' 'ff' 'go' 'hc' 'hl' 'jj' 'l0' 'os' 'q9' 'qc' 'qh' 'qo' 'rt' 'rx' 'so' 'sr' 'to' 'tx' 'uk' 'wb' 'y4' - f | 'd6' 'e4' 'ev' 'fd' 'i3' 'if' 'j1' 'j5' 'o8' 'oj' 'ok' 'pw' 'qc' 'qd' 'qf' 'qt' 't8' 'tw' 'u1' 'u7' 'wr' 'wv' 'ww' 'yh' 'yn' 'yz' - f | '1j' 'ag' 'c3' 'cl' 'e4' 'ef' 'eh' 'f5' 'fi' 'gy' 'hx' 'lw' 'oa' 'pu' 'qa' 'qi' 'qt' 'qu' 'rl' 'ro' 'rs' 'sg' 'uq' 'wq' 'ya' 'yh' 'ys' - f | 'a7' 'ah' 'cj' 'co' 'cv' 'dt' 'ex' 'fs' 'gx' 'hn' 'jv' 'kp' 'l5' 'od' 'on' 'pr' 'q6' 'q8' 'qi' 'qq' 'qr' 'rl' 'u2' 'wb' 'wx' 'yh' 'zq' - f | 'bg' 'eh' 'eq' 'gg' 'gh' 'gm' 'gx' 'i7' 'iv' 'm3' 'mv' 'n3' 'o6' 'ox' 'oz' 'pb' 'qk' 'rj' 'rs' 'sk' 'su' 'tg' 'uf' 'uj' 've' 'ww' 'yf' - f | 'cd' 'dl' 'e6' 'en' 'eu' 'gg' 'je' 'kp' 'lv' 'pv' 'q0' 'q3' 'qc' 'qd' 'qo' 'qs' 'qz' 'rw' 'se' 'tx' 'uh' 'uj' 'ul' 'wo' 'ye' 'yi' 'zx' - f | 'd6' 'do' 'e4' 'eg' 'hm' 'i3' 'kg' 'nz' 'ow' 'pc' 'pv' 'q0' 'q4' 'q6' 'qx' 'r0' 'ri' 'sm' 'sn' 'tw' 'u9' 'ul' 'up' 'vk' 'we' 'wm' 'zv' - f | 'a1' 'cd' 'cl' 'd8' 'ek' 'ig' 'ih' 'in' 'lq' 'o3' 'ow' 'px' 'qg' 'qm' 'qq' 'qr' 'qs' 'qy' 'rd' 'rh' 'to' 'tq' 'ul' 'wc' 'x9' 'ya' 'yf' 'yw' - f | 'aa' 'as' 'cg' 'dh' 'dn' 'dr' 'e0' 'h2' 'hr' 'j2' 'jf' 'js' 'kc' 'kw' 'ld' 'lh' 'mk' 'n3' 'q3' 'qe' 'ql' 'rv' 's3' 'w0' 'xg' 'ym' 'yt' 'zv' - f | 'ap' 'ca' 'dt' 'dx' 'ep' 'f5' 'fg' 'gq' 'hi' 'hj' 'i4' 'ic' 'it' 'iy' 'jl' 'lz' 'nd' 'o9' 'og' 'oq' 'pk' 'q6' 'qo' 'ra' 'sf' 'wd' 'wt' 'x9' - f | 'e2' 'ef' 'ev' 'fe' 'ij' 'j8' 'jm' 'kw' 'nb' 'ny' 'o6' 'o7' 'ou' 'pb' 'qd' 'qv' 'rh' 'rp' 's7' 'ti' 'ub' 'uk' 'wh' 'wi' 'wj' 'xj' 'xo' 'yx' - f | '3d' 'ad' 'dr' 'ee' 'ez' 'f4' 'fd' 'fg' 'fu' 'g9' 'gk' 'h9' 'hl' 'iz' 'jd' 'nb' 'o0' 'oo' 'oy' 'qj' 'qt' 'rp' 'ru' 'sv' 'tl' 'tv' 'wf' 'wp' 'wy' - f | '5t' 'al' 'db' 'dt' 'dx' 'ea' 'en' 'g6' 'gc' 'gm' 'gy' 'if' 'ii' 'ik' 'jb' 'jv' 'k5' 'po' 'pv' 'py' 'qj' 'qp' 'rz' 'ux' 'v1' 'w4' 'w8' 'wi' 'yv' - f | 'a1' 'b0' 'd0' 'db' 'ef' 'er' 'ev' 'ew' 'fe' 'fm' 'g8' 'la' 'n5' 'oh' 'os' 'pk' 'pn' 'qq' 'r7' 'sq' 'tw' 'ua' 'uu' 'wa' 'wk' 'wr' 'wu' 'xc' 'yi' - f | 'ab' 'c1' 'cl' 'd3' 'do' 'e5' 'e8' 'eg' 'ek' 'ex' 'gy' 'ia' 'iq' 'iw' 'jf' 'kv' 'm9' 'n4' 'nh' 'nj' 'q3' 'qa' 'qe' 'qg' 'qm' 'sy' 'ta' 'w5' 'w6' - f | 'av' 'bh' 'cd' 'cw' 'dv' 'em' 'gn' 'iw' 'ja' 'ki' 'lc' 'lx' 'my' 'oi' 'ox' 'q0' 'qb' 'qi' 'qn' 'uf' 'ux' 'we' 'wn' 'xd' 'xq' 'y4' 'y9' 'zf' 'zs' - f | 'ch' 'e1' 'fi' 'g8' 'go' 'hf' 'i1' 'ic' 'in' 'it' 'j7' 'jk' 'jl' 'jv' 'nm' 'of' 'oz' 'r8' 'rc' 'rk' 'rp' 'rx' 'sp' 'tb' 'tv' 'tw' 'ul' 'wx' 'zj' - f | 'an' 'dh' 'do' 'hs' 'hv' 'ia' 'ic' 'ne' 'of' 'oi' 'oq' 'pe' 'pg' 'q9' 'r5' 'rk' 'sc' 'sf' 'sh' 'ta' 'tb' 'tq' 'um' 'wb' 'wj' 'wm' 'wq' 'wt' 'yi' 'ym' - f | 'bb' 'bq' 'c2' 'cw' 'cy' 'db' 'dd' 'f3' 'fl' 'fn' 'id' 'ig' 'jb' 'kc' 'kl' 'lp' 'lx' 'mh' 'o0' 'pk' 'qi' 'qx' 'rq' 've' 'w8' 'wd' 'x1' 'y4' 'ye' 'zm' - f | 'c7' 'eb' 'er' 'gb' 'if' 'ko' 'ml' 'oq' 'ot' 'pa' 'qk' 'qs' 'rl' 'rp' 'sc' 'tf' 'tv' 'tw' 'uc' 'ud' 'uz' 'vk' 'vm' 'w0' 'wm' 'wu' 'yd' 'yq' 'yy' 'zu' - f | '27' '2e' '2p' 'aa' 'eh' 'en' 'eq' 'eu' 'ew' 'ff' 'g8' 'hv' 'mx' 'oi' 'pd' 'q3' 'qs' 'rl' 'sa' 'sw' 'te' 'tn' 'ty' 'uh' 'uo' 'wb' 'wh' 'wy' 'xx' 'z8' 'zt' - f | '3k' 'di' 'dp' 'em' 'ew' 'f5' 'hb' 'hn' 'j3' 'k0' 'lk' 'm4' 'mq' 'pr' 'qe' 'qo' 'qy' 'rc' 'ri' 'rt' 'so' 'ts' 've' 'w4' 'w7' 'wl' 'wn' 'wy' 'xf' 'y6' 'yt' - f | '3v' 'ab' 'at' 'bn' 'bz' 'cx' 'eh' 'gd' 'hd' 'hr' 'i9' 'j8' 'jg' 'jx' 'lf' 'ng' 'oj' 'ov' 'oz' 'pn' 'ps' 'qq' 'qr' 'ro' 'rt' 'u9' 'up' 'uz' 'yl' 'yw' 'zi' - f | '6n' 'da' 'dl' 'dx' 'el' 'ew' 'ff' 'fl' 'gc' 'iu' 'jh' 'jt' 'kt' 'lz' 'pd' 'q6' 'qj' 'ql' 'r3' 'ra' 'rn' 'ry' 'sg' 'tf' 'tp' 'tt' 'ub' 'ye' 'yf' 'yl' 'yp' - f | 'a3' 'ai' 'at' 'd7' 'eu' 'fu' 'gd' 'ii' 'ik' 'j0' 'je' 'lw' 'ly' 'mx' 'n7' 'pm' 'qf' 'qk' 'qt' 'ss' 'to' 'tq' 'un' 'vn' 'vq' 'wo' 'wz' 'yk' 'yy' 'zf' 'zg' - f | 'aa' 'ch' 'cm' 'db' 'dd' 'e7' 'eu' 'f5' 'fr' 'gg' 'ie' 'jc' 'kt' 'na' 'on' 'or' 'po' 'q0' 're' 's3' 'tb' 'tc' 'td' 'tt' 'tv' 'uf' 'wi' 'wq' 'wt' 'y8' 'ys' - f | 'ac' 'al' 'b3' 'bg' 'ci' 'cn' 'ea' 'f4' 'ih' 'ix' 'kt' 'p8' 'pk' 'qd' 'qe' 'qi' 'rb' 'rc' 'ru' 'te' 'th' 'tl' 'tn' 'vb' 'wb' 'ws' 'ww' 'xk' 'xo' 'yb' 'zf' - f | 'az' 'eq' 'fe' 'go' 'gv' 'ig' 'iz' 'ja' 'l4' 'mo' 'nm' 'no' 'of' 'pk' 'q9' 'qb' 'ql' 'qt' 'r7' 'rc' 'rm' 'tg' 'tv' 'u3' 'u7' 'wc' 'x4' 'xw' 'yl' 'zk' 'zn' - f | 'c8' 'db' 'dh' 'fh' 'g7' 'gm' 'if' 'ih' 'jd' 'li' 'ms' 'mt' 'no' 'or' 'p7' 'pc' 'qb' 'qm' 'sh' 'tk' 'uf' 'uz' 'vb' 'vp' 'wh' 'wr' 'wv' 'xh' 'xm' 'xp' 'zj' - f | '5s' 'aa' 'ar' 'bu' 'c2' 'cw' 'd2' 'ej' 'g8' 'gy' 'ij' 'iv' 'k0' 'l1' 'lb' 'm1' 'od' 'pm' 'q1' 'q4' 'q5' 'qd' 'rn' 'ry' 'sj' 'sm' 'ta' 'th' 'u8' 'vf' 'wr' 'xm' - f | '6d' 'ao' 'bo' 'cq' 'e0' 'fi' 'fm' 'h6' 'i2' 'jl' 'kn' 'mj' 'nv' 'oq' 'p6' 'qd' 'qi' 'qn' 'r2' 'r8' 'rd' 'sd' 'sl' 'ta' 'tp' 'ub' 'uh' 'w3' 'wh' 'y1' 'yj' 'zk' - f | 'aa' 'cb' 'd2' 'dd' 'de' 'e4' 'gd' 'go' 'hc' 'ic' 'in' 'ip' 'js' 'lm' 'o1' 'o3' 'pl' 'ra' 'rx' 'sj' 'ti' 'tu' 'tv' 'wc' 'we' 'wl' 'wq' 'wx' 'xg' 'xi' 'yc' 'yi' - f | 'ad' 'ai' 'ak' 'df' 'ee' 'fr' 'gg' 'i2' 'i5' 'ig' 'ij' 'ir' 'j1' 'kk' 'km' 'l9' 'qb' 'ql' 'qt' 'qw' 'rb' 's2' 'te' 'ue' 'w0' 'wk' 'wu' 'y4' 'yb' 'yf' 'yp' 'zn' - f | 'ap' 'aw' 'ba' 'cz' 'dw' 'eh' 'g5' 'g9' 'gc' 'gi' 'go' 'ir' 'j8' 'kl' 'mg' 'p6' 'ql' 'qu' 'qz' 'sa' 't7' 'tw' 'v5' 'v7' 'wj' 'xt' 'y6' 'yg' 'yh' 'yq' 'z5' 'zj' - f | 'bp' 'cy' 'd6' 'd7' 'em' 'et' 'gf' 'gt' 'hl' 'ib' 'io' 'kz' 'mx' 'nm' 'oe' 'pd' 'qf' 'qk' 'rk' 'rz' 'se' 'si' 'tq' 'tu' 'uu' 'v3' 'wa' 'wf' 'wg' 'wx' 'yd' 'ym' - f | '15' '72' 'av' 'ba' 'dt' 'ev' 'hd' 'hu' 'jy' 'lo' 'mo' 'mt' 'oe' 'oi' 'pa' 'qc' 'qe' 'qt' 'r1' 'rg' 'ry' 'td' 'tk' 'us' 'wk' 'wq' 'ww' 'wz' 'x7' 'xq' 'y9' 'yx' 'z3' - f | '3q' 'an' 'cp' 'ej' 'fx' 'gj' 'i3' 'ib' 'ik' 'jt' 'k5' 'n9' 'no' 'os' 'pg' 'qo' 'qp' 'qx' 'rb' 'rk' 'su' 'tk' 'tv' 'u1' 'up' 'w1' 'we' 'wj' 'wr' 'yf' 'yq' 'z5' 'zd' - f | 'ak' 'dx' 'eb' 'es' 'eu' 'fd' 'ga' 'gf' 'hl' 'i2' 'kb' 'kd' 'mz' 'n1' 'o8' 'qe' 'qs' 'qw' 'rp' 'sf' 'sk' 'sp' 't0' 'tu' 'uh' 'v9' 'vj' 'vs' 'vw' 'wv' 'xs' 'yo' 'z2' - f | '25' 'ad' 'ao' 'at' 'ch' 'db' 'dz' 'eq' 'fp' 'gq' 'ih' 'iu' 'jo' 'km' 'p1' 'pb' 'qa' 'qb' 'qc' 'qg' 'ql' 'qq' 'qy' 'rb' 'rf' 'rl' 'rw' 'sp' 'ty' 'uk' 'ur' 'wu' 'wv' 'yw' - f | '1o' '3w' 'aw' 'be' 'bx' 'e7' 'eo' 'ey' 'ff' 'fx' 'ht' 'jb' 'km' 'kv' 'l6' 'la' 'nu' 'ny' 'pk' 'qc' 'qi' 'rk' 'ro' 'rw' 's3' 'sh' 't5' 'u1' 'uy' 'vi' 'vm' 'wi' 'x3' 'xo' 'yn' - f | '2a' '5d' 'ag' 'ar' 'at' 'dc' 'fa' 'fh' 'gn' 'gx' 'hh' 'iy' 'kd' 'ke' 'ld' 'nd' 'nx' 'oz' 'qc' 'qd' 'qo' 'qp' 'qu' 'r0' 'rg' 't5' 'tc' 'tg' 'th' 'tk' 'w2' 'w5' 'wt' 'yl' 'zn' - f | '30' 'cl' 'dl' 'e7' 'ee' 'ef' 'eh' 'el' 'fk' 'fn' 'fs' 'gi' 'gw' 'i2' 'il' 'l7' 'm6' 'm9' 'mf' 'o8' 'ob' 'ok' 'pc' 'pe' 'qb' 'qe' 'qn' 'qr' 'tu' 'uc' 'ud' 'wp' 'xq' 'ym' 'ys' - f | '5o' 'ar' 'at' 'az' 'bf' 'bv' 'e0' 'ew' 'fh' 'hd' 'ij' 'in' 'is' 'iu' 'ji' 'l5' 'ld' 'mz' 'nk' 'oc' 'p2' 'ph' 'px' 'q9' 'qi' 'qn' 'qo' 'qq' 'qv' 'rw' 'sf' 'sp' 'te' 'up' 'y8' - f | 'a2' 'bx' 'e4' 'em' 'eu' 'ey' 'gd' 'jj' 'k1' 'lm' 'my' 'oz' 'p8' 'pa' 'pj' 'qj' 'qp' 'qy' 'ri' 'rw' 'sj' 'sw' 'tv' 'uj' 'uk' 'um' 'ux' 'vs' 'wd' 'wf' 'xj' 'xs' 'xx' 'xy' 'yd' - f | 'ab' 'at' 'cr' 'e9' 'g0' 'gv' 'ib' 'iv' 'iz' 'jb' 'jm' 'k0' 'kh' 'o2' 'o7' 'oq' 'ot' 'pj' 'ps' 'qj' 'qz' 'r9' 'rn' 'sa' 't1' 'ti' 'tq' 'ue' 'us' 'wc' 'wo' 'ww' 'yi' 'ys' 'za' - f | 'by' 'c1' 'da' 'ec' 'ej' 'ew' 'g4' 'gq' 'gt' 'ir' 'jv' 'kl' 'kz' 'l5' 'nm' 'oi' 'on' 'pc' 'q2' 'qx' 'qz' 'rd' 'se' 'sq' 'ti' 'tk' 'to' 'ur' 'w8' 'wm' 'wr' 'y1' 'yn' 'yp' 'zy' - f | 'd8' 'dm' 'eg' 'et' 'ge' 'gz' 'i8' 'ig' 'il' 'iy' 'jo' 'k6' 'lm' 'oj' 'p3' 'pw' 'qd' 'qp' 'qq' 'qz' 'r7' 's1' 'sr' 'sx' 't4' 'uz' 'vm' 'vr' 'w0' 'wj' 'wp' 'xc' 'y1' 'yj' 'zd' - f | '0w' 'bu' 'df' 'dj' 'du' 'dy' 'dz' 'gj' 'ho' 'je' 'kw' 'm1' 'o3' 'oo' 'pf' 'qh' 'qn' 'qu' 'rf' 'rj' 'ro' 'rv' 'ss' 't4' 'tc' 'tf' 'um' 'uo' 'v6' 'v8' 'wn' 'wo' 'xh' 'y0' 'yz' 'zr' - f | 'a3' 'do' 'ef' 'hp' 'ih' 'im' 'jp' 'km' 'lv' 'mt' 'nh' 'oc' 'od' 'ph' 'pi' 'pj' 'qe' 'qh' 'qr' 'rq' 'rr' 'rs' 'sc' 'st' 'ts' 'tt' 'tw' 'ut' 'vv' 'w7' 'wv' 'x3' 'xa' 'xc' 'xl' 'zc' - f | 'ad' 'aj' 'ar' 'b4' 'dj' 'eo' 'eu' 'ex' 'gt' 'hj' 'ie' 'ij' 'iz' 'kg' 'ks' 'l1' 'ld' 'me' 'my' 'mz' 'oi' 'pr' 'qb' 'ry' 'sd' 'to' 'u5' 'wc' 'wf' 'wl' 'xs' 'y2' 'yg' 'z1' 'zj' 'zt' - f | '2y' 'a3' 'ac' 'ae' 'ar' 'dp' 'ec' 'f8' 'f9' 'ga' 'gq' 'gs' 'hm' 'ib' 'if' 'im' 'j0' 'na' 'pb' 'pf' 'q9' 'qa' 'qu' 'qw' 'ra' 'rj' 'rm' 'rr' 'su' 'tj' 'vw' 'wb' 'wf' 'wk' 'wo' 'ym' 'yu' - f | '5y' 'a2' 'by' 'c9' 'e6' 'fd' 'fn' 'g7' 'h9' 'hj' 'j9' 'kg' 'kk' 'kn' 'kx' 'ln' 'q3' 'qa' 'qh' 'rb' 'rh' 'sj' 'sn' 'tx' 'tz' 'un' 'us' 've' 'vf' 'vh' 'wf' 'wp' 'wz' 'xj' 'xq' 'yi' 'yz' - f | 'a1' 'aj' 'bi' 'cd' 'cw' 'd2' 'e4' 'e9' 'gz' 'ij' 'k1' 'kb' 'kn' 'lw' 'me' 'nj' 'oq' 'p4' 'ph' 'ps' 'q3' 'q4' 'qj' 's7' 'sp' 'sx' 'tc' 'tw' 'v1' 'vj' 'vt' 'w7' 'wa' 'wb' 'wp' 'y1' 'zc' - f | 'a9' 'av' 'aw' 'dj' 'e0' 'ej' 'er' 'eu' 'ez' 'fr' 'gq' 'hh' 'hp' 'ko' 'kq' 'lw' 'n8' 'pe' 'pl' 'pn' 'pp' 'pv' 'q9' 'qc' 'qj' 'qs' 'qw' 'r6' 'rl' 'tq' 'u5' 'uu' 'uz' 'wn' 'wp' 'wr' 'zo' - f | 'dq' 'ed' 'el' 'fg' 'fv' 'gh' 'hx' 'i2' 'iv' 'jc' 'la' 'ld' 'nb' 'oa' 'of' 'oi' 'py' 'q7' 'qe' 'qr' 'qs' 'qw' 'qx' 'qz' 'rl' 'rr' 'sp' 't3' 'te' 'ti' 'tu' 'ue' 'w0' 'wa' 'wq' 'wu' 'xn' - f | '4p' 'bw' 'cq' 'dk' 'dr' 'dx' 'e3' 'e8' 'ex' 'ez' 'g9' 'h9' 'ih' 'in' 'kt' 'lt' 'me' 'o7' 'od' 'oi' 'on' 'p1' 'pu' 'qa' 'qd' 'qm' 'rz' 'sd' 'tn' 'ua' 'uz' 'vg' 'vx' 'wx' 'xy' 'ya' 'yl' 'yx' - f | '6b' 'af' 'd3' 'df' 'dg' 'ds' 'e1' 'eb' 'er' 'f3' 'ft' 'ho' 'ik' 'k1' 'k2' 'li' 'mj' 'ni' 'py' 'qx' 'rb' 'rp' 'rv' 'sd' 'sh' 'sl' 'u5' 'uf' 'vk' 'vs' 'vx' 'wg' 'wm' 'wr' 'ws' 'wz' 'xn' 'zh' - f | '4u' 'a3' 'ck' 'dw' 'e5' 'fu' 'ij' 'iu' 'jd' 'jp' 'ka' 'kb' 'ld' 'op' 'p1' 'po' 'pw' 'q5' 'q7' 'qf' 'qq' 'qr' 'qv' 'rm' 'sv' 't6' 'td' 'tp' 'uw' 'vb' 'w4' 'w6' 'wq' 'xh' 'y2' 'yf' 'yt' 'zj' 'zz' - f | '7n' 'ai' 'aj' 'ap' 'b9' 'bx' 'cu' 'dn' 'e1' 'e2' 'ed' 'eg' 'eo' 'hu' 'ie' 'ln' 'lq' 'nl' 'oa' 'qa' 'qe' 'qo' 'r5' 'ra' 'ri' 'rm' 'rw' 'ss' 'sx' 't3' 'tz' 'ut' 'uz' 'wd' 'we' 'wn' 'wq' 'wr' 'ws' - f | '8p' 'ao' 'aq' 'c1' 'dh' 'dt' 'e9' 'eo' 'ev' 'fp' 'fu' 'gc' 'gz' 'hk' 'i5' 'id' 'ip' 'iy' 'jg' 'jr' 'k2' 'lo' 'mb' 'mf' 'oi' 'ou' 'qp' 'qr' 'qw' 'qx' 'ra' 'rj' 'tl' 'ui' 'uu' 'uy' 'vb' 'wh' 'yt' - f | 'a7' 'bq' 'el' 'ey' 'fp' 'fu' 'fx' 'gr' 'hi' 'hl' 'jm' 'ki' 'kp' 'n6' 'o9' 'oj' 'op' 'ou' 'p7' 'pm' 'pp' 'q7' 'qa' 'qc' 'qj' 'qk' 'qm' 'si' 'st' 'tn' 'uc' 'w6' 'wp' 'xu' 'y5' 'yk' 'ys' 'yu' 'zs' - f | 'ah' 'ai' 'b8' 'ci' 'd2' 'ea' 'ed' 'en' 'et' 'fj' 'fv' 'ht' 'iu' 'iv' 'iw' 'jb' 'jd' 'ky' 'mj' 'nt' 'q1' 'qb' 'qe' 'ql' 'qz' 'ru' 'sg' 'tl' 'tm' 'tn' 'tw' 'ty' 'u1' 'ud' 'us' 'wj' 'y1' 'zd' 'zj' - f | 'bf' 'cf' 'cg' 'cj' 'ef' 'ej' 'ek' 'ep' 'fk' 'gi' 'ik' 'ir' 'j0' 'km' 'ko' 'lu' 'mr' 'n6' 'oc' 'op' 'pt' 'q4' 'qe' 'qk' 'qv' 'rc' 'rt' 's8' 't3' 'tk' 'ut' 'wc' 'we' 'ws' 'wt' 'ww' 'y8' 'yd' 'yr' - f | '1d' 'a2' 'af' 'bb' 'dd' 'dg' 'dl' 'du' 'ee' 'ff' 'g0' 'hj' 'hk' 'i4' 'iy' 'jt' 'kq' 'm6' 'mh' 'ov' 'qf' 'qh' 'qq' 'rt' 'sw' 'ta' 'tc' 'we' 'wn' 'wy' 'wz' 'xd' 'xs' 'y2' 'yg' 'yj' 'yk' 'zi' 'zq' 'zw' - f | '1l' 'e2' 'e4' 'eo' 'ep' 'fd' 'ha' 'hp' 'hx' 'io' 'iu' 'jr' 'jx' 'k4' 'l8' 'nb' 'oa' 'om' 'on' 'ow' 'p2' 'qh' 'qp' 'qz' 'ra' 'rz' 'sy' 'tk' 'tt' 'ul' 'uq' 'vm' 'wt' 'xg' 'xn' 'ya' 'yf' 'yw' 'yx' 'zp' - f | '2l' '55' 'aq' 'aw' 'cm' 'dq' 'eb' 'fl' 'fo' 'fp' 'fy' 'gk' 'hy' 'ic' 'iu' 'jc' 'jl' 'lc' 'mb' 'nx' 'oe' 'oo' 'oz' 'p5' 'pa' 'pf' 'qr' 'rj' 'sc' 'sz' 'td' 'tz' 'u1' 'vq' 'wu' 'xb' 'xx' 'y0' 'za' 'zc' - f | '4z' 'a2' 'eg' 'f4' 'g9' 'gi' 'hd' 'hn' 'ij' 'io' 'ix' 'iz' 'jb' 'jd' 'kk' 'l4' 'lh' 'lq' 'p9' 'pa' 'pb' 'q7' 'qh' 'qi' 'qk' 'qs' 'r9' 'rc' 'ro' 'rr' 's4' 'tj' 'tz' 'vi' 'we' 'wp' 'xe' 'yd' 'yg' 'zj' - f | '7h' 'an' 'b6' 'cl' 'e1' 'e4' 'en' 'ey' 'ff' 'gn' 'h7' 'ht' 'i5' 'i9' 'ia' 'iw' 'jx' 'kl' 'ku' 'op' 'pe' 'pt' 'qb' 'ql' 'qp' 'qy' 'ri' 'se' 'si' 'ta' 'ti' 'tl' 'tp' 'uo' 'vr' 'w4' 'wn' 'xi' 'xs' 'ym' - f | '7k' 'et' 'ew' 'ez' 'g9' 'gk' 'he' 'hp' 'ip' 'jp' 'jq' 'jr' 'jv' 'kb' 'kk' 'ku' 'lx' 'o0' 'o3' 'oa' 'or' 'ou' 'q7' 'qa' 'qf' 'qg' 'qh' 'qo' 'ro' 'rx' 'tl' 'us' 'vk' 'vw' 'wc' 'wp' 'x0' 'xz' 'yg' 'z5' - f | 'a5' 'ab' 'ad' 'ao' 'b6' 'ea' 'es' 'ez' 'gm' 'gv' 'i0' 'in' 'jw' 'lh' 'lm' 'my' 'oj' 'ox' 'oy' 'pv' 'q1' 'qf' 'qh' 'qj' 'qo' 'qs' 'qt' 'qw' 'r0' 'ri' 'rk' 'ry' 's4' 'vs' 'wa' 'xy' 'y5' 'yg' 'yl' 'zm' - f | 'aa' 'ap' 'b0' 'c6' 'e1' 'ex' 'fe' 'fu' 'fz' 'g1' 'g8' 'go' 'hl' 'iz' 'k3' 'ls' 'mw' 'mz' 'oa' 'ow' 'p6' 'q5' 'qh' 'qq' 'qx' 'ra' 'rh' 'rp' 'rw' 'tf' 'tk' 'tl' 'uc' 'wi' 'x1' 'xb' 'yb' 'yw' 'yz' 'zm' - f | 'ad' 'as' 'dn' 'dq' 'e7' 'ei' 'ey' 'fj' 'h2' 'hu' 'hw' 'i7' 'i9' 'iu' 'jr' 'ma' 'na' 'nh' 'nk' 'pa' 'q5' 'qd' 'qn' 's0' 'so' 'sx' 'u7' 'uc' 'um' 'vb' 'w9' 'wf' 'wh' 'wl' 'wn' 'wr' 'xl' 'y8' 'yl' 'zw' - f | 'ae' 'ai' 'di' 'ea' 'eb' 'ec' 'gw' 'h8' 'hb' 'iw' 'iz' 'jf' 'jg' 'ks' 'le' 'p8' 'pl' 'pp' 'qd' 'qg' 'qm' 'qn' 'qu' 'qy' 'r4' 'rf' 'rq' 's1' 'sc' 'sq' 'v8' 'vh' 'vk' 'wd' 'x7' 'y7' 'ym' 'yp' 'yr' 'yw' - f | 'ah' 'aw' 'e5' 'en' 'ew' 'fi' 'fm' 'gq' 'gt' 'hy' 'ib' 'ie' 'ir' 'j9' 'jr' 'js' 'ny' 'ob' 'or' 'ot' 'oy' 'pt' 'qi' 'rd' 'ri' 'rl' 'ro' 'rt' 'ss' 'tb' 'tg' 'th' 'vy' 'wq' 'wz' 'xt' 'y6' 'y8' 'zf' 'zh' - f | '43' '9w' 'ah' 'av' 'bl' 'dr' 'ef' 'es' 'fm' 'ft' 'gy' 'hg' 'hq' 'hy' 'iu' 'ix' 'j1' 'jn' 'lg' 'np' 'o9' 'op' 'ou' 'ox' 'qq' 'st' 'sw' 't8' 't9' 'vs' 'vw' 'wd' 'wx' 'wz' 'xr' 'xt' 'xv' 'y5' 'y8' 'yi' 'ze' - f | '5f' '5i' 'ak' 'cg' 'dh' 'di' 'dk' 'eq' 'f6' 'g1' 'g8' 'ie' 'md' 'o0' 'oj' 'oq' 'pt' 'q7' 'qb' 'qd' 'qe' 'qo' 'ro' 'ru' 'rv' 'rx' 'sp' 'sr' 'tf' 'tp' 'uh' 'ul' 'vs' 'xn' 'xw' 'y2' 'yh' 'yk' 'zg' 'zm' 'zq' - f | 'ab' 'ac' 'b9' 'br' 'c8' 'eh' 'en' 'eq' 'ev' 'ew' 'f6' 'gd' 'ik' 'j5' 'jm' 'kc' 'ke' 'ok' 'p6' 'pa' 'qv' 'ri' 'rm' 'ro' 'rp' 'rq' 'rx' 'rz' 'se' 'tb' 'td' 'ti' 'tj' 'tn' 'ul' 'wj' 'wp' 'ws' 'yh' 'zb' 'zc' - f | 'ak' 'at' 'cs' 'di' 'dt' 'ef' 'eq' 'f1' 'f8' 'fb' 'fh' 'fv' 'g4' 'hs' 'j2' 'jo' 'kq' 'lf' 'lq' 'no' 'oa' 'ot' 'q0' 'qh' 'qs' 'qw' 'se' 'so' 'sx' 't3' 'ul' 'un' 'uu' 'wn' 'ws' 'wx' 'y0' 'y3' 'yf' 'zb' 'zc' - f | 'bq' 'cc' 'cg' 'dc' 'fu' 'ho' 'ja' 'jt' 'km' 'lk' 'ln' 'o2' 'ob' 'ou' 'pd' 'ph' 'pn' 'pt' 'pz' 'q5' 'qa' 'ql' 'qq' 'qv' 'rk' 'rm' 'ry' 'sq' 'su' 'sx' 'uc' 'un' 'uo' 'ur' 'uy' 'w9' 'wj' 'wl' 'wm' 'yk' 'ym' - f | 'ca' 'cm' 'cr' 'eh' 'fd' 'fp' 'g8' 'ga' 'gh' 'h1' 'iu' 'je' 'jy' 'kh' 'lx' 'o4' 'oi' 'ov' 'ph' 'pm' 'qi' 'qv' 're' 'rg' 'rv' 'ry' 'si' 'sl' 'sv' 'sx' 'tq' 'tz' 'uh' 'uq' 'uy' 'wi' 'wo' 'y7' 'yc' 'yn' 'yy' - f | '1u' '5k' 'ax' 'az' 'c4' 'eo' 'fi' 'gq' 'hg' 'ho' 'is' 'jq' 'ki' 'oq' 'ov' 'p4' 'pl' 'po' 'qe' 'qf' 'qn' 'qy' 'r3' 'rm' 'sd' 'se' 'sz' 'tb' 'td' 'u4' 'u5' 'vl' 'vv' 'wb' 'we' 'wh' 'wz' 'y5' 'ye' 'yv' 'zh' 'zj' - f | '4r' 'ae' 'd9' 'da' 'ef' 'em' 'en' 'gi' 'h8' 'jq' 'kf' 'kh' 'kr' 'kv' 'li' 'lq' 'lr' 'm2' 'ny' 'po' 'q0' 'q8' 'qf' 'qg' 'qu' 'qw' 'qx' 'rd' 'rh' 'ry' 'sm' 'st' 't3' 'tc' 'u5' 'un' 'uw' 'wg' 'wt' 'yt' 'z9' 'zc' - f | '4t' 'ay' 'b3' 'dz' 'eo' 'ep' 'fd' 'fh' 'ht' 'hw' 'i3' 'jl' 'kn' 'op' 'qb' 'qd' 'ql' 'qm' 'qp' 'qy' 'rc' 'rh' 'rs' 'rw' 'sm' 't9' 'td' 'tu' 'tw' 'tz' 'u1' 'ug' 'uh' 'ur' 'vt' 'we' 'wi' 'wn' 'x7' 'y8' 'yn' 'yy' - f | '5z' 'ai' 'al' 'ax' 'cd' 'dr' 'e1' 'ep' 'fi' 'gh' 'gk' 'ha' 'hc' 'ht' 'i5' 'ic' 'id' 'iq' 'iz' 'kk' 'kt' 'ld' 'mi' 'oh' 'ov' 'pn' 'q2' 'q7' 'qm' 'qy' 'ri' 'ru' 'rv' 'sq' 'u8' 'u9' 'un' 'wq' 'wt' 'yd' 'yk' 'ys' - f | 'ar' 'c3' 'c6' 'e0' 'e9' 'ed' 'ex' 'fh' 'fi' 'gu' 'ha' 'hr' 'id' 'jr' 'kk' 'kr' 'kz' 'm0' 'mp' 'ng' 'om' 'pa' 'qa' 'qf' 'rj' 'sp' 'to' 'tu' 'ty' 'ub' 'ud' 'ug' 'uk' 'vj' 'vp' 'wf' 'wl' 'wu' 'x6' 'xs' 'y0' 'ya' - f | 'bt' 'dg' 'dz' 'e4' 'ek' 'ep' 'fi' 'gk' 'gl' 'gu' 'hl' 'ho' 'io' 'ls' 'ni' 'nw' 'pv' 'px' 'qe' 'qf' 'qj' 'ql' 'qu' 'qy' 'rw' 'rx' 'ss' 't7' 'tt' 'ty' 'ud' 'up' 'uq' 'uw' 'wn' 'wy' 'xt' 'yv' 'yx' 'za' 'zv' 'zw' - f | '2o' '6t' 'a7' 'bv' 'di' 'dm' 'ea' 'eo' 'fd' 'fj' 'ft' 'fw' 'gs' 'hu' 'hz' 'ik' 'it' 'jc' 'js' 'jx' 'jz' 'k4' 'lp' 'lx' 'ol' 'p3' 'pf' 'q9' 'qu' 'qv' 'qx' 're' 'rl' 'ro' 'sb' 'ur' 'vf' 'wd' 'wk' 'wo' 'wx' 'yp' 'zo' - f | '5y' 'av' 'ax' 'cl' 'cw' 'dh' 'di' 'g7' 'ga' 'gi' 'gx' 'gy' 'i0' 'iu' 'iv' 'jk' 'jv' 'k3' 'l2' 'lo' 'lq' 'mj' 'oj' 'pd' 'pn' 'pv' 'q6' 'qq' 'qv' 'sc' 'tt' 'tv' 'ug' 'ut' 've' 'vy' 'wi' 'xg' 'xo' 'yi' 'yl' 'yv' 'zm' - f | 'ab' 'av' 'c9' 'cs' 'cx' 'dy' 'e4' 'eb' 'ej' 'er' 'ez' 'f0' 'f2' 'ft' 'gk' 'h5' 'i5' 'ic' 'ig' 'io' 'ki' 'kz' 'lj' 'mm' 'of' 'og' 'ov' 'oz' 'p4' 'pl' 'qh' 'qx' 'rw' 's1' 'sf' 'tw' 'uc' 'ui' 'uo' 'uw' 'x4' 'xk' 'ze' - f | 'ac' 'bv' 'd2' 'de' 'e8' 'ea' 'go' 'gw' 'h4' 'ht' 'iy' 'jm' 'ot' 'pp' 'pw' 'pz' 'qd' 'qn' 'qu' 'qy' 'r1' 'ra' 'rg' 'rh' 'rp' 's0' 'sb' 'sn' 'st' 'td' 'uz' 'vg' 'w1' 'w6' 'wa' 'wc' 'we' 'wi' 'wj' 'wu' 'ww' 'y9' 'zr' - f | 'ak' 'bg' 'bj' 'bn' 'co' 'e6' 'e7' 'ec' 'ek' 'el' 'ew' 'ez' 'fo' 'im' 'jm' 'js' 'lc' 'lh' 'ob' 'oi' 'os' 'ot' 'ou' 'p0' 'pu' 'q0' 'qh' 'ra' 'rc' 'rm' 'rr' 'sd' 'sq' 'tc' 'tl' 'wj' 'wy' 'x2' 'xw' 'y6' 'yp' 'yv' 'zj' - f | 'ao' 'ei' 'ep' 'fc' 'fm' 'gr' 'ha' 'he' 'i5' 'ia' 'if' 'kh' 'lv' 'ml' 'ms' 'n9' 'nh' 'nl' 'oe' 'of' 'ow' 'pb' 'pu' 'pw' 'q0' 'qr' 'qt' 'qu' 'qv' 'qw' 'ra' 'sv' 'ti' 'u0' 'u4' 'ug' 'uq' 'vx' 'w1' 'wy' 'x1' 'xh' 'xz' - f | 'cr' 'd3' 'e5' 'ei' 'fb' 'gd' 'gx' 'hm' 'it' 'jp' 'ku' 'lf' 'lh' 'lp' 'ma' 'md' 'nt' 'o9' 'ot' 'pk' 'pm' 'ps' 'qc' 'qi' 'qp' 'qt' 'ra' 'rk' 'rq' 'rt' 'tb' 'tc' 'tg' 'to' 'u8' 'vg' 'vj' 'vt' 'w0' 'wr' 'wx' 'yd' 'zx' - f | '3p' '5x' 'ag' 'au' 'aw' 'dl' 'eg' 'f2' 'f8' 'fb' 'fn' 'g9' 'ge' 'gt' 'hd' 'i7' 'ia' 'ij' 'is' 'jc' 'ku' 'kx' 'l7' 'lb' 'lq' 'mc' 'o3' 'oj' 'q1' 'qa' 'qg' 'qt' 'r9' 'rg' 'tw' 'tz' 'u6' 'us' 'w2' 'wr' 'wt' 'wu' 'x3' 'xx' - f | 'aa' 'al' 'ay' 'c1' 'ca' 'de' 'ea' 'ee' 'eq' 'er' 'ez' 'fc' 'gy' 'i1' 'jb' 'jn' 'jz' 'lp' 'o5' 'om' 'os' 'ou' 'ox' 'px' 'q3' 'qc' 'qs' 'rm' 'rq' 'rt' 'rx' 'ry' 'rz' 'ss' 'ta' 'tg' 'ti' 'tr' 'u6' 'uv' 'vp' 'wn' 'wu' 'zt' - f | 'ak' 'bg' 'bk' 'du' 'eb' 'ef' 'el' 'ft' 'gb' 'gc' 'hc' 'ho' 'i6' 'iv' 'ji' 'ke' 'mo' 'oc' 'oh' 'pe' 'pj' 'q6' 'qe' 'qg' 'qs' 'qv' 'r1' 'rr' 'rx' 'tc' 'te' 'tf' 'tk' 'tq' 'ua' 'uq' 'ur' 'wq' 'wx' 'wz' 'xi' 'xq' 'yk' 'yo' - f | 'al' 'ar' 'cl' 'dl' 'dz' 'ej' 'fj' 'gu' 'i2' 'i6' 'ih' 'jx' 'k9' 'ln' 'ls' 'mf' 'mx' 'oi' 'om' 'pn' 'q4' 'qd' 'qe' 'qg' 'qk' 'qp' 'qr' 'qw' 'qx' 'rr' 'sd' 'sf' 'ub' 'uh' 'um' 'uu' 'vg' 'w6' 'wj' 'wp' 'ws' 'yn' 'zc' 'zm' - f | 'am' 'aw' 'ay' 'bj' 'df' 'eb' 'eo' 'eq' 'et' 'ey' 'f9' 'fo' 'g0' 'gg' 'h4' 'hq' 'in' 'k4' 'k7' 'kb' 'kl' 'my' 'nt' 'o3' 'og' 'pc' 'q4' 'qg' 'qi' 'qm' 'qy' 'ri' 'rv' 's9' 'sv' 'tl' 'ue' 'uo' 'v6' 'wb' 'wt' 'wx' 'xi' 'xu' - f | 'bs' 'cd' 'ci' 'cq' 'dd' 'el' 'ev' 'f4' 'f6' 'f7' 'fh' 'fu' 'hi' 'ib' 'kp' 'nn' 'nu' 'oc' 'os' 'qc' 'qj' 'qm' 'qn' 'qu' 'qw' 'qx' 'r7' 'rc' 'rv' 't1' 'tl' 'tr' 'u0' 'vi' 'wl' 'wp' 'wu' 'xc' 'y2' 'yu' 'zc' 'zi' 'zw' 'zy' - f | 'bt' 'c9' 'db' 'dy' 'ee' 'ej' 'eo' 'et' 'fr' 'gm' 'hz' 'ic' 'ik' 'is' 'jk' 'ls' 'lt' 'oj' 'or' 'ox' 'p8' 'pl' 'pz' 'q1' 'qq' 'qt' 'rw' 's7' 't4' 't8' 'to' 'tp' 'u7' 'ub' 'vx' 'w4' 'ws' 'wt' 'wv' 'x8' 'xs' 'xt' 'yg' 'yo' - f | 'cm' 'd2' 'en' 'ex' 'f4' 'f9' 'fp' 'ga' 'gk' 'hy' 'i3' 'j4' 'j9' 'jb' 'kj' 'ls' 'lw' 'mb' 'mq' 'o9' 'qg' 'qi' 'qm' 'qo' 'qw' 'qy' 'rc' 'rf' 'rj' 'rm' 's8' 'tb' 'tl' 'tp' 'um' 'vg' 'vn' 'wb' 'wf' 'wl' 'xw' 'yl' 'zf' 'zt' - f | '28' 'b1' 'bj' 'bm' 'cz' 'dd' 'ds' 'ed' 'em' 'hh' 'hr' 'ie' 'ii' 'im' 'io' 'jn' 'jw' 'lg' 'lo' 'm6' 'mn' 'mw' 'ny' 'o6' 'p7' 'q4' 'qd' 'qf' 'qn' 'qq' 'qt' 'r1' 'r2' 'rn' 'rt' 'sh' 'ur' 'uv' 'ux' 'vc' 'wb' 'wn' 'xh' 'xx' 'yw' - f | '2e' 'al' 'am' 'bt' 'eo' 'ff' 'fl' 'ge' 'gz' 'h0' 'hb' 'i0' 'j2' 'lh' 'lm' 'ls' 'mg' 'mw' 'on' 'ox' 'pa' 'pb' 'pg' 'q3' 'q6' 'qe' 'ra' 're' 'rm' 'rr' 'rt' 'ry' 'sc' 'sm' 't9' 'tu' 'tx' 'ul' 'ux' 'vh' 'wn' 'wt' 'wv' 'ys' 'yw' - f | '3d' 'at' 'aw' 'bt' 'by' 'cg' 'dk' 'do' 'ec' 'eu' 'gv' 'is' 'k9' 'kb' 'kc' 'me' 'mm' 'o5' 'oe' 'pr' 'qa' 'qn' 'rc' 'rl' 'rn' 'rw' 'sc' 'sk' 'st' 't1' 't7' 'td' 'to' 'ue' 'up' 'ur' 'uy' 've' 'wi' 'xf' 'xu' 'yf' 'z9' 'zo' 'zr' - f | '40' '6a' '8j' 'am' 'bf' 'dp' 'dz' 'ew' 'gh' 'he' 'hh' 'ib' 'ii' 'iq' 'is' 'iu' 'kt' 'mf' 'oh' 'or' 'p8' 'pa' 'pb' 'pi' 'qc' 'qo' 'qp' 'qq' 'qs' 'qt' 'qu' 'r4' 'rf' 'rl' 'sd' 'sl' 't4' 'tb' 'ua' 'ue' 'ut' 'wh' 'wm' 'y0' 'y6' - f | 'a0' 'a6' 'aa' 'bk' 'dk' 'dv' 'e8' 'ff' 'fo' 'go' 'hh' 'hl' 'hu' 'if' 'jx' 'kc' 'kq' 'lr' 'lx' 'n6' 'ni' 'ob' 'ol' 'ql' 'rf' 'ry' 'sm' 'sn' 't7' 'tu' 'tz' 'uc' 'um' 'uv' 'wf' 'wr' 'ws' 'wu' 'ww' 'xi' 'ym' 'z7' 'zo' 'zv' 'zz' - f | 'as' 'dj' 'dq' 'e3' 'ej' 'es' 'fn' 'fp' 'fu' 'ga' 'hb' 'hy' 'jz' 'l6' 'mp' 'ps' 'q4' 'qd' 'qm' 'qw' 'qy' 'qz' 'sa' 'sv' 'td' 'tn' 'tu' 'tx' 'ud' 'ue' 'uq' 'uv' 'v0' 'wi' 'wk' 'wm' 'wx' 'xh' 'y3' 'yd' 'yi' 'yn' 'yx' 'yy' 'zc' - f | 'cx' 'dg' 'do' 'dt' 'e7' 'ek' 'ez' 'fn' 'hj' 'hv' 'i2' 'i6' 'is' 'it' 'ka' 'kh' 'l5' 'lg' 'p5' 'qa' 'qf' 'qj' 'qw' 'qz' 'rl' 'sl' 'sr' 't7' 'tf' 'tu' 'uc' 'ud' 'uv' 'vr' 'w0' 'we' 'wf' 'wj' 'ws' 'ww' 'xe' 'xh' 'xn' 'y9' 'yv' - f | '2a' '4s' 'c4' 'cd' 'dh' 'dv' 'eh' 'ek' 'ew' 'ex' 'ez' 'fm' 'hh' 'hj' 'ig' 'jf' 'ld' 'ly' 'mg' 'mx' 'o5' 'or' 'pa' 'pv' 'q1' 'qe' 'qj' 'qq' 'qr' 'qw' 'rb' 'rr' 's4' 's9' 'tm' 'uj' 'wc' 'wv' 'x0' 'xt' 'yc' 'ye' 'yh' 'yi' 'yk' 'yy' - f | 'a2' 'a9' 'ac' 'bw' 'cu' 'dm' 'e8' 'ee' 'ej' 'ep' 'f7' 'fh' 'ga' 'go' 'im' 'it' 'jv' 'kg' 'lb' 'ml' 'oi' 'p5' 'p9' 'pe' 'pf' 'pp' 'qk' 'rg' 'rh' 'sn' 'sv' 't5' 'tj' 'tk' 'tq' 'ty' 'v6' 'vk' 'w2' 'w5' 'wh' 'wy' 'y7' 'yf' 'zq' 'zt' - f | 'aa' 'am' 'av' 'dt' 'du' 'eh' 'em' 'ev' 'ex' 'gu' 'h0' 'i4' 'i8' 'ku' 'la' 'lo' 'lw' 'n0' 'n9' 'on' 'pc' 'pl' 'pn' 'pr' 'q8' 'qc' 'ql' 'qn' 'qz' 'ra' 'rd' 'ry' 'sp' 'tn' 'ts' 'u4' 'vg' 'vs' 'w7' 'wj' 'wm' 'xl' 'yg' 'yh' 'z0' 'zs' - f | 'ak' 'ar' 'cm' 'cq' 'da' 'df' 'e3' 'ew' 'fe' 'fr' 'gm' 'id' 'io' 'iy' 'jg' 'jt' 'jx' 'kb' 'lf' 'nv' 'od' 'qy' 'r3' 'r4' 'ro' 'rq' 'rw' 'sl' 'ss' 'su' 'sw' 't7' 'th' 'ti' 'tp' 'u9' 'uq' 'v9' 'vq' 'vw' 'we' 'wg' 'wk' 'ws' 'xr' 'yf' - f | 'bv' 'cv' 'dy' 'ec' 'ef' 'eg' 'eh' 'eu' 'ew' 'fc' 'fn' 'fs' 'g9' 'hj' 'hk' 'i3' 'iu' 'ja' 'k6' 'l1' 'la' 'nl' 'oh' 'oi' 'oy' 'pc' 'qe' 'ql' 'qq' 'qs' 'qu' 're' 'sf' 'sq' 'tb' 'up' 'w1' 'w2' 'wg' 'wi' 'xd' 'xt' 'xv' 'yf' 'zn' 'zz' - f | '3y' 'ak' 'am' 'b8' 'cp' 'd7' 'df' 'di' 'eg' 'em' 'en' 'eo' 'es' 'f6' 'fc' 'gg' 'gt' 'gv' 'hd' 'i3' 'jk' 'k6' 'la' 'lc' 'ol' 'ov' 'ow' 'p5' 'pl' 'qc' 'qf' 'rl' 'rn' 'rt' 'rx' 't1' 'tf' 'tj' 'tz' 'ug' 'uo' 'we' 'wg' 'wk' 'wp' 'wv' 'y7' - f | '5g' 'a5' 'dj' 'dl' 'dr' 'e9' 'ed' 'ep' 'er' 'gb' 'hh' 'hl' 'i0' 'if' 'ig' 'io' 'it' 'k2' 'ki' 'll' 'o9' 'om' 'ot' 'p5' 'q1' 'q6' 'qa' 'qp' 'qw' 'qy' 'r9' 'rb' 'rm' 'ry' 's7' 'tl' 'ts' 'ut' 'w0' 'wd' 'wu' 'wv' 'xk' 'ya' 'z5' 'zq' 'zs' - f | '5l' 'a2' 'ak' 'au' 'cj' 'cx' 'ed' 'eg' 'ej' 'em' 'es' 'ex' 'fs' 'ft' 'hm' 'i9' 'ky' 'l3' 'lz' 'mi' 'o2' 'oc' 'oe' 'ok' 'p2' 'q1' 'q5' 'qo' 'qv' 'qw' 'rf' 'rm' 'rv' 'sc' 'si' 'tx' 'uq' 'v3' 'v4' 'vo' 'w4' 'wf' 'wl' 'wq' 'wt' 'xj' 'yr' - f | 'al' 'ar' 'av' 'bg' 'bh' 'bz' 'e9' 'ed' 'ee' 'ef' 'eh' 'es' 'fl' 'fz' 'gl' 'h1' 'he' 'hf' 'hv' 'hw' 'i1' 'jg' 'kj' 'kn' 'kz' 'lr' 'pp' 'q1' 'qa' 'qf' 'qt' 'qz' 'rg' 'rj' 's5' 'sv' 'sx' 't4' 'th' 'tn' 'ur' 'wz' 'xp' 'yg' 'ym' 'yr' 'ze' - f | 'az' 'cx' 'dm' 'do' 'dq' 'e2' 'e5' 'e8' 'eb' 'fh' 'fj' 'fq' 'gj' 'gl' 'hq' 'im' 'iq' 'iz' 'jn' 'ku' 'l1' 'mz' 'nt' 'o6' 'q1' 'qn' 'qo' 'qz' 'r4' 'rh' 'rn' 'rw' 'sn' 'sv' 't7' 'ud' 'ue' 'ur' 'uv' 'w9' 'wh' 'wk' 'wy' 'xq' 'yh' 'zs' 'zw' - f | '18' 'bf' 'bj' 'cs' 'dw' 'el' 'ep' 'ez' 'fr' 'g1' 'gv' 'hu' 'hz' 'ii' 'jc' 'je' 'jj' 'jo' 'jv' 'n5' 'ni' 'ns' 'oh' 'oi' 'ov' 'pe' 'q8' 'qg' 'qi' 'qr' 'r6' 'rh' 's3' 'sj' 'sr' 't0' 'ta' 'u2' 'uj' 'ur' 'vu' 'wb' 'wh' 'wx' 'yn' 'yq' 'zg' 'zr' - f | '2s' 'a5' 'aw' 'b5' 'b7' 'ce' 'cq' 'dc' 'dp' 'eb' 'eq' 'ez' 'ff' 'gg' 'gy' 'iw' 'jd' 'jl' 'kw' 'ky' 'mt' 'ng' 'o9' 'od' 'oe' 'po' 'pp' 'qa' 'qe' 'qk' 'ql' 'qp' 'r4' 'r9' 'rb' 'si' 'sl' 'sn' 'sq' 'tg' 'ti' 'tx' 'uh' 'ws' 'xf' 'yv' 'z1' 'zm' - f | '4j' 'bi' 'bk' 'c0' 'c5' 'dh' 'dy' 'e8' 'fw' 'g3' 'g8' 'h6' 'hd' 'hn' 'jl' 'kc' 'kj' 'km' 'll' 'lo' 'nh' 'nj' 'nt' 'ob' 'on' 'oq' 'or' 'ox' 'ph' 'qc' 'qj' 'ql' 'qm' 'qo' 'qu' 'qz' 'sr' 't7' 'tc' 'ug' 'uq' 'wn' 'wr' 'ws' 'yv' 'z3' 'zj' 'zz' - f | '6w' 'af' 'ak' 'b7' 'bf' 'bi' 'bv' 'cx' 'dy' 'es' 'ey' 'ez' 'f0' 'fe' 'fw' 'ge' 'gt' 'iw' 'j9' 'jh' 'kh' 'l0' 'l1' 'l8' 'm5' 'nv' 'o6' 'pw' 'qi' 'r0' 'se' 'ta' 'tf' 'th' 'to' 'u8' 'ux' 'vm' 'w4' 'w6' 'wa' 'wt' 'wu' 'xo' 'xu' 'yo' 'z0' 'zc' - f | 'au' 'bl' 'c4' 'd7' 'dc' 'dm' 'ea' 'eo' 'ft' 'gb' 'h3' 'ia' 'ix' 'jr' 'kc' 'ke' 'lq' 'lr' 'mn' 'op' 'pj' 'qc' 'qj' 'qn' 'qq' 'qx' 'r8' 'rb' 'ri' 'rm' 'rn' 'rx' 't6' 'tf' 'tv' 'um' 'vg' 'vi' 'we' 'wp' 'wz' 'x6' 'xb' 'xd' 'xg' 'xv' 'yx' 'yz' - f | '7r' 'a8' 'aa' 'ag' 'bo' 'bp' 'c7' 'd0' 'd6' 'e5' 'er' 'fc' 'ff' 'go' 'ha' 'hs' 'jj' 'jq' 'ki' 'lc' 'le' 'no' 'pf' 'qc' 'qh' 'qq' 'qs' 'qv' 'rf' 'rj' 'rx' 'su' 'ta' 'ti' 'tu' 'tv' 'ty' 'ug' 'un' 'up' 'vb' 'vi' 'wp' 'wu' 'wz' 'xt' 'y0' 'yd' 'yu' - f | 'at' 'b7' 'be' 'c3' 'cc' 'cd' 'cj' 'e8' 'eb' 'ei' 'fd' 'g8' 'gh' 'go' 'gv' 'if' 'ih' 'j7' 'jy' 'kj' 'lh' 'mc' 'oe' 'og' 'oy' 'oz' 'pm' 'qd' 'qe' 'qo' 'rg' 'rq' 'rx' 'se' 'su' 't3' 't6' 't9' 'u5' 'us' 'vw' 'w4' 'wb' 'wg' 'ws' 'xw' 'yu' 'yx' 'zj' - f | '18' 'a6' 'af' 'ax' 'du' 'dy' 'e7' 'ei' 'ej' 'es' 'eu' 'fj' 'gt' 'hn' 'hu' 'ky' 'le' 'lo' 'm8' 'nb' 'of' 'oj' 'ok' 'op' 'oz' 'pw' 'q7' 'qi' 'qu' 'r4' 'rp' 's7' 'sk' 'tb' 'tf' 'tp' 'ua' 'um' 'up' 'ur' 'uz' 'w4' 'wc' 'wn' 'wv' 'wx' 'wy' 'yc' 'yq' 'yx' - f | '82' 'a2' 'ad' 'dp' 'el' 'ep' 'fk' 'gd' 'hj' 'ij' 'lc' 'lm' 'lv' 'ml' 'n2' 'n6' 'o1' 'oi' 'on' 'oo' 'ow' 'pb' 'pe' 'pq' 'q6' 'qi' 'qo' 'qp' 'qw' 'rs' 'rv' 's6' 'sa' 'sk' 't8' 'tl' 'tt' 'tx' 'ub' 'ue' 'uf' 'uj' 'va' 'wl' 'xv' 'y1' 'ye' 'ym' 'yw' 'zm' - f | 'a3' 'b9' 'c0' 'cg' 'ct' 'cu' 'dy' 'eh' 'el' 'ge' 'gt' 'hh' 'hu' 'if' 'j5' 'kq' 'lb' 'mh' 'o3' 'o6' 'oe' 'oi' 'ow' 'p5' 'ph' 'pw' 'q0' 'qh' 'ql' 'qy' 'r7' 'si' 'sk' 'sv' 'tf' 'tm' 'to' 'tt' 'ty' 'u3' 'uj' 'uo' 'vi' 'vl' 'wc' 'wf' 'xx' 'y0' 'ya' 'yu' - f | 'ad' 'am' 'av' 'ax' 'dl' 'dt' 'dw' 'ej' 'ez' 'f6' 'fe' 'g5' 'ga' 'gv' 'hr' 'hx' 'id' 'ie' 'ii' 'im' 'ix' 'jd' 'kn' 'ml' 'nu' 'ol' 'oo' 'pu' 'qd' 'qm' 'qt' 'r5' 'rb' 'rd' 'rg' 'ru' 'sg' 'ub' 'uk' 'vm' 'wl' 'wr' 'wz' 'x4' 'yf' 'yu' 'yv' 'yw' 'yx' 'zt' - f | 'bw' 'ci' 'cp' 'cu' 'cw' 'd7' 'e0' 'e3' 'em' 'ev' 'fi' 'fk' 'fm' 'fu' 'ht' 'ix' 'jn' 'kc' 'kw' 'lm' 'mo' 'n8' 'od' 'oo' 'q1' 'qd' 'qw' 'rs' 'sd' 'su' 'sz' 'th' 'ti' 'ts' 'tw' 'ty' 'u3' 'ua' 'wa' 'wt' 'wy' 'xg' 'xh' 'xp' 'xr' 'yd' 'yj' 'yq' 'yx' 'zu' - f | '1d' 'a4' 'ai' 'av' 'b9' 'be' 'c9' 'cc' 'cy' 'de' 'ee' 'g8' 'gg' 'gl' 'gs' 'gu' 'hb' 'kl' 'kr' 'ky' 'mi' 'mz' 'nd' 'og' 'op' 'pr' 'q0' 'q5' 'qk' 'qy' 'r4' 'sk' 'sz' 't1' 't8' 'td' 'tn' 'tx' 'ty' 'tz' 'uc' 'up' 'vq' 'wa' 'ws' 'wv' 'x3' 'yn' 'yq' 'yy' 'zm' - f | '1u' 'ad' 'al' 'c4' 'cz' 'db' 'dk' 'dr' 'e5' 'e7' 'ea' 'f6' 'fk' 'fw' 'g3' 'gl' 'gt' 'gu' 'ha' 'hk' 'hv' 'ie' 'ka' 'kq' 'ks' 'lr' 'nf' 'pk' 'q3' 'q8' 'qf' 'qj' 'qo' 'rp' 'rr' 'rz' 'ss' 'sv' 'sx' 'tb' 'th' 'tt' 'ur' 'vp' 'wj' 'ws' 'wt' 'xk' 'xo' 'ym' 'yz' - f | '3f' '93' 'am' 'an' 'at' 'bj' 'bm' 'bw' 'c6' 'eb' 'ef' 'em' 'eq' 'es' 'g9' 'hc' 'hl' 'hm' 'kg' 'kt' 'mg' 'nq' 'op' 'ox' 'pc' 'py' 'qd' 'qn' 'qz' 'rb' 'rw' 's6' 'se' 'sr' 'sy' 'ts' 'u4' 'ud' 'un' 'w3' 'w8' 'wb' 'wf' 'wg' 'wy' 'x1' 'xl' 'xy' 'yk' 'yo' 'zp' - f | 'a6' 'ad' 'aj' 'b1' 'bz' 'cd' 'df' 'ed' 'eg' 'eh' 'ev' 'fi' 'fo' 'gj' 'h3' 'he' 'ib' 'j0' 'jc' 'jz' 'kz' 'lc' 'mh' 'pk' 'pu' 'q4' 'qc' 'qk' 'qr' 'qw' 'rc' 'rl' 'ro' 'rr' 'so' 'sw' 'td' 'tw' 'tx' 'ue' 'us' 'ut' 'vd' 'wi' 'wu' 'xm' 'y2' 'y8' 'ym' 'z6' 'zm' - f | '2e' '3y' 'ap' 'as' 'bl' 'di' 'dl' 'e9' 'eh' 'er' 'ff' 'fg' 'fh' 'gc' 'gg' 'hp' 'hq' 'i3' 'ih' 'jm' 'kw' 'lc' 'li' 'ls' 'nm' 'ok' 'pc' 'pn' 'q1' 'qf' 'qi' 'qk' 'r0' 'rl' 'sp' 'sy' 'sz' 'ta' 'to' 'ts' 'tv' 'uh' 'uk' 'vb' 'wa' 'wg' 'wi' 'wp' 'yl' 'yq' 'yw' 'yy' - f | '2h' '9y' 'ac' 'at' 'cf' 'e3' 'e6' 'ec' 'ek' 'en' 'f1' 'fa' 'fn' 'gm' 'gx' 'hk' 'i7' 'id' 'iw' 'ke' 'kl' 'ks' 'l6' 'l8' 'ls' 'lx' 'me' 'o4' 'oi' 'oj' 'ox' 'pn' 'qa' 'qh' 'qk' 'ql' 'rp' 'tb' 'te' 'tn' 'ts' 'u4' 'u9' 'ud' 'vj' 'vz' 'wc' 'wr' 'ye' 'yk' 'yq' 'zq' - f | '4j' 'af' 'ap' 'bn' 'ch' 'dc' 'df' 'e0' 'ef' 'eg' 'er' 'g3' 'hh' 'hi' 'hw' 'i1' 'if' 'ii' 'in' 'iy' 'l1' 'mx' 'og' 'px' 'q3' 'qc' 'qg' 'qt' 'qy' 'r3' 'r6' 'rd' 'rj' 'rz' 'sm' 't2' 't4' 'td' 'tj' 'tm' 'tx' 'ty' 'u9' 'uo' 'up' 'v8' 'wv' 'ye' 'zb' 'zc' 'zi' 'zy' - f | 'a0' 'ai' 'bx' 'ca' 'e2' 'eb' 'ed' 'eg' 'eh' 'eo' 'et' 'hn' 'ix' 'jh' 'ki' 'lm' 'lw' 'm8' 'mb' 'mh' 'mk' 'nc' 'o3' 'o9' 'of' 'qc' 'qe' 'qf' 'qh' 'qi' 'qq' 'qr' 'qz' 'r2' 'r3' 'rc' 'rh' 'rs' 'rv' 'sr' 'uk' 'up' 'ur' 'uv' 'wm' 'wr' 'wz' 'xd' 'y3' 'ya' 'yv' 'zr' - f | 'ab' 'ae' 'al' 'am' 'an' 'ap' 'be' 'd2' 'eb' 'ec' 'ep' 'eq' 'fn' 'hp' 'hx' 'i9' 'ie' 'jn' 'kh' 'kv' 'mi' 'mq' 'mv' 'ox' 'oz' 'pn' 'qk' 'qo' 'qr' 'r8' 're' 'rf' 'rp' 't8' 'uj' 'uk' 'up' 'ur' 'vo' 'w7' 'wf' 'wp' 'ws' 'wt' 'ww' 'wx' 'xw' 'yd' 'yj' 'yn' 'yv' 'zr' - f | 'ax' 'ch' 'de' 'dl' 'e1' 'el' 'fl' 'fo' 'fp' 'fx' 'gb' 'gj' 'gx' 'hg' 'ho' 'hs' 'ip' 'jr' 'kg' 'l6' 'li' 'ln' 'mg' 'mm' 'mo' 'o2' 'os' 'pm' 'q2' 'q6' 'qr' 're' 'ro' 'rr' 'rt' 'rw' 'rx' 'sk' 'tl' 'to' 'tr' 'tt' 'u3' 'u6' 'vo' 'w4' 'w5' 'wb' 'wo' 'wt' 'yi' 'zh' - f | 'bl' 'bn' 'cy' 'ea' 'ed' 'ef' 'ei' 'em' 'er' 'es' 'fp' 'gb' 'hi' 'hp' 'if' 'io' 'ir' 'kw' 'lg' 'lo' 'ls' 'n8' 'nj' 'np' 'oa' 'of' 'oi' 'p6' 'pw' 'q3' 'q4' 'q8' 'qf' 'qh' 'qo' 'qw' 'rg' 'rt' 'sy' 't3' 'u8' 'ug' 'uo' 'uv' 'w6' 'we' 'wg' 'wl' 'wy' 'x9' 'yy' 'yz' - f | '16' '64' 'aj' 'aw' 'bf' 'c0' 'ca' 'd2' 'd6' 'ec' 'ed' 'eq' 'ew' 'go' 'h2' 'in' 'is' 'j0' 'jd' 'ji' 'kv' 'l5' 'lp' 'lz' 'og' 'pb' 'pw' 'q9' 'qb' 'qe' 'qf' 'qg' 'qn' 'qp' 'qt' 'qy' 'r7' 'rh' 'rw' 'sz' 't8' 'tb' 'te' 'tx' 'ui' 'un' 'uq' 'wa' 'wp' 'wu' 'wy' 'yn' 'ys' - f | '1m' '1s' 'aa' 'b0' 'be' 'cb' 'dc' 'dd' 'dh' 'eq' 'fa' 'ib' 'if' 'ik' 'it' 'jr' 'jz' 'ka' 'lq' 'lu' 'm2' 'o2' 'ob' 'oc' 'of' 'om' 'oz' 'pv' 'q4' 'qb' 'qk' 'qn' 'qs' 'qu' 'r3' 'r9' 'rs' 'rw' 'sg' 'si' 'sv' 't9' 'tw' 'vq' 'vy' 'w2' 'w6' 'w9' 'y0' 'y1' 'ye' 'yq' 'z1' - f | '1o' 'a0' 'aa' 'bd' 'bj' 'ch' 'cm' 'dj' 'e2' 'eq' 'eu' 'fj' 'fo' 'g9' 'go' 'hi' 'ia' 'ix' 'jh' 'jl' 'jy' 'lh' 'nd' 'nw' 'ox' 'p3' 'pi' 'pm' 'pt' 'q3' 'qh' 'qn' 'ra' 'ri' 'rr' 'ru' 'rv' 't3' 'tm' 'u3' 'ue' 'us' 'uz' 'vn' 'w1' 'wl' 'wo' 'wx' 'xj' 'xn' 'yc' 'yp' 'zr' - f | '3b' 'am' 'aq' 'ar' 'bm' 'de' 'e7' 'ed' 'eh' 'es' 'ey' 'fx' 'g5' 'gf' 'gs' 'hl' 'i7' 'iy' 'jn' 'k9' 'kf' 'km' 'll' 'ly' 'm0' 'm5' 'mh' 'nm' 'nq' 'of' 'or' 'p5' 'pn' 'pz' 'qp' 'qt' 'qv' 'r0' 'rb' 'sg' 'sv' 'uv' 'w3' 'w8' 'w9' 'wa' 'we' 'wj' 'wn' 'wr' 'yd' 'yk' 'zq' - f | '4t' 'a3' 'ac' 'au' 'bp' 'br' 'e5' 'ei' 'ek' 'ez' 'fz' 'g5' 'gn' 'ik' 'k9' 'kb' 'kc' 'lm' 'ls' 'ly' 'mj' 'mx' 'nk' 'nr' 'ny' 'o2' 'o8' 'pb' 'pu' 'q2' 'qa' 'qc' 'qh' 'qi' 'qs' 'qx' 'qz' 'rv' 'rx' 'tc' 'tk' 'tv' 'u8' 'ui' 'uz' 'vo' 'w2' 'wm' 'wp' 'xt' 'yf' 'yv' 'zl' - f | 'aa' 'ab' 'ag' 'ar' 'az' 'bp' 'bx' 'cx' 'eb' 'eh' 'ek' 'eo' 'eq' 'ez' 'fl' 'gm' 'gw' 'hd' 'ib' 'ig' 'jz' 'kg' 'lf' 'nw' 'oh' 'ok' 'om' 'oy' 'pg' 'ph' 'pi' 'qb' 'qi' 'qn' 'qs' 'qt' 'qy' 'rl' 'sy' 'tc' 'tt' 'up' 'ur' 'uy' 'w6' 'w8' 'wb' 'wh' 'wv' 'ww' 'y5' 'yi' 'yp' - f | 'aa' 'af' 'ah' 'am' 'as' 'cd' 'ch' 'dd' 'dg' 'dr' 'e2' 'eb' 'eq' 'er' 'fb' 'fj' 'fk' 'fn' 'fx' 'gk' 'gs' 'h8' 'hg' 'hj' 'ia' 'ij' 'in' 'jw' 'l9' 'lj' 'lw' 'nk' 'nt' 'o1' 'ob' 'oo' 'p5' 'pd' 'pk' 'qq' 'sb' 'sd' 'sg' 'u4' 'wl' 'wq' 'wr' 'wu' 'xc' 'xj' 'xt' 'yq' 'zl' - f | 'ad' 'ao' 'au' 'az' 'ci' 'dj' 'dv' 'e1' 'ei' 'ej' 'em' 'eq' 'fa' 'fi' 'fj' 'fn' 'fw' 'he' 'i8' 'in' 'io' 'jd' 'jt' 'jv' 'lr' 'mt' 'mx' 'ns' 'nt' 'oc' 'oe' 'oi' 'pz' 'qe' 'ql' 'qt' 'qy' 'rq' 'sc' 'sp' 'te' 'um' 'wo' 'wv' 'x6' 'xu' 'yf' 'yi' 'yo' 'yv' 'yw' 'yz' 'zg' - f | 'ag' 'ay' 'b6' 'bk' 'c6' 'cp' 'da' 'ea' 'eh' 'es' 'f1' 'fj' 'fv' 'gb' 'gn' 'ic' 'id' 'in' 'iq' 'iw' 'kx' 'ly' 'n7' 'o5' 'oc' 'og' 'op' 'os' 'pa' 'pr' 'q4' 'q6' 'qf' 'qg' 'qh' 'qp' 'qq' 'qu' 'qw' 'qy' 'qz' 'rj' 'rm' 'ro' 's1' 'w2' 'we' 'wl' 'wn' 'wp' 'yj' 'yp' 'yx' - f | 'ah' 'bg' 'bw' 'ca' 'cn' 'cy' 'da' 'dq' 'dt' 'eq' 'ex' 'fe' 'ff' 'ga' 'gr' 'kv' 'ld' 'my' 'od' 'oq' 'p3' 'p5' 'pc' 'pf' 'q5' 'qa' 'qc' 'qi' 'ql' 'qr' 'qx' 'qz' 'rd' 'rk' 'rq' 'ru' 'rw' 's7' 'sa' 'sh' 'tk' 'um' 'un' 'v4' 'vw' 'wg' 'wr' 'wt' 'xt' 'ya' 'yo' 'z6' 'zm' - f | '2r' '7g' 'a1' 'ar' 'av' 'b8' 'dj' 'dr' 'e7' 'ee' 'fd' 'fn' 'fp' 'gf' 'gp' 'gq' 'gw' 'jm' 'jv' 'ke' 'ki' 'kl' 'l9' 'lc' 'lg' 'lh' 'nm' 'oc' 'oq' 'px' 'q6' 'qe' 'qh' 'qu' 'rs' 's8' 't0' 'tt' 'tw' 'u2' 'uu' 'uy' 'vr' 'vs' 'wg' 'wt' 'ww' 'x3' 'x5' 'xu' 'yw' 'zh' 'zp' 'zt' - f | '4u' 'a2' 'aq' 'as' 'b8' 'bu' 'cc' 'cp' 'cv' 'dg' 'e3' 'e9' 'eh' 'fg' 'fh' 'g9' 'gf' 'gk' 'gx' 'hp' 'il' 'jq' 'kc' 'l4' 'lm' 'mt' 'my' 'nw' 'oq' 'pe' 'qe' 'qu' 'qz' 'r1' 'rb' 'sh' 'sx' 't5' 'td' 'tl' 'tr' 'vb' 'vd' 'w6' 'wh' 'wk' 'wp' 'wv' 'y0' 'ye' 'yq' 'z2' 'zj' 'zz' - f | 'a1' 'a5' 'aw' 'b5' 'cd' 'dz' 'em' 'eq' 'eu' 'fx' 'gk' 'hi' 'hq' 'ju' 'k8' 'kl' 'kw' 'la' 'lk' 'lm' 'm1' 'mj' 'mp' 'nz' 'o6' 'o8' 'oc' 'q0' 'q1' 'qg' 'qh' 'qw' 'rc' 'rd' 'ro' 'rr' 'sg' 'sy' 'th' 'ti' 'tt' 'uk' 'uo' 'ut' 'vy' 'wg' 'wi' 'x7' 'yr' 'yu' 'yx' 'yz' 'zr' 'zz' - f | 'a6' 'au' 'aw' 'bj' 'bn' 'cy' 'dg' 'e3' 'ei' 'ek' 'em' 'et' 'ex' 'fi' 'fm' 'g4' 'gm' 'gz' 'hp' 'ia' 'ie' 'jd' 'lg' 'lj' 'm6' 'mi' 'nd' 'oj' 'oo' 'ov' 'px' 'q0' 'q4' 'qj' 'qp' 'qq' 'r1' 'r5' 'rh' 'ro' 'tg' 'tr' 'tu' 'ty' 'u2' 'uk' 'ux' 'we' 'wi' 'wk' 'wq' 'wu' 'xy' 'y0' - f | 'a9' 'aa' 'aq' 'ay' 'bb' 'cg' 'db' 'de' 'do' 'e4' 'e5' 'e9' 'ek' 'ep' 'er' 'fi' 'fp' 'gb' 'hj' 'hk' 'hw' 'ir' 'iy' 'l1' 'lj' 'mb' 'nq' 'oi' 'pj' 'px' 'q9' 'qe' 'qu' 'qw' 'qz' 'rh' 'rl' 'sv' 'tc' 'ts' 'tv' 'uo' 'ur' 'vh' 'vp' 'w7' 'wf' 'wh' 'wo' 'wr' 'xu' 'z4' 'zc' 'zv' - f | 'au' 'cm' 'dj' 'e1' 'eh' 'ey' 'f3' 'fd' 'fg' 'fv' 'hn' 'i0' 'ia' 'jt' 'jy' 'k2' 'll' 'ne' 'o0' 'o2' 'op' 'pa' 'pf' 'qq' 'qx' 'rr' 'rs' 's4' 'sn' 'so' 'sq' 'tc' 'tn' 'ts' 'ty' 'tz' 'un' 'va' 'vg' 'vj' 'w8' 'wa' 'wb' 'wk' 'wo' 'wp' 'wr' 'x3' 'xx' 'yj' 'yz' 'z3' 'z7' 'zn' - f | '1f' '2u' 'ap' 'as' 'ax' 'bb' 'bf' 'cg' 'di' 'du' 'g3' 'g7' 'gk' 'ha' 'hv' 'hw' 'i6' 'jw' 'kg' 'ko' 'lu' 'ob' 'pn' 'qb' 'qg' 'qi' 'qs' 'qw' 'r3' 'r7' 'r9' 'rc' 're' 'rf' 'rn' 'ru' 'rz' 'sh' 'sq' 't7' 'ta' 'td' 'u2' 'ua' 'uy' 'v8' 'va' 'wk' 'wn' 'wu' 'wy' 'xr' 'yi' 'yt' 'za' - f | '1p' 'a7' 'ae' 'b2' 'dd' 'ef' 'eh' 'el' 'em' 'eo' 'fc' 'g0' 'hf' 'hh' 'i8' 'i9' 'in' 'is' 'iy' 'j3' 'ja' 'jl' 'js' 'jt' 'kz' 'lk' 'ng' 'nu' 'o6' 'oc' 'oe' 'om' 'ox' 'pi' 'pp' 'pq' 'ps' 'qc' 'qe' 'qy' 'rh' 'rk' 'rn' 's3' 'sc' 'so' 'st' 'tc' 'tm' 'tp' 'tq' 'vh' 'wa' 'ya' 'zq' - f | '1w' '2r' '9e' '9r' 'ci' 'es' 'f1' 'f4' 'fb' 'fx' 'g6' 'ga' 'gk' 'hj' 'hm' 'hu' 'i5' 'ii' 'jg' 'ji' 'ko' 'ku' 'l2' 'ld' 'lx' 'mt' 'n1' 'no' 'nw' 'o7' 'oi' 'pk' 'qy' 'r9' 'rj' 'rr' 'rt' 's6' 'sd' 't0' 'tc' 'to' 'tp' 'tv' 'ud' 'ux' 'vk' 'wr' 'wy' 'xb' 'xr' 'xt' 'yc' 'yp' 'zy' - f | '2k' '4s' 'd2' 'dk' 'dm' 'ea' 'ej' 'ep' 'et' 'eu' 'fr' 'gk' 'gs' 'hm' 'iu' 'jq' 'kj' 'km' 'lm' 'nb' 'nr' 'o1' 'oc' 'od' 'of' 'oj' 'ox' 'pf' 'pj' 'px' 'q0' 'q2' 'qa' 'qe' 'qh' 'qv' 'r5' 'r9' 'rb' 'rh' 'ti' 'tl' 'tw' 'u2' 'vb' 'vc' 'vs' 'xe' 'xn' 'y7' 'yb' 'yg' 'yy' 'zb' 'zl' - f | '48' 'ae' 'au' 'az' 'b2' 'di' 'ep' 'eq' 'ev' 'f7' 'fw' 'gh' 'gj' 'gn' 'hz' 'i5' 'iy' 'jf' 'kg' 'kj' 'kk' 'nc' 'nm' 'o4' 'oc' 'oe' 'of' 'oh' 'ol' 'ov' 'oz' 'pt' 'pv' 'q5' 'q7' 'qb' 'qe' 'qf' 'rh' 'ry' 'se' 'so' 'tv' 'ua' 'vd' 'wb' 'wg' 'wi' 'wp' 'xf' 'xo' 'yl' 'yr' 'yy' 'z0' - f | '70' 'ai' 'bj' 'bm' 'cj' 'ct' 'eb' 'ef' 'ek' 'es' 'fg' 'gh' 'gu' 'hg' 'ia' 'iu' 'iw' 'jt' 'ko' 'kq' 'mb' 'n1' 'o1' 'ox' 'pa' 'pr' 'q4' 'qd' 'qo' 'qr' 'qt' 'qw' 'qz' 'r3' 'r5' 'rg' 'ri' 'rj' 'rn' 'rp' 's7' 'sn' 't9' 'tq' 'tx' 'ty' 'uj' 'v0' 'vt' 'vz' 'w1' 'ww' 'xb' 'z6' 'zi' - f | 'a1' 'aa' 'c1' 'd7' 'dc' 'df' 'dh' 'e4' 'e9' 'ec' 'es' 'et' 'eu' 'ev' 'f3' 'g2' 'gu' 'he' 'il' 'j1' 'j6' 'jt' 'jv' 'ke' 'm8' 'm9' 'mh' 'ng' 'o8' 'on' 'pe' 'pf' 'pi' 'pn' 'qf' 'qn' 'qu' 'qv' 'qw' 'rh' 'rl' 'rr' 'ru' 'sg' 'sk' 'uk' 'ul' 'ux' 'vd' 'vj' 'wk' 'yd' 'yn' 'yx' 'z0' - f | 'af' 'bq' 'c9' 'cg' 'eg' 'ei' 'eq' 'ez' 'fo' 'fp' 'gl' 'i6' 'it' 'iz' 'jg' 'ka' 'lf' 'lm' 'lr' 'mv' 'oc' 'oj' 'om' 'or' 'p1' 'p4' 'pt' 'q3' 'qd' 'qe' 'qh' 'qx' 'ra' 'rd' 'sr' 'sz' 'tb' 'tc' 'tj' 'ug' 'uh' 'ux' 'wb' 'wc' 'wj' 'wy' 'xa' 'xb' 'y0' 'yj' 'yp' 'yz' 'zf' 'zl' 'zq' - f | '1a' '4p' 'bn' 'br' 'dk' 'ec' 'en' 'eu' 'gd' 'h0' 'ha' 'hm' 'if' 'il' 'io' 'ip' 'is' 'iz' 'jl' 'jq' 'k6' 'kd' 'kf' 'kn' 'kp' 'ld' 'lf' 'lt' 'n8' 'na' 'nl' 'o1' 'pd' 'pr' 'q8' 'qf' 'qr' 'qw' 're' 'rx' 'sa' 'sf' 'ta' 'tq' 'ux' 'v3' 'w6' 'wp' 'x2' 'y9' 'yi' 'yw' 'yx' 'z5' 'zl' 'zs' - f | '1y' '5p' '6o' 'au' 'b2' 'bg' 'do' 'dw' 'e1' 'e9' 'ea' 'ei' 'em' 'ev' 'f0' 'gc' 'hj' 'hx' 'k7' 'km' 'kp' 'kx' 'mj' 'mu' 'o3' 'oz' 'pf' 'q5' 'qe' 'qg' 'qh' 'qk' 'ql' 'qn' 'qo' 'rh' 'rk' 'ru' 'rx' 't1' 't3' 'th' 'v2' 'vx' 'w2' 'wc' 'wl' 'wm' 'wn' 'ws' 'x5' 'ye' 'yh' 'yk' 'yu' 'zw' - f | '2b' '3f' '5e' '7o' 'a4' 'ai' 'as' 'bc' 'bn' 'cz' 'd8' 'dg' 'dj' 'du' 'e5' 'el' 'et' 'ex' 'gc' 'gj' 'hi' 'hn' 'ig' 'ij' 'j3' 'js' 'l3' 'l6' 'lh' 'ls' 'mb' 'n1' 'o0' 'ot' 'q5' 'qd' 'qh' 'qv' 'qz' 'ra' 'rj' 's8' 'tb' 'tf' 'th' 'tx' 'ty' 'vm' 'wf' 'wg' 'wh' 'wn' 'y1' 'y5' 'yw' 'yz' - f | '2j' 'ai' 'al' 'ch' 'dy' 'dz' 'ed' 'ei' 'fg' 'fk' 'gp' 'i0' 'il' 'ip' 'iv' 'ix' 'js' 'lm' 'na' 'p1' 'pq' 'pu' 'qe' 'qi' 'qk' 'ql' 'qq' 'qv' 'qx' 'r0' 'rl' 'rm' 'rv' 'sf' 'sj' 'te' 'tk' 'tq' 'tt' 'tu' 'uc' 'ud' 'um' 'un' 'uq' 'vg' 'vu' 'vz' 'w8' 'wa' 'wf' 'xg' 'yj' 'yw' 'yy' 'z6' - f | '4c' '4z' 'ag' 'ak' 'an' 'as' 'aw' 'bl' 'd7' 'dy' 'e7' 'eb' 'ec' 'eq' 'et' 'fx' 'gg' 'gm' 'ht' 'hv' 'ik' 'in' 'ip' 'j6' 'k6' 'ka' 'lc' 'md' 'mx' 'n7' 'pw' 'py' 'q7' 'qa' 'qg' 'qh' 'qn' 'qo' 'qt' 'ri' 'rl' 'rs' 'sj' 'su' 'tj' 'u0' 'ua' 'w0' 'wh' 'wi' 'wp' 'wr' 'wx' 'wz' 'ys' 'yx' - f | '1y' '4f' 'ao' 'bv' 'co' 'cq' 'dd' 'df' 'dy' 'eq' 'eu' 'ex' 'gg' 'gm' 'gr' 'hm' 'iw' 'j9' 'jb' 'jg' 'jo' 'ju' 'k0' 'km' 'lf' 'ng' 'np' 'nw' 'nz' 'od' 'oj' 'or' 'pp' 'pr' 'pu' 'q5' 'q7' 'q9' 'qc' 'qd' 'qn' 'qx' 'r9' 'rd' 'rk' 'ro' 'sb' 'ta' 'th' 'tv' 'ty' 'tz' 'uq' 'vb' 'wb' 'wj' 'xo' - f | '4m' 'at' 'be' 'bo' 'bz' 'cv' 'd0' 'd8' 'ea' 'ec' 'ed' 'ex' 'f9' 'ff' 'fk' 'fv' 'gc' 'ge' 'i8' 'ij' 'is' 'k4' 'k6' 'mw' 'ob' 'ov' 'oy' 'pv' 'q1' 'q2' 'q9' 'qc' 'ql' 'r4' 'rn' 'ru' 'rx' 'ry' 'sj' 'tg' 'ts' 'tv' 'uj' 'vf' 'vh' 'w4' 'wc' 'ww' 'x5' 'xf' 'xx' 'y5' 'yg' 'yy' 'zg' 'zn' 'zw' - f | '4p' 'am' 'bj' 'bq' 'cg' 'cm' 'cz' 'dm' 'ds' 'du' 'e6' 'eg' 'en' 'eo' 'eq' 'et' 'ev' 'ex' 'fw' 'go' 'hb' 'hq' 'i0' 'ij' 'k6' 'kg' 'l6' 'lm' 'lq' 'mr' 'ms' 'oa' 'p6' 'q7' 'qa' 'qi' 'qk' 'r8' 'ra' 're' 'rr' 's8' 'sj' 't6' 't9' 'tc' 'v1' 'vd' 'w1' 'wf' 'wj' 'xy' 'yd' 'yn' 'yo' 'yt' 'yu' - f | 'a6' 'aa' 'ae' 'bn' 'c0' 'c7' 'ck' 'df' 'dn' 'ds' 'dt' 'e0' 'ea' 'eo' 'eu' 'f0' 'f6' 'g1' 'gd' 'i4' 'j6' 'ja' 'je' 'jt' 'mv' 'no' 'of' 'os' 'ou' 'ox' 'pi' 'pq' 'pr' 'q6' 'qk' 'ql' 'qv' 'qw' 'rh' 'rm' 'rv' 'sv' 't6' 'u2' 'un' 'ux' 'wc' 'wl' 'wo' 'wp' 'xg' 'y7' 'y8' 'ya' 'yn' 'yx' 'zl' - f | 'ab' 'am' 'aq' 'b9' 'bj' 'ck' 'cw' 'e2' 'e8' 'ek' 'eo' 'ew' 'f2' 'ff' 'fh' 'ft' 'gl' 'gs' 'h7' 'i0' 'ik' 'jl' 'jo' 'jq' 'jr' 'kc' 'kg' 'kw' 'mm' 'mo' 'pb' 'pn' 'qe' 'qg' 'qk' 'qr' 'qu' 'r2' 's8' 'sd' 'sq' 'su' 'td' 'tr' 'uh' 'uu' 'v2' 'vl' 'w8' 'wg' 'wm' 'wt' 'xl' 'xq' 'yi' 'yo' 'yq' - f | 'ah' 'ar' 'bk' 'db' 'dd' 'dq' 'eo' 'fe' 'fj' 'fv' 'g2' 'gc' 'hp' 'i7' 'j2' 'k9' 'kt' 'l9' 'ld' 'lu' 'lz' 'me' 'mr' 'mu' 'mx' 'nr' 'o6' 'ol' 'or' 'pa' 'ph' 'pi' 'q4' 'qn' 'qp' 'r5' 'rb' 'rn' 's8' 'sf' 't6' 'tl' 'tq' 'tx' 'u6' 'uc' 'uv' 'wf' 'x1' 'x7' 'xa' 'xl' 'xm' 'yb' 'yh' 'yw' 'zw' - f | '1m' '2u' 'ai' 'ap' 'bb' 'bd' 'cb' 'dn' 'fl' 'fm' 'fo' 'fu' 'gi' 'gl' 'gs' 'hg' 'hi' 'hm' 'hp' 'i0' 'if' 'ij' 'jn' 'jt' 'ju' 'k8' 'ld' 'lm' 'oe' 'ox' 'pa' 'q7' 'qe' 'qi' 'qo' 'qw' 'rf' 'rg' 'ry' 'sq' 't7' 'tk' 'tn' 'tz' 'u5' 'uc' 'v4' 'vr' 'w4' 'w6' 'wd' 'wo' 'ws' 'wt' 'wu' 'wv' 'x3' 'yl' - f | '1x' '2h' '6b' 'ah' 'av' 'ay' 'b9' 'bu' 'bv' 'c6' 'dv' 'e2' 'ea' 'ec' 'fh' 'gk' 'ia' 'j2' 'ji' 'k2' 'kc' 'kg' 'kq' 'kz' 'la' 'mq' 'nb' 'od' 'oe' 'ot' 'p8' 'pm' 'pp' 'qc' 'qi' 'qr' 'rg' 'rp' 'rw' 'tl' 'u5' 'ub' 'un' 'uv' 'vc' 'vi' 'wc' 'wi' 'wp' 'wv' 'xg' 'xq' 'yq' 'z3' 'zd' 'zv' 'zx' 'zy' - f | 'as' 'bb' 'bn' 'ce' 'd4' 'dp' 'ez' 'gu' 'gw' 'hd' 'hp' 'ia' 'ih' 'ij' 'j6' 'jm' 'jz' 'ku' 'kw' 'lh' 'mu' 'nl' 'nu' 'oc' 'pa' 'pm' 'q8' 'qd' 'qg' 'qi' 'qm' 'qn' 'qr' 'qs' 'qw' 'r9' 're' 'ro' 'ru' 'rv' 'rw' 't1' 't3' 'tm' 'u4' 'uf' 'uw' 'wa' 'wf' 'wk' 'xa' 'xs' 'yg' 'yp' 'ys' 'yu' 'yz' 'z4' - f | '3q' '5p' 'a2' 'be' 'bv' 'bw' 'cv' 'd8' 'df' 'dh' 'dk' 'e2' 'ec' 'eo' 'fc' 'g8' 'gq' 'h8' 'ig' 'io' 'ir' 'iv' 'je' 'kq' 'kx' 'lc' 'lr' 'lt' 'ok' 'pk' 'q1' 'q4' 'qe' 'qh' 'qw' 'qz' 'rj' 'rv' 't8' 'tf' 'tl' 'tq' 'uh' 'uz' 'wk' 'wp' 'ws' 'wt' 'wv' 'x3' 'yb' 'ye' 'yo' 'yp' 'yy' 'z6' 'z8' 'zb' 'zm' - f | 'a1' 'ag' 'ai' 'ap' 'bp' 'by' 'dc' 'dy' 'e5' 'eg' 'ei' 'el' 'em' 'fc' 'gl' 'h8' 'hr' 'i1' 'ib' 'ie' 'if' 'ij' 'j5' 'jj' 'kf' 'kx' 'lc' 'm3' 'mg' 'nb' 'ol' 'ph' 'py' 'q1' 'q5' 'qi' 'qk' 'ql' 'qp' 'qs' 'sv' 'td' 'te' 'tn' 'vd' 'vm' 'vq' 'wv' 'xp' 'xu' 'yb' 'yo' 'yq' 'yx' 'yy' 'zp' 'zr' 'zx' 'zz' - f | 'af' 'ag' 'ak' 'bb' 'cr' 'cy' 'd7' 'db' 'df' 'di' 'eh' 'ew' 'fl' 'fr' 'gd' 'gp' 'hf' 'hk' 'hu' 'ib' 'ik' 'io' 'ix' 'jg' 'k9' 'kc' 'ke' 'm4' 'ma' 'mx' 'mz' 'ob' 'oi' 'oo' 'or' 'ox' 'pg' 'pl' 'pu' 'qc' 'qe' 'qg' 'rl' 'ro' 'rv' 'rz' 'sj' 'sq' 'u4' 've' 'we' 'wi' 'wk' 'wy' 'xd' 'yh' 'yq' 'yv' 'yx' - f | 'ah' 'c3' 'd1' 'dh' 'dz' 'ei' 'em' 'ex' 'fe' 'fk' 'g7' 'gz' 'hi' 'hx' 'i3' 'iu' 'j1' 'jm' 'k8' 'kb' 'ku' 'lf' 'lv' 'lz' 'm3' 'mn' 'my' 'nc' 'oj' 'pk' 'qh' 'qn' 'qo' 'qq' 'rf' 'ru' 'ry' 's9' 'sa' 'sc' 'sd' 'se' 'sz' 'td' 'tg' 'tq' 'tx' 'tz' 'vr' 'vu' 'w3' 'wd' 'wl' 'wp' 'x9' 'yb' 'yd' 'yr' 'yt' - f | '0z' 'ae' 'br' 'cr' 'dd' 'di' 'du' 'e7' 'ea' 'eb' 'ee' 'ej' 'em' 'eo' 'fo' 'fs' 'gc' 'gf' 'gj' 'hu' 'iv' 'kc' 'lb' 'nw' 'o9' 'ov' 'oz' 'pq' 'qq' 'ro' 'rr' 'rt' 'ru' 'rw' 's4' 'te' 'tm' 'tq' 'um' 'v7' 'va' 'vi' 'vr' 'wb' 'wg' 'wo' 'wp' 'wr' 'ww' 'wy' 'xv' 'y1' 'y9' 'ya' 'ym' 'yt' 'yu' 'z5' 'zl' 'zs' - f | '3i' 'a3' 'af' 'cc' 'cs' 'cv' 'd8' 'ei' 'ej' 'ep' 'et' 'ex' 'fb' 'fe' 'fx' 'hc' 'ij' 'jf' 'jp' 'jr' 'js' 'kc' 'km' 'kz' 'lc' 'm7' 'nv' 'ob' 'oc' 'pt' 'q5' 'q7' 'q8' 'qc' 'qm' 'qn' 'qs' 'qy' 'ra' 're' 'rk' 'rx' 's0' 's4' 'se' 'sh' 'sm' 't7' 'tl' 'to' 'tz' 'ue' 'um' 'w3' 'wk' 'wp' 'ya' 'yl' 'yn' 'ys' - f | 'a5' 'al' 'b6' 'bu' 'co' 'd0' 'd4' 'db' 'e0' 'e2' 'eb' 'ei' 'ej' 'el' 'et' 'ez' 'g7' 'gu' 'j6' 'jl' 'k3' 'kf' 'km' 'kw' 'kx' 'lp' 'me' 'mw' 'my' 'n2' 'n5' 'n7' 'nr' 'ny' 'oj' 'q8' 'qb' 'qg' 'qn' 'qq' 'qz' 'ra' 'rk' 'rl' 'rn' 's3' 's6' 't2' 't8' 'tk' 'tm' 'ul' 'uq' 'wg' 'y1' 'yc' 'yn' 'yq' 'yy' 'zr' - f | 'af' 'am' 'az' 'ce' 'ci' 'dd' 'e5' 'eb' 'ee' 'eh' 'fe' 'fj' 'fv' 'ge' 'gl' 'hp' 'hx' 'i5' 'i8' 'ia' 'ig' 'jb' 'jm' 'jv' 'k6' 'kg' 'kv' 'li' 'ls' 'lx' 'm2' 'md' 'mz' 'o0' 'oe' 'oy' 'pv' 'q5' 'qh' 'qm' 'qo' 'qy' 'rf' 'ri' 'rp' 'rz' 'su' 'tj' 'tz' 'u2' 'uh' 'ur' 'vg' 'we' 'wi' 'xj' 'yb' 'yx' 'yz' 'z0' - f | 'b9' 'c9' 'cc' 'cx' 'di' 'ec' 'eo' 'f5' 'fn' 'gd' 'ge' 'gh' 'hc' 'hy' 'i8' 'if' 'iq' 'ir' 'ix' 'ja' 'jl' 'jx' 'km' 'l5' 'l8' 'le' 'mm' 'oc' 'pr' 'qg' 'qo' 'rm' 'ro' 'rv' 'sm' 'td' 'th' 'tm' 'tx' 'u7' 'uc' 'ue' 'uk' 'ux' 'vw' 'wa' 'wb' 'wd' 'wh' 'wr' 'ww' 'xa' 'xj' 'xz' 'y4' 'yd' 'yl' 'ym' 'zf' 'zv' - f | '1a' 'a0' 'ab' 'ah' 'ay' 'cd' 'db' 'di' 'eh' 'eo' 'eq' 'f4' 'f8' 'fs' 'gv' 'h7' 'he' 'hf' 'i0' 'id' 'il' 'io' 'ip' 'kc' 'l1' 'l7' 'lw' 'mp' 'na' 'nb' 'nj' 'oj' 'ou' 'pa' 'pn' 'ps' 'pu' 'qb' 'qf' 'ql' 'qn' 'qr' 'rn' 'rw' 'so' 'tf' 'tg' 'th' 'tr' 'us' 'w3' 'w6' 'wb' 'wc' 'wf' 'wg' 'wp' 'wr' 'yv' 'yw' 'zh' - f | '1n' 'am' 'b9' 'bc' 'bi' 'dl' 'dv' 'ea' 'eq' 'ey' 'fj' 'g1' 'g3' 'g8' 'ge' 'it' 'iu' 'jh' 'jz' 'kk' 'ln' 'mc' 'mw' 'nf' 'nj' 'o4' 'o9' 'ob' 'ox' 'pq' 'q4' 'qb' 'qk' 'qn' 'qs' 'qt' 'r4' 'ra' 'rg' 'rp' 'rx' 'sj' 'sq' 'ss' 't0' 'tg' 'ty' 'ue' 'ui' 'uj' 'uv' 'v6' 'vr' 'w1' 'w9' 'wj' 'wy' 'xt' 'ym' 'ys' 'yz' - f | '1v' '20' 'au' 'bo' 'br' 'c9' 'cv' 'dp' 'ep' 'fo' 'gj' 'hb' 'hx' 'iv' 'iw' 'jc' 'jm' 'jw' 'k8' 'kg' 'km' 'kn' 'nd' 'o8' 'oh' 'ok' 'op' 'oy' 'px' 'pz' 'qh' 'qo' 'qp' 'qs' 'qw' 're' 'rr' 'ry' 's3' 'sj' 't7' 't9' 'tt' 'tw' 'u9' 'ue' 'us' 'uu' 'v2' 'w3' 'wa' 'wf' 'wg' 'wt' 'wv' 'xc' 'yi' 'yu' 'yv' 'z4' 'zj' - f | '6g' 'ar' 'ce' 'cn' 'ds' 'dt' 'e9' 'eh' 'el' 'ew' 'gt' 'gw' 'gx' 'hb' 'ho' 'i8' 'j8' 'jp' 'jr' 'kb' 'la' 'lr' 'mg' 'mi' 'ml' 'nd' 'ng' 'oj' 'op' 'pb' 'pd' 'pn' 'py' 'qc' 'qe' 'qk' 'qr' 'qt' 'qv' 'qz' 'sm' 'tc' 'tg' 'ti' 'tp' 'ty' 'u8' 'uq' 'uw' 'uy' 've' 'w0' 'wb' 'wd' 'wp' 'x4' 'xm' 'ym' 'z2' 'zh' 'zz' - f | 'af' 'as' 'b2' 'bl' 'bz' 'ca' 'cd' 'co' 'de' 'dp' 'e4' 'ed' 'en' 'eo' 'eu' 'ft' 'g0' 'gj' 'ha' 'hh' 'hn' 'hy' 'ij' 'jb' 'jj' 'jn' 'l7' 'll' 'lp' 'oh' 'ot' 'pb' 'ph' 'pi' 'pn' 'qc' 'qx' 'r5' 'rb' 'ri' 'sb' 'sd' 'sn' 'sv' 'sw' 't7' 'tb' 'ti' 'ty' 'u7' 'un' 'uo' 'wb' 'we' 'wf' 'wh' 'wo' 'wp' 'yb' 'yc' 'ys' - f | '4a' 'a9' 'au' 'bn' 'bs' 'cg' 'd4' 'dx' 'fb' 'fk' 'gc' 'hh' 'ht' 'hy' 'iy' 'je' 'jg' 'jj' 'jp' 'ju' 'kz' 'la' 'm8' 'na' 'oa' 'oj' 'ol' 'or' 'ov' 'ox' 'p6' 'po' 'q0' 'q9' 'qc' 'qe' 'qi' 'qq' 'qt' 'qx' 'si' 'sn' 'su' 'sw' 't8' 'ta' 'tv' 'tz' 'uq' 'ut' 'w6' 'w7' 'we' 'wk' 'wl' 'wq' 'wy' 'y4' 'ya' 'ze' 'zq' 'zy' - f | 'b0' 'bg' 'bh' 'cu' 'd8' 'dv' 'er' 'fd' 'fm' 'fo' 'gg' 'ij' 'ir' 'iu' 'jc' 'jl' 'jn' 'jo' 'k4' 'kb' 'ku' 'lq' 'ly' 'mw' 'of' 'op' 'ph' 'pk' 'ps' 'px' 'q0' 'qd' 'qg' 'qz' 'rb' 'rp' 'rs' 'rv' 'su' 't9' 'tm' 'tp' 'tx' 'ty' 'ug' 'ul' 'uo' 'up' 'uv' 'v5' 'wh' 'wr' 'ww' 'xw' 'y1' 'yd' 'yf' 'yn' 'z4' 'z8' 'zf' 'zn' - f | '1i' 'ap' 'bh' 'ce' 'cp' 'di' 'dm' 'dt' 'es' 'eu' 'f2' 'f3' 'fw' 'fx' 'g5' 'gf' 'gy' 'h2' 'hl' 'i4' 'ii' 'iy' 'kp' 'lh' 'lr' 'me' 'of' 'og' 'ok' 'on' 'oo' 'pb' 'pd' 'pt' 'qf' 'qk' 'qz' 'r4' 'r7' 're' 'ri' 'rj' 'ro' 'rx' 's2' 'tk' 'tl' 'tq' 'tx' 'ub' 'ui' 'us' 'vm' 'w1' 'w7' 'wi' 'wj' 'wq' 'wx' 'xp' 'yg' 'yr' 'yx' - f | 'a2' 'a7' 'aq' 'bv' 'cy' 'd0' 'df' 'dg' 'do' 'ei' 'ek' 'ev' 'ey' 'fg' 'ge' 'gg' 'h4' 'hv' 'im' 'iq' 'ix' 'j4' 'j5' 'jt' 'kv' 'nc' 'o2' 'ou' 'ow' 'ph' 'pz' 'qf' 'qj' 'qm' 'qo' 'qq' 'qs' 'ra' 'rl' 'rz' 'te' 'tj' 'tp' 'tr' 'ts' 'tx' 'u5' 'ue' 'uj' 'uk' 'uo' 'us' 'v7' 'w2' 'wa' 'wu' 'wv' 'ww' 'wz' 'y2' 'y8' 'yy' 'zg' - f | 'a6' 'ao' 'az' 'by' 'db' 'dl' 'eg' 'ei' 'el' 'eo' 'fh' 'fv' 'gl' 'h0' 'hl' 'hx' 'i9' 'iq' 'j7' 'jx' 'kg' 'kh' 'l9' 'nz' 'o9' 'oi' 'om' 'on' 'oq' 'or' 'pf' 'pz' 'qf' 'qj' 'r5' 'rq' 'rw' 'sr' 'sx' 'tc' 'tg' 'tj' 'tl' 'to' 'uj' 'un' 'vw' 'w1' 'w5' 'w8' 'wn' 'ws' 'wv' 'x2' 'x3' 'xi' 'y4' 'yd' 'ym' 'ys' 'z7' 'zq' 'zv' - f | 'am' 'as' 'ay' 'bl' 'e0' 'e1' 'ea' 'eg' 'f6' 'fc' 'fx' 'ia' 'io' 'jq' 'jx' 'kd' 'ky' 'lb' 'll' 'lr' 'lv' 'me' 'nv' 'o4' 'o7' 'oe' 'ok' 'oq' 'pe' 'pr' 'py' 'q7' 'qg' 'qh' 'qj' 'qk' 'ql' 'qq' 'qr' 'qs' 'r1' 'rg' 'rl' 'rm' 'ro' 'sq' 'sr' 'sz' 'ti' 'ts' 'ue' 'ui' 'uu' 'uz' 'vk' 'y0' 'y2' 'y9' 'ym' 'yt' 'yx' 'z8' 'zd' - f | '1o' 'ah' 'ap' 'ba' 'bn' 'cs' 'cu' 'd2' 'd8' 'dg' 'dr' 'ed' 'ee' 'er' 'et' 'eu' 'ey' 'ff' 'g1' 'hj' 'i3' 'i5' 'il' 'in' 'jd' 'kd' 'ku' 'lm' 'lv' 'mu' 'nu' 'ok' 'ol' 'oo' 'ov' 'oy' 'p8' 'pu' 'qb' 'qq' 'qx' 'r5' 'rm' 'rp' 'rv' 'sh' 'sk' 'sl' 'sr' 'tx' 'uy' 'v3' 'vu' 'w3' 'wa' 'wb' 'wv' 'xh' 'xq' 'ys' 'yt' 'yx' 'z2' 'zi' - f | '2e' 'ak' 'bv' 'cb' 'ch' 'cy' 'd9' 'dq' 'dr' 'e8' 'ec' 'ef' 'ek' 'es' 'f1' 'fw' 'gj' 'gw' 'hh' 'hr' 'i3' 'i6' 'ic' 'ji' 'jr' 'jy' 'kr' 'md' 'mu' 'od' 'og' 'oo' 'p9' 'pd' 'q1' 'q3' 'qq' 'qw' 'qy' 'ra' 'rj' 'rl' 'rn' 'sf' 't4' 't5' 't7' 'tl' 'tm' 'ts' 'u4' 'ug' 'ui' 'uw' 'vo' 'ws' 'ww' 'wy' 'yd' 'yo' 'yq' 'zm' 'zp' 'zw' - f | '5y' 'af' 'av' 'az' 'cj' 'cp' 'cq' 'dk' 'e6' 'e7' 'ea' 'ec' 'ee' 'ei' 'ek' 'em' 'ga' 'gk' 'gw' 'hc' 'id' 'ie' 'kp' 'kv' 'lw' 'm0' 'mf' 'mn' 'mw' 'ny' 'ob' 'ol' 'p9' 'ph' 'pk' 'pp' 'pw' 'q0' 'qg' 'qi' 'ql' 'qv' 'qx' 'r3' 'rg' 'ry' 'se' 'sh' 'sq' 'sz' 't1' 'tu' 'u2' 'uh' 'uj' 'uk' 'ut' 'w2' 'wb' 'ww' 'wx' 'yf' 'yo' 'zr' - f | 'ab' 'an' 'bm' 'bn' 'bp' 'ca' 'd9' 'dc' 'e0' 'e4' 'e5' 'eh' 'er' 'fe' 'fk' 'fv' 'ga' 'ge' 'hy' 'ic' 'ie' 'io' 'ja' 'jb' 'je' 'kp' 'ks' 'ln' 'md' 'ng' 'nr' 'oj' 'oy' 'p5' 'p6' 'p7' 'pe' 'pg' 'pu' 'qa' 'qq' 'qv' 'qw' 'qy' 'rg' 'rj' 'rk' 'sk' 'tf' 'tw' 'ui' 'um' 'uu' 'v9' 'vu' 'wo' 'wp' 'wx' 'wy' 'xf' 'y0' 'yp' 'z6' 'zi' - f | 'ae' 'cn' 'ct' 'dz' 'eb' 'ee' 'ff' 'fi' 'fk' 'fo' 'ft' 'gj' 'gr' 'ie' 'il' 'iv' 'iw' 'iy' 'jb' 'jf' 'ji' 'ke' 'ku' 'kx' 'l3' 'la' 'of' 'ol' 'ox' 'pb' 'pi' 'pq' 'q8' 'qi' 'qj' 'qp' 'qq' 'qw' 'r0' 'r5' 'rk' 'rq' 'ru' 'rz' 'su' 't8' 'tb' 'u0' 'ue' 'um' 'w2' 'wc' 'wm' 'ws' 'wt' 'x4' 'xc' 'xd' 'xm' 'xn' 'y0' 'yb' 'zi' 'zp' - f | '3r' '6o' 'ab' 'ay' 'b3' 'bc' 'bh' 'd8' 'dd' 'df' 'eb' 'ee' 'eh' 'el' 'eu' 'ex' 'fn' 'g3' 'ge' 'gr' 'gz' 'hd' 'ib' 'ie' 'ih' 'il' 'it' 'iu' 'jd' 'jq' 'jt' 'jv' 'li' 'pc' 'pp' 'qc' 'ql' 'qp' 'qu' 'qx' 'qz' 'ro' 'rq' 'sj' 'sz' 'te' 'tt' 'tu' 'uh' 'uo' 'up' 'us' 'uu' 'ux' 'v7' 'w3' 'wl' 'wn' 'xf' 'xu' 'ya' 'yh' 'yk' 'za' 'zt' - f | '5o' 'ad' 'aw' 'az' 'bi' 'bo' 'd1' 'db' 'di' 'do' 'e7' 'eb' 'ei' 'em' 'ep' 'eq' 'eu' 'fo' 'gg' 'gw' 'i0' 'ig' 'ih' 'iu' 'j3' 'j4' 'jo' 'js' 'kq' 'lc' 'lo' 'lu' 'm9' 'mi' 'mk' 'mt' 'n4' 'ni' 'o7' 'od' 'ot' 'pc' 'pg' 'pp' 'qc' 'qr' 'qw' 'rd' 'rx' 'se' 'sq' 'sy' 't5' 'ts' 'ub' 'vz' 'wb' 'wl' 'wr' 'wt' 'xe' 'xt' 'yg' 'yr' 'zw' - f | '5t' 'ad' 'am' 'ed' 'ei' 'en' 'eo' 'ey' 'f0' 'fp' 'fr' 'gc' 'hp' 'hz' 'ic' 'ix' 'jt' 'kn' 'kr' 'lk' 'ls' 'm1' 'mt' 'nk' 'od' 'p3' 'pa' 'pe' 'pi' 'q4' 'qa' 'qi' 'qk' 'qq' 'qt' 'qv' 'rb' 'rr' 'rv' 's3' 'se' 'sr' 't0' 'tj' 'tk' 'tp' 'tu' 'u1' 'ud' 'uf' 'uv' 'ux' 'vd' 'vu' 'wh' 'wi' 'wp' 'wu' 'x9' 'xa' 'ye' 'yn' 'yw' 'zj' 'zs' - f | '6h' '7f' 'ab' 'aq' 'ax' 'bc' 'bl' 'ce' 'cm' 'dn' 'do' 'e1' 'ec' 'ed' 'en' 'fc' 'fl' 'g1' 'ga' 'ge' 'gn' 'h8' 'i0' 'id' 'iq' 'iw' 'jc' 'ji' 'lt' 'mj' 'nm' 'o0' 'o6' 'oo' 'pa' 'pb' 'pt' 'q6' 'qf' 'qg' 'qz' 'r1' 'r2' 'r8' 'rd' 'rf' 's9' 'se' 't8' 'tc' 'te' 'u3' 'u5' 'uc' 'ug' 'ul' 'us' 'uu' 'v2' 'vz' 'wp' 'x8' 'ye' 'yq' 'yw' - f | 'a5' 'ae' 'ah' 'as' 'b9' 'bh' 'c5' 'd0' 'dc' 'dd' 'do' 'dp' 'dz' 'eg' 'eh' 'fg' 'fu' 'g3' 'gh' 'gu' 'ha' 'hb' 'hd' 'hg' 'hw' 'i3' 'ib' 'jb' 'je' 'jm' 'js' 'm8' 'ma' 'n4' 'nu' 'nz' 'ob' 'oy' 'qu' 'qz' 'r5' 'rd' 're' 'rr' 'ru' 's5' 'sf' 'sp' 'st' 'tn' 'uz' 'vi' 'vo' 'w9' 'wj' 'wm' 'ws' 'wu' 'wv' 'wy' 'xa' 'xc' 'yl' 'zd' 'zp' - f | 'a6' 'ad' 'ak' 'ap' 'd1' 'de' 'dj' 'eb' 'eg' 'fi' 'fq' 'ft' 'gx' 'gy' 'hn' 'hs' 'i4' 'ia' 'im' 'is' 'kf' 'ko' 'kt' 'l0' 'lr' 'm8' 'mv' 'oi' 'on' 'pc' 'pl' 'pn' 'pp' 'q2' 'q4' 'qj' 'ql' 'qv' 'qy' 'r2' 'rc' 'rd' 're' 'rm' 'rt' 'sj' 'tb' 'tc' 'tf' 'tk' 'uf' 'uk' 'ul' 'um' 'uq' 'uy' 'vj' 'wl' 'wv' 'ww' 'x7' 'xj' 'y7' 'yx' 'zb' - f | 'ab' 'ct' 'cv' 'cw' 'dg' 'ds' 'e0' 'e5' 'f3' 'fj' 'fl' 'fn' 'gy' 'ha' 'ie' 'if' 'jf' 'ju' 'jx' 'kg' 'ki' 'kw' 'l0' 'ls' 'm9' 'mj' 'nj' 'of' 'om' 'oq' 'os' 'ov' 'pv' 'pz' 'q0' 'q5' 'q8' 'qf' 'qg' 'qi' 'qj' 'qm' 'qn' 'qw' 'rk' 'ru' 'sc' 'sn' 'so' 't1' 'tn' 'tz' 'un' 'uv' 'uw' 'vn' 'wh' 'wn' 'ww' 'xy' 'yd' 'yr' 'yv' 'zh' 'zy' - f | 'am' 'db' 'dj' 'dn' 'ec' 'eo' 'ev' 'ex' 'fn' 'fw' 'fz' 'gi' 'gn' 'gq' 'ha' 'ho' 'in' 'is' 'ja' 'jj' 'jk' 'js' 'kv' 'lc' 'lz' 'mj' 'o2' 'oa' 'of' 'oi' 'pe' 'pn' 'ps' 'pv' 'py' 'q3' 'qa' 'qb' 'qi' 'qn' 'qo' 'qq' 'qr' 'qv' 'rk' 'rl' 't8' 'tk' 'tv' 'tw' 'ug' 'uk' 'ux' 'uz' 'v7' 'vr' 'wd' 'wf' 'wk' 'wv' 'ya' 'yq' 'yu' 'zp' 'zx' - f | 'ao' 'as' 'bz' 'dc' 'dl' 'dn' 'du' 'dy' 'e3' 'ed' 'ee' 'ej' 'em' 'eo' 'ep' 'eq' 'er' 'fc' 'ft' 'g4' 'gc' 'gm' 'gq' 'gr' 'gy' 'hq' 'jg' 'k6' 'k9' 'ks' 'kz' 'l7' 'lo' 'lu' 'lw' 'ly' 'nb' 'oc' 'oo' 'q9' 'qc' 'qd' 'qk' 'qs' 'qv' 'ro' 'rx' 'ry' 'sk' 'sv' 'sy' 'ti' 'tk' 'ui' 'un' 'uq' 'vl' 'w4' 'wl' 'wm' 'yj' 'yo' 'yu' 'yw' 'zg' - f | '1i' '3z' 'b6' 'bh' 'bn' 'bu' 'cm' 'do' 'dt' 'ef' 'eg' 'ek' 'eo' 'et' 'ex' 'f9' 'g3' 'gj' 'gl' 'h7' 'hp' 'i8' 'ih' 'im' 'in' 'jn' 'k1' 'k7' 'kd' 'lq' 'ms' 'mv' 'n7' 'nj' 'nt' 'o4' 'o9' 'ov' 'q2' 'qa' 'qh' 'qk' 'ql' 'qp' 'qu' 'qv' 'rj' 's8' 'sf' 'sh' 't7' 't8' 'um' 'un' 'uo' 'ut' 'uu' 'w1' 'wa' 'wf' 'wi' 'wx' 'xj' 'yc' 'yo' 'yt' - f | '1w' '5i' 'a4' 'a6' 'bz' 'de' 'dg' 'dt' 'ed' 'ei' 'ej' 'ey' 'fa' 'ff' 'gg' 'hi' 'hj' 'ho' 'i2' 'ii' 'ij' 'jq' 'jy' 'ka' 'kr' 'kw' 'lv' 'm7' 'nd' 'ot' 'ov' 'oy' 'oz' 'pe' 'pj' 'qb' 'qj' 'qq' 'qt' 'qv' 'r0' 'r3' 'r4' 'ri' 'rj' 'si' 'ta' 'tc' 'ts' 'tx' 'u3' 'uj' 'ur' 'v6' 'vh' 'w3' 'w6' 'wc' 'wg' 'wq' 'x5' 'xv' 'yb' 'yt' 'yx' 'zl' - f | '10' '2p' '6r' 'a2' 'bb' 'c9' 'dq' 'dy' 'ec' 'eh' 'ej' 'ek' 'es' 'fl' 'g1' 'g9' 'gd' 'h1' 'h2' 'hj' 'hn' 'hw' 'ip' 'iz' 'jg' 'jl' 'l5' 'le' 'lk' 'me' 'mi' 'o0' 'o6' 'oa' 'oe' 'ox' 'pd' 'pm' 'pr' 'py' 'q1' 'qo' 'qq' 'qz' 'r3' 'rs' 'rw' 's7' 'sl' 'sw' 't2' 't4' 'td' 'tj' 'u3' 'ud' 'ur' 'uw' 'vy' 'wi' 'wj' 'wl' 'wq' 'ys' 'yu' 'z7' 'zp' - f | '19' 'a4' 'a7' 'ae' 'ar' 'as' 'bj' 'bp' 'bv' 'cp' 'd8' 'dp' 'e0' 'e8' 'ea' 'ee' 'ek' 'ep' 'es' 'et' 'ez' 'fp' 'gu' 'h2' 'h4' 'he' 'hp' 'ii' 'im' 'in' 'lc' 'lk' 'lu' 'lv' 'ma' 'od' 'oe' 'p7' 'pm' 'q7' 'qa' 'qe' 'qf' 'qi' 'qr' 'qu' 'rc' 'rd' 're' 'rj' 'rl' 'ro' 'tj' 'tw' 'v4' 'vb' 'w7' 'wc' 'wg' 'wi' 'wm' 'wu' 'y4' 'yj' 'yv' 'z1' 'zt' 'zu' - f | '2f' 'af' 'al' 'at' 'bc' 'bo' 'cd' 'd6' 'dk' 'dp' 'dy' 'ea' 'eu' 'ev' 'ey' 'fe' 'ft' 'gd' 'gi' 'ha' 'hm' 'hr' 'i6' 'im' 'is' 'iz' 'jb' 'jt' 'ju' 'li' 'm4' 'my' 'na' 'nm' 'nu' 'oh' 'p4' 'pa' 'pn' 'po' 'qk' 'qz' 'r4' 're' 'rf' 'rs' 'sm' 'tb' 'ti' 'to' 'tw' 'u7' 'ut' 'vz' 'w3' 'w5' 'w9' 'wc' 'wj' 'wq' 'wr' 'x7' 'xg' 'xz' 'ye' 'yx' 'ze' 'zo' - f | '2o' '4i' '93' 'a2' 'az' 'b5' 'bk' 'cd' 'do' 'ea' 'ed' 'ej' 'el' 'es' 'ez' 'fd' 'fk' 'fu' 'h7' 'hg' 'hr' 'hs' 'hv' 'iz' 'j0' 'j9' 'jb' 'jc' 'jt' 'ln' 'lu' 'lw' 'lz' 'mz' 'ns' 'nv' 'o8' 'oi' 'oq' 'ov' 'oz' 'pr' 'qi' 'qw' 'rb' 'rg' 'ro' 'rq' 'sb' 'sc' 't8' 't9' 'te' 'ti' 'tm' 'uo' 'uz' 'wd' 'wi' 'wn' 'wp' 'ww' 'wx' 'xt' 'xv' 'yb' 'yr' 'zs' - f | '2p' '3w' 'ah' 'at' 'bg' 'bv' 'bz' 'cu' 'd8' 'dw' 'eb' 'ec' 'ei' 'er' 'es' 'fn' 'gl' 'hd' 'hx' 'ij' 'ip' 'ki' 'lh' 'mx' 'my' 'nq' 'o3' 'o7' 'ok' 'or' 'os' 'pd' 'pu' 'q5' 'qa' 'qc' 'qd' 'qg' 'qh' 'ql' 'qs' 'qt' 'qu' 'qx' 'qy' 'rb' 'rd' 'rk' 'rs' 'ru' 'rv' 'rw' 'sn' 'tm' 'vw' 'w9' 'wa' 'wb' 'wm' 'ws' 'xe' 'xo' 'xu' 'y6' 'yh' 'yj' 'z4' 'zg' - f | '45' '85' 'an' 'bi' 'ca' 'cb' 'co' 'cq' 'cu' 'dt' 'e4' 'eb' 'ed' 'ef' 'em' 'eq' 'er' 'et' 'ex' 'fe' 'fk' 'fv' 'fw' 'gm' 'gq' 'hu' 'it' 'iw' 'j5' 'jj' 'jm' 'jr' 'lq' 'lz' 'mc' 'n0' 'nq' 'o7' 'ok' 'pd' 'ph' 'q6' 'qe' 'qh' 'qm' 'qy' 'qz' 'r2' 'r5' 'ra' 're' 's6' 'sb' 'sl' 'tb' 'tg' 'ur' 'vl' 'wa' 'wf' 'wt' 'wv' 'ww' 'xr' 'xz' 'yb' 'yf' 'zx' - f | 'a2' 'af' 'an' 'aq' 'ar' 'bj' 'bz' 'cx' 'dy' 'eh' 'ek' 'en' 'ex' 'fy' 'gg' 'gr' 'hh' 'if' 'ip' 'it' 'ix' 'j5' 'jf' 'jn' 'k0' 'kk' 'lq' 'nc' 'oc' 'od' 'og' 'oh' 'oo' 'or' 'pm' 'po' 'qa' 'qd' 'qe' 'qh' 'ql' 'qo' 'qt' 'qx' 'qz' 'rq' 'rr' 'ru' 'sj' 'sk' 'tl' 'tr' 'ug' 'wa' 'wb' 'wd' 'we' 'wk' 'wn' 'wq' 'wr' 'xl' 'xo' 'xw' 'yf' 'yq' 'ys' 'yw' - f | 'a6' 'ao' 'aq' 'ba' 'be' 'bg' 'cb' 'ci' 'cj' 'cq' 'cs' 'dc' 'e6' 'ea' 'ei' 'ex' 'fw' 'fy' 'g7' 'gg' 'gq' 'hg' 'ib' 'ig' 'im' 'jz' 'kv' 'me' 'mi' 'mj' 'mp' 'o6' 'oq' 'os' 'ov' 'pd' 'pl' 'pr' 'px' 'qf' 'qg' 'ql' 'qr' 'qz' 'r5' 'rl' 'ro' 'ru' 'rz' 'sy' 'sz' 't7' 'tt' 'u7' 'uf' 'ug' 'uo' 'vt' 'w3' 'w8' 'wb' 'wp' 'ww' 'wy' 'xl' 'xv' 'yc' 'z3' - f | 'a7' 'ab' 'ba' 'bv' 'e7' 'ea' 'eh' 'ep' 'er' 'es' 'f4' 'gq' 'hg' 'hi' 'hs' 'ip' 'iw' 'je' 'k0' 'kh' 'kn' 'ko' 'lo' 'ly' 'm6' 'n2' 'o1' 'oh' 'on' 'op' 'os' 'pi' 'q6' 'q9' 'qh' 'qj' 'qm' 'qn' 'qp' 'qs' 's7' 'sa' 'sb' 'sc' 'tn' 'tq' 'tt' 'u4' 'un' 'ux' 've' 'w1' 'wd' 'wf' 'wj' 'wl' 'wr' 'wu' 'wy' 'xf' 'xx' 'y3' 'y9' 'yk' 'zh' 'zn' 'zo' 'zr' - f | '1l' '2u' 'ao' 'av' 'bb' 'c7' 'c8' 'ca' 'cm' 'cp' 'dc' 'di' 'e2' 'e4' 'eo' 'er' 'et' 'ex' 'g1' 'gb' 'hi' 'hr' 'ht' 'i9' 'id' 'ie' 'jk' 'jq' 'ju' 'k3' 'k5' 'lb' 'lm' 'lv' 'mg' 'no' 'o8' 'oo' 'ot' 'p2' 'pn' 'pw' 'py' 'q6' 'qg' 'ql' 'qo' 'r2' 'rf' 'rk' 'rq' 'ry' 't2' 'tb' 'tn' 'tr' 'tw' 'ub' 'w1' 'wa' 'wb' 'we' 'wi' 'ww' 'xo' 'yk' 'ys' 'yx' 'zg' - f | '26' '4v' 'a1' 'ac' 'af' 'ah' 'au' 'cn' 'd3' 'dn' 'e4' 'ec' 'eg' 'ep' 'er' 'fa' 'fb' 'g6' 'gr' 'h3' 'hb' 'hf' 'hj' 'iu' 'iv' 'j1' 'jk' 'jy' 'k2' 'k8' 'kd' 'lc' 'lf' 'mb' 'nn' 'ot' 'p5' 'pb' 'pl' 'pw' 'py' 'q4' 'q9' 'qf' 'r6' 'ra' 'rl' 'rr' 'ru' 'rz' 'ta' 'te' 'ti' 'tp' 'u1' 'uy' 'w6' 'wb' 'wc' 'wd' 'wh' 'wv' 'wy' 'xo' 'xu' 'yd' 'yh' 'yo' 'yt' - f | 'a3' 'ac' 'af' 'ar' 'b4' 'cu' 'd6' 'dx' 'dz' 'eb' 'ej' 'ek' 'eo' 'es' 'eu' 'ff' 'gj' 'go' 'h6' 'ho' 'hx' 'i6' 'ih' 'ir' 'iy' 'jd' 'jg' 'jh' 'k5' 'n0' 'nn' 'oe' 'og' 'pj' 'pk' 'pq' 'px' 'q5' 'qc' 'qf' 'qg' 'qi' 'qm' 'qx' 'rb' 'rd' 're' 'rx' 'sn' 'sq' 'tp' 'uh' 'ul' 'um' 'uo' 'up' 'uq' 'uz' 'vt' 'wb' 'wj' 'wp' 'ws' 'wv' 'xk' 'xo' 'yv' 'yw' 'yx' - f | 'a9' 'ag' 'bh' 'dk' 'dq' 'e3' 'e7' 'ea' 'ee' 'ej' 'et' 'ez' 'f1' 'gh' 'gn' 'gr' 'hb' 'hf' 'hh' 'hz' 'ip' 'iw' 'j7' 'jh' 'ki' 'kp' 'kx' 'ml' 'nu' 'ov' 'ow' 'pg' 'ph' 'pn' 'po' 'pu' 'qd' 'qe' 'qr' 'qs' 'qv' 'r2' 'r3' 'rb' 'rd' 'ry' 's3' 'sd' 'sr' 'su' 'sz' 'tp' 'ty' 'uh' 'un' 'v0' 'v4' 'w0' 'wa' 'wk' 'wq' 'wx' 'xe' 'y6' 'yd' 'yf' 'yj' 'yl' 'zt' - f | 'ah' 'ai' 'aj' 'cb' 'cg' 'dd' 'dl' 'dp' 'eb' 'ee' 'ei' 'es' 'ey' 'f5' 'fm' 'fr' 'g0' 'gj' 'gp' 'gq' 'gw' 'hc' 'hf' 'ig' 'ij' 'iu' 'kg' 'ld' 'mr' 'nh' 'o2' 'pp' 'pz' 'qk' 'qr' 'qv' 'qz' 'r4' 'rb' 'rd' 'ro' 'rt' 'rw' 'rz' 'sr' 'sw' 'sy' 't6' 't9' 'tb' 'ts' 'tt' 'uo' 'vg' 'wd' 'wj' 'wl' 'wo' 'wp' 'wq' 'wt' 'ww' 'x8' 'yw' 'yx' 'z3' 'z9' 'zi' 'zz' - f | 'al' 'ar' 'ax' 'be' 'bu' 'dl' 'do' 'du' 'e5' 'es' 'ey' 'fb' 'fn' 'ga' 'he' 'hh' 'ho' 'i8' 'ic' 'in' 'iy' 'j1' 'j6' 'j9' 'jk' 'jm' 'ko' 'kv' 'kz' 'lk' 'ls' 'nb' 'oe' 'ou' 'pf' 'pj' 'pm' 'pu' 'py' 'q4' 'q7' 'qe' 'ql' 'qo' 'qr' 'qt' 'rh' 'rs' 'ry' 's3' 's6' 'sl' 'sq' 'tm' 'ud' 'uf' 'ur' 'vy' 'w5' 'w8' 'wp' 'wt' 'wx' 'xq' 'yc' 'yd' 'yi' 'yl' 'ym' - f | 'b0' 'b7' 'bh' 'bt' 'c0' 'cd' 'dv' 'e5' 'ed' 'ef' 'eq' 'ff' 'fj' 'fk' 'ga' 'gt' 'gx' 'hm' 'hx' 'i1' 'ih' 'il' 'j5' 'ja' 'jf' 'jp' 'jt' 'm2' 'nf' 'nt' 'of' 'oo' 'ou' 'p7' 'pa' 'pl' 'po' 'qa' 'qd' 'qr' 'qs' 'qt' 'qx' 'rb' 'rd' 're' 'rf' 'rv' 's0' 'sj' 'sw' 'tf' 'tu' 'tz' 'u2' 'ue' 'uq' 'ur' 'us' 'vw' 'w8' 'wc' 'wo' 'wt' 'x8' 'xo' 'yd' 'zi' 'zq' - f | '1i' '6c' 'ah' 'aq' 'ax' 'b4' 'cg' 'd5' 'da' 'do' 'eu' 'ex' 'ez' 'fa' 'go' 'gt' 'h5' 'h8' 'hi' 'i2' 'ib' 'il' 'in' 'is' 'iz' 'j0' 'j7' 'jn' 'jq' 'ku' 'kw' 'li' 'ls' 'n3' 'nc' 'nt' 'o6' 'oc' 'p4' 'p7' 'pc' 'qc' 'qi' 'qp' 'qs' 'qw' 'r3' 'rl' 'rq' 'ry' 's5' 'si' 'sm' 'ta' 'tr' 'ud' 'ut' 'uw' 'v0' 'w7' 'wg' 'wm' 'xi' 'yi' 'yl' 'yn' 'z1' 'z5' 'zk' 'zp' - f | '3y' 'a9' 'au' 'bp' 'ca' 'cf' 'cn' 'de' 'dh' 'dk' 'dw' 'ec' 'er' 'ey' 'fn' 'g1' 'hb' 'hd' 'hg' 'hi' 'hj' 'ib' 'if' 'iu' 'j7' 'jd' 'jg' 'jj' 'jx' 'k2' 'km' 'kv' 'ld' 'lo' 'mz' 'o3' 'ok' 'ot' 'pl' 'pn' 'pr' 'q0' 'q2' 'q7' 'qa' 'qc' 'qg' 'qm' 'qp' 'qx' 'rc' 'rq' 'rt' 'si' 'so' 'tp' 'tu' 'uj' 'vi' 'vx' 'w7' 'wb' 'wd' 'wm' 'ws' 'wt' 'wv' 'y9' 'yf' 'yq' - f | '7u' 'a4' 'ah' 'aj' 'al' 'cu' 'e1' 'fi' 'fq' 'fr' 'he' 'hi' 'hk' 'hn' 'hp' 'hs' 'ih' 'iw' 'je' 'k4' 'k6' 'kx' 'l1' 'lf' 'li' 'lj' 'lm' 'lp' 'ls' 'lu' 'ma' 'nr' 'o2' 'o4' 'oe' 'oh' 'os' 'ow' 'p7' 'pl' 'q3' 'qj' 'qt' 'qy' 'qz' 'r3' 'rc' 'rm' 'ro' 's0' 's8' 'so' 't7' 'th' 'ti' 'tm' 'tt' 'ut' 'v5' 'va' 've' 'w7' 'wc' 'wk' 'wl' 'wm' 'wq' 'wu' 'x0' 'zh' - f | '8l' 'au' 'ay' 'bn' 'bs' 'ch' 'co' 'cu' 'dq' 'dv' 'eq' 'ev' 'ff' 'fp' 'fr' 'fz' 'gb' 'gu' 'hb' 'he' 'hj' 'id' 'ih' 'jb' 'kg' 'lm' 'ls' 'nm' 'oe' 'ot' 'p6' 'pb' 'ps' 'q0' 'q8' 'qg' 'qh' 'qj' 'qo' 'qp' 'qr' 'qt' 'qz' 'r1' 're' 'rh' 'rp' 'ry' 's1' 's4' 'sc' 'sd' 'sn' 'tf' 'ty' 'u4' 'us' 'vt' 'vx' 'wc' 'wd' 'ws' 'wv' 'xp' 'y2' 'y5' 'y6' 'yy' 'zh' 'zx' - f | 'a2' 'al' 'av' 'b9' 'bd' 'bo' 'c8' 'cq' 'dz' 'ej' 'el' 'ep' 'ew' 'f3' 'f5' 'fr' 'ft' 'h2' 'i3' 'ic' 'id' 'iy' 'jd' 'ke' 'kf' 'kr' 'l3' 'lf' 'mg' 'mu' 'nj' 'o3' 'ob' 'ol' 'ov' 'pb' 'pj' 'pl' 'pp' 'qn' 'qz' 're' 'rg' 'rk' 'ru' 's3' 'sa' 'sh' 'sj' 'su' 'sw' 'tb' 'tk' 'tv' 'ue' 'uh' 'ul' 'un' 'vk' 'vu' 'w0' 'wa' 'wm' 'wo' 'wq' 'xa' 'xl' 'ya' 'yn' 'yo' - f | 'a3' 'a4' 'a5' 'bd' 'co' 'cq' 'd8' 'da' 'dn' 'do' 'du' 'dv' 'ed' 'ei' 'en' 'eo' 'es' 'et' 'f1' 'fa' 'ha' 'hr' 'i6' 'id' 'ie' 'if' 'ir' 'jx' 'kd' 'kh' 'mb' 'mq' 'mv' 'o6' 'oa' 'or' 'oy' 'pl' 'pr' 'q3' 'q8' 'qa' 'qb' 'qc' 'qd' 'qk' 'qs' 'ra' 'rc' 'rp' 'rz' 'se' 'tg' 'tj' 'u2' 'uq' 'uv' 'uy' 'vm' 'vz' 'wh' 'wu' 'xf' 'xs' 'yc' 'ye' 'yl' 'yn' 'z1' 'zf' - f | '18' 'at' 'c6' 'ca' 'dj' 'dl' 'dx' 'ec' 'ek' 'f4' 'fo' 'fs' 'fz' 'h8' 'hn' 'ik' 'il' 'j4' 'jn' 'ld' 'ln' 'ls' 'lx' 'nu' 'o6' 'os' 'ot' 'ox' 'pa' 'pe' 'pp' 'pw' 'q1' 'qc' 'qd' 'qh' 'qk' 'qq' 'qr' 'rq' 'rr' 'rt' 'rv' 'rz' 's5' 'sd' 'sh' 'sy' 't3' 'tu' 'ty' 'uj' 'uo' 'up' 'uu' 've' 'vl' 'vu' 'wa' 'wd' 'wo' 'wr' 'ws' 'ww' 'xk' 'y0' 'y6' 'yi' 'yq' 'yy' 'z3' - f | '1t' 'a7' 'aj' 'au' 'ca' 'cn' 'cw' 'dg' 'dp' 'ec' 'ei' 'en' 'ew' 'ez' 'f3' 'f8' 'hp' 'ht' 'hw' 'is' 'iv' 'jd' 'ji' 'kn' 'l4' 'lq' 'lz' 'm7' 'o6' 'oj' 'oz' 'p4' 'p7' 'po' 'pp' 'q2' 'qa' 'qj' 'qo' 'qt' 'qv' 'qw' 'qy' 'ra' 'rd' 'ri' 'rn' 'rr' 'ry' 's7' 'st' 'sz' 'tg' 'to' 'tu' 'tx' 'w2' 'w4' 'w5' 'wd' 'we' 'wg' 'wo' 'ws' 'xm' 'y5' 'yf' 'yl' 'ym' 'yr' 'yz' - f | '2e' '3u' 'aa' 'an' 'ap' 'bc' 'br' 'bx' 'cc' 'dt' 'ea' 'ei' 'ej' 'em' 'eo' 'ep' 'eq' 'es' 'et' 'ey' 'fb' 'fw' 'gh' 'gs' 'gv' 'hd' 'hg' 'hn' 'i7' 'it' 'jf' 'ka' 'kl' 'l8' 'la' 'm0' 'nu' 'o2' 'oc' 'oy' 'ph' 'po' 'pz' 'q2' 'qg' 'qh' 'qn' 'qp' 'qt' 'r7' 'r8' 'rd' 'rf' 'rj' 'rk' 'sm' 't1' 'tb' 'th' 'ub' 'ui' 'ul' 'um' 'uu' 've' 'w8' 'wj' 'wx' 'y8' 'zf' 'zn' - f | '2n' 'ar' 'b3' 'bc' 'bd' 'c0' 'c9' 'cb' 'cp' 'do' 'ee' 'eh' 'ep' 'f3' 'fb' 'fj' 'gg' 'gk' 'gm' 'h9' 'hk' 'hp' 'ht' 'i7' 'ij' 'iw' 'kx' 'm2' 'mt' 'mv' 'nj' 'ns' 'o7' 'oa' 'od' 'of' 'om' 'or' 'pg' 'pm' 'pu' 'qq' 'qw' 'qx' 'r0' 'rb' 'rg' 'rk' 'rv' 'se' 'sj' 'sp' 'su' 'tf' 'uu' 'v9' 've' 'vg' 'vm' 'w5' 'w6' 'w8' 'wa' 'wp' 'y5' 'y9' 'yk' 'yy' 'z2' 'zd' 'zw' - f | '4z' 'a8' 'ds' 'dv' 'dx' 'eq' 'f1' 'fs' 'gs' 'h9' 'hm' 'hp' 'hu' 'hz' 'i6' 'ip' 'is' 'j4' 'jf' 'k3' 'ku' 'lc' 'le' 'lj' 'nb' 'o6' 'ob' 'of' 'p6' 'pj' 'pm' 'ps' 'qc' 'qf' 'qh' 'ql' 'qr' 'qu' 'qx' 'r3' 'rf' 'ri' 's1' 's4' 'sr' 'st' 'sw' 'tb' 'tc' 'tg' 'tr' 'tz' 'ug' 'uq' 'uu' 'uv' 'uw' 'uz' 'vl' 'w2' 'w4' 'wq' 'wx' 'xe' 'xv' 'xx' 'y6' 'yg' 'yn' 'yp' 'yr' - f | '6x' 'a4' 'ae' 'as' 'd5' 'df' 'e9' 'ek' 'ew' 'ex' 'ez' 'fa' 'fe' 'fr' 'gk' 'hb' 'hg' 'hl' 'hp' 'id' 'it' 'ix' 'jc' 'jg' 'jk' 'jm' 'js' 'ju' 'ln' 'lq' 'mo' 'ms' 'o7' 'od' 'oi' 'oo' 'pn' 'qd' 'qt' 'qv' 'r0' 'rc' 'rj' 'rk' 'rn' 'rp' 'rx' 'rz' 'sc' 'sg' 'sk' 'so' 'ti' 'tn' 'to' 'tr' 'tz' 'uk' 'um' 'ut' 'v1' 'vr' 'wo' 'wr' 'wu' 'wz' 'xn' 'xu' 'y4' 'yg' 'z2' - f | '6y' 'aw' 'az' 'cn' 'cs' 'cx' 'd4' 'di' 'dl' 'e7' 'eg' 'eh' 'eo' 'ep' 'et' 'ew' 'ex' 'ey' 'fv' 'fz' 'gu' 'gv' 'hg' 'hl' 'hm' 'iv' 'jc' 'lb' 'me' 'nc' 'nz' 'o2' 'ox' 'pn' 'po' 'pw' 'q4' 'q5' 'qg' 'qq' 'qv' 'qz' 'r7' 're' 'rl' 'rq' 'rs' 'rx' 'sa' 'se' 'ti' 'tj' 'tn' 'tz' 'u6' 'uq' 'ur' 'us' 'v9' 'we' 'wn' 'wp' 'wr' 'ws' 'xo' 'ya' 'ye' 'yg' 'yx' 'zj' 'zl' - f | '8j' 'ao' 'bc' 'bh' 'co' 'cz' 'di' 'dq' 'dr' 'e1' 'ec' 'ei' 'el' 'et' 'eu' 'ex' 'f8' 'g7' 'gl' 'hq' 'hw' 'ib' 'kh' 'kn' 'kt' 'lc' 'md' 'n7' 'oe' 'p0' 'pg' 'pl' 'pm' 'po' 'pr' 'q0' 'q6' 'ql' 'qv' 'qy' 'r7' 'ra' 're' 'rq' 'ru' 'ry' 'sm' 'sn' 't4' 't5' 'tw' 'ty' 'u7' 'ud' 'uh' 'um' 'us' 'ux' 'v0' 'v7' 'vn' 'w6' 'we' 'wi' 'wm' 'x1' 'xo' 'xz' 'yn' 'yo' 'yz' - f | 'a9' 'b8' 'de' 'dl' 'eg' 'fb' 'fi' 'fm' 'gj' 'hp' 'hx' 'if' 'ig' 'ii' 'ik' 'io' 'iw' 'ix' 'j1' 'j3' 'j8' 'ji' 'ki' 'kt' 'ld' 'm8' 'mi' 'mj' 'mx' 'mz' 'n2' 'nk' 'oe' 'ok' 'oq' 'os' 'ot' 'pj' 'q4' 'qe' 'ql' 'qp' 'qq' 'r4' 'rl' 'rq' 'rx' 'sf' 'ss' 'td' 'ti' 'tl' 'u4' 'u5' 'uq' 'ux' 'v4' 'vl' 'vs' 'wb' 'wc' 'wg' 'wj' 'wn' 'ww' 'x9' 'xa' 'ye' 'yg' 'yl' 'yn' - f | 'ab' 'ap' 'ay' 'b6' 'bg' 'bm' 'bq' 'br' 'ce' 'co' 'dt' 'e4' 'ef' 'ej' 'ek' 'ep' 'fj' 'fq' 'fu' 'g3' 'gg' 'gp' 'hk' 'i3' 'i9' 'je' 'k7' 'ku' 'li' 'lq' 'lt' 'lx' 'mc' 'mz' 'n7' 'nl' 'o1' 'of' 'os' 'pm' 'qc' 'ql' 'qs' 'qw' 'r6' 'rf' 'rj' 'rp' 'ru' 'rx' 'rz' 'tl' 'tw' 'ty' 'u1' 'u3' 'ud' 'un' 'uw' 'vu' 'w6' 'wf' 'wl' 'wq' 'xh' 'xm' 'yc' 'yh' 'yr' 'yw' 'z9' - f | 'ad' 'af' 'ar' 'b0' 'bw' 'c4' 'cn' 'do' 'e0' 'e4' 'em' 'eo' 'fn' 'ga' 'gg' 'gy' 'hf' 'ht' 'id' 'ig' 'ik' 'iv' 'j6' 'mi' 'n5' 'ng' 'ob' 'og' 'ou' 'oy' 'p4' 'pd' 'pg' 'pk' 'po' 'pt' 'pw' 'q3' 'q5' 'qb' 'qr' 'qt' 'r3' 'rc' 'rd' 'rl' 's0' 'sy' 't9' 'tc' 'tm' 'tq' 'ub' 'up' 'uv' 'ux' 'vm' 'vv' 'vw' 'wc' 'wg' 'wk' 'wm' 'x0' 'xp' 'xr' 'xv' 'xx' 'yg' 'ym' 'yw' - f | 'at' 'bm' 'd0' 'd5' 'eg' 'ei' 'ek' 'eq' 'er' 'eu' 'ez' 'fs' 'gh' 'hc' 'ho' 'ix' 'ko' 'kq' 'l1' 'll' 'mz' 'ob' 'ou' 'p2' 'pi' 'pl' 'ps' 'pt' 'q0' 'q5' 'qa' 'qg' 'qj' 'qm' 'qq' 'qt' 'rf' 'ri' 'rm' 'ru' 'rv' 'rx' 'ry' 's4' 't4' 'td' 'tn' 'tq' 'tx' 'u3' 'up' 'uq' 'vn' 'wb' 'wf' 'wk' 'wr' 'wv' 'wy' 'xh' 'xx' 'y3' 'yc' 'ye' 'ys' 'za' 'zd' 'zo' 'zs' 'zv' 'zy' - f | '0p' 'ab' 'as' 'az' 'b5' 'bl' 'cn' 'cr' 'ct' 'cu' 'dq' 'dr' 'em' 'eq' 'fe' 'fj' 'fk' 'fy' 'gn' 'gx' 'h9' 'hg' 'hk' 'hr' 'io' 'iy' 'jh' 'la' 'lb' 'll' 'ln' 'lq' 'me' 'mp' 'mr' 'mv' 'ns' 'od' 'op' 'os' 'ov' 'po' 'pv' 'qa' 'qn' 'qp' 'qs' 'qt' 'r0' 'rc' 'rd' 'rl' 's5' 'si' 't7' 'tc' 'th' 'u0' 'um' 'uz' 'vg' 'vo' 'wa' 'wp' 'wu' 'xk' 'yg' 'yh' 'yi' 'yo' 'yy' 'zy' - f | '10' 'ac' 'as' 'av' 'b6' 'bb' 'cj' 'd3' 'd8' 'dt' 'eb' 'ey' 'ez' 'fm' 'g8' 'gf' 'gs' 'hn' 'hq' 'ib' 'ii' 'j0' 'jb' 'k4' 'l5' 'ld' 'mg' 'mu' 'nz' 'oa' 'ou' 'pn' 'q1' 'qa' 'qg' 'qj' 'qm' 'qn' 'qs' 'qx' 'qy' 'rj' 'rm' 'rr' 'ry' 's0' 's7' 't4' 't6' 'tb' 'td' 'u0' 'ua' 'ut' 'ux' 'uz' 'v1' 'v9' 'vd' 'vz' 'wc' 'wj' 'wo' 'ws' 'ww' 'x9' 'xu' 'y7' 'yq' 'yv' 'zw' 'zy' - f | '11' '3o' '7a' 'au' 'bc' 'bx' 'cg' 'dz' 'e5' 'ea' 'eb' 'ee' 'eg' 'eo' 'er' 'eu' 'g2' 'gq' 'gx' 'i4' 'in' 'ip' 'iv' 'k4' 'kd' 'kw' 'lf' 'ls' 'oq' 'ot' 'oz' 'p7' 'ph' 'pk' 'ps' 'qa' 'qd' 'qf' 'qi' 'qj' 'qr' 'qt' 'r4' 'rb' 'rd' 'rl' 'th' 'tl' 'tx' 'uc' 'uj' 'v6' 'vj' 'vz' 'we' 'wj' 'wl' 'wm' 'wp' 'wy' 'wz' 'xj' 'xk' 'xl' 'ya' 'yj' 'yl' 'yo' 'yw' 'zs' 'zv' 'zw' - f | '5h' 'ak' 'al' 'aq' 'cw' 'd7' 'dm' 'ec' 'ee' 'ef' 'el' 'f4' 'f5' 'f8' 'fh' 'fr' 'fu' 'gh' 'hk' 'ir' 'ix' 'k0' 'k2' 'ka' 'ku' 'lg' 'lr' 'm8' 'mf' 'nq' 'of' 'ph' 'pr' 'px' 'q2' 'qf' 'qh' 'qk' 'qn' 'qq' 'qs' 'qv' 'qx' 'qy' 'qz' 're' 'rh' 'ro' 'rx' 'sa' 'sh' 'sk' 'tb' 'u1' 'ud' 'uo' 'us' 'uw' 'vg' 'vp' 'w3' 'wb' 'wc' 'wf' 'wg' 'wi' 'wk' 'yr' 'ys' 'yw' 'zw' 'zz' - f | 'ae' 'ak' 'ax' 'cm' 'cw' 'cx' 'df' 'dm' 'dw' 'eb' 'ef' 'ei' 'el' 'f1' 'fn' 'gu' 'hf' 'hk' 'ik' 'iw' 'jd' 'ju' 'jz' 'k7' 'ku' 'lq' 'mf' 'mw' 'oa' 'od' 'oe' 'ou' 'ow' 'pc' 'ph' 'pw' 'q0' 'q2' 'qd' 'qq' 'qw' 'qx' 'rj' 'rk' 'rn' 'rx' 's2' 'sm' 'sx' 't9' 'ti' 'tu' 'tw' 'u3' 'ud' 'wf' 'wm' 'wt' 'xl' 'xo' 'xs' 'yh' 'yi' 'ym' 'yr' 'yu' 'yw' 'z2' 'za' 'zf' 'zl' 'zz' - f | 'ae' 'ap' 'bi' 'd7' 'ee' 'ej' 'ek' 'ev' 'ew' 'fb' 'gm' 'hf' 'ho' 'hz' 'i6' 'ib' 'id' 'il' 'im' 'io' 'ip' 'ir' 'iv' 'j2' 'jf' 'jl' 'jo' 'kh' 'kk' 'kt' 'lr' 'm4' 'mi' 'nm' 'ns' 'og' 'pv' 'pw' 'q4' 'qd' 'qi' 'r6' 'r8' 'rn' 'rp' 'si' 't7' 'ta' 'td' 'te' 'ti' 'tm' 'u6' 'ub' 'vu' 'w6' 'w8' 'wa' 'wb' 'wc' 'wd' 'wf' 'wh' 'wn' 'wt' 'wz' 'y5' 'ya' 'yg' 'yk' 'yn' 'z3' - f | 'as' 'b1' 'bc' 'br' 'cd' 'dw' 'e2' 'ec' 'ew' 'f1' 'f2' 'f6' 'g0' 'gi' 'gy' 'hf' 'hk' 'if' 'ii' 'is' 'iy' 'j2' 'jp' 'kl' 'ku' 'l4' 'lg' 'lp' 'lv' 'lw' 'nd' 'ng' 'oo' 'q4' 'q6' 'q9' 'qc' 'qh' 'ql' 'qm' 'qo' 'qx' 'qz' 're' 'ro' 'sm' 't5' 'th' 'tt' 'ul' 'um' 'v0' 'vb' 'vf' 'vx' 'w9' 'wc' 'we' 'wp' 'xi' 'xl' 'xt' 'xv' 'y3' 'y6' 'ye' 'yg' 'yq' 'yw' 'yx' 'zs' 'zu' - f | '3y' 'a1' 'ay' 'cn' 'd3' 'dc' 'dh' 'e9' 'ef' 'ew' 'f9' 'fd' 'fg' 'ft' 'h6' 'hs' 'hu' 'hz' 'i0' 'ib' 'ip' 'iu' 'jw' 'kf' 'kp' 'kw' 'li' 'lp' 'mg' 'mj' 'mz' 'ng' 'oc' 'od' 'om' 'ou' 'ov' 'p8' 'pi' 'q8' 'qe' 'qf' 'qh' 'qj' 'qo' 'qt' 'qv' 'qz' 'r2' 'r3' 'rc' 'rm' 't4' 'ty' 'tz' 'u6' 'ua' 'ut' 'v3' 'vj' 'w0' 'w6' 'wi' 'wj' 'wq' 'wt' 'wu' 'xe' 'xv' 'xy' 'y3' 'yv' 'zw' - f | '4a' '56' 'ae' 'ao' 'ay' 'bn' 'bx' 'c9' 'cl' 'd0' 'd7' 'en' 'et' 'fj' 'fk' 'fu' 'g4' 'ha' 'ht' 'hv' 'ih' 'ij' 'ir' 'iw' 'k1' 'k7' 'kq' 'kw' 'lg' 'm5' 'me' 'nw' 'oj' 'ou' 'p6' 'pl' 'pq' 'pt' 'qc' 'qg' 'qk' 'ql' 'qm' 'qo' 'qq' 'rj' 's1' 'sd' 'sq' 'sz' 'tb' 'td' 'tr' 'tw' 'ug' 'uj' 'uo' 'ut' 'uy' 'wj' 'wk' 'ws' 'wz' 'x2' 'xx' 'xy' 'y1' 'y4' 'yc' 'yk' 'yt' 'yx' 'zp' - f | 'a2' 'a4' 'ac' 'ag' 'ao' 'ay' 'ce' 'di' 'dm' 'dr' 'dz' 'e9' 'eg' 'ej' 'el' 'em' 'eq' 'et' 'ey' 'fa' 'fw' 'fz' 'gg' 'gh' 'gp' 'gr' 'h6' 'hj' 'hk' 'i7' 'id' 'ik' 'iu' 'j2' 'kc' 'l1' 'lh' 'm5' 'm9' 'mj' 'mr' 'mx' 'ok' 'ot' 'ov' 'oz' 'p5' 'q0' 'q1' 'qe' 'qg' 'r2' 'rj' 'rk' 'rw' 'sa' 't3' 't6' 't9' 'te' 'tf' 'tj' 'tk' 'u4' 'wa' 'wf' 'wu' 'wy' 'yg' 'yj' 'ys' 'zk' 'zy' - f | '2b' '2e' 'ao' 'ap' 'au' 'bo' 'by' 'cr' 'do' 'du' 'dy' 'e7' 'eb' 'eh' 'em' 'ey' 'f6' 'ff' 'fu' 'gc' 'gi' 'gk' 'h1' 'hp' 'hs' 'hy' 'ia' 'ic' 'ig' 'ik' 'im' 'je' 'jf' 'ji' 'jr' 'l5' 'lf' 'lp' 'mv' 'ne' 'nt' 'oa' 'os' 'pj' 'po' 'q9' 'qd' 'qe' 'qf' 'qw' 'qy' 'ra' 'rg' 'rk' 'rp' 'rq' 'rx' 'sb' 'si' 'sn' 'ta' 'ux' 'v1' 'vd' 'wa' 'wk' 'wr' 'wu' 'xo' 'yg' 'yl' 'yz' 'zi' 'zt' - f | '2l' 'a5' 'ah' 'ak' 'ar' 'be' 'e6' 'eh' 'ge' 'gj' 'gn' 'gt' 'gy' 'ia' 'ii' 'iw' 'ix' 'jb' 'k9' 'kd' 'ke' 'kh' 'kv' 'lm' 'ly' 'me' 'mt' 'nb' 'np' 'o5' 'oe' 'of' 'og' 'pd' 'pf' 'pm' 'pt' 'pw' 'q3' 'qc' 'qe' 'qg' 'qh' 'qk' 'ql' 'qs' 'qt' 'qx' 'qz' 'rr' 'rw' 'rx' 'ry' 'sk' 'sr' 'su' 'sv' 'sz' 'ug' 'uk' 'uq' 'vg' 'w7' 'wb' 'wn' 'wr' 'ws' 'wz' 'x6' 'yg' 'yj' 'yo' 'zb' 'zx' - f | '3h' '4h' '93' 'ak' 'ao' 'bq' 'cw' 'db' 'dx' 'eb' 'ef' 'el' 'eu' 'ex' 'fc' 'fg' 'fo' 'fr' 'fs' 'fx' 'g5' 'gl' 'go' 'hs' 'i0' 'ii' 'ix' 'j3' 'jc' 'ke' 'l2' 'lf' 'lo' 'm6' 'ms' 'ne' 'oi' 'ox' 'p6' 'pb' 'pr' 'q9' 'qa' 'qi' 'qj' 'qq' 'qs' 'qu' 'qw' 're' 'rq' 's0' 'sa' 'se' 'sk' 'sx' 'tj' 'tk' 'u2' 'ua' 'uh' 'un' 'vy' 'wf' 'wh' 'wj' 'wq' 'wv' 'xl' 'xq' 'yf' 'yi' 'yw' 'z3' - f | 'a6' 'ac' 'ao' 'au' 'cf' 'co' 'cr' 'd9' 'da' 'ds' 'du' 'e4' 'ea' 'ew' 'f1' 'fx' 'hr' 'hu' 'ic' 'id' 'ii' 'in' 'iq' 'iy' 'j3' 'jb' 'ji' 'jl' 'kf' 'mi' 'mp' 'o3' 'oe' 'oj' 'on' 'p5' 'ph' 'pk' 'q1' 'q9' 'qa' 'qs' 'qu' 'qv' 'r9' 'rc' 'rj' 'rv' 's1' 'se' 'sl' 'su' 't2' 't5' 'tk' 'u8' 'uv' 'v9' 'vd' 'vi' 'w0' 'w3' 'we' 'wh' 'wm' 'wp' 'xm' 'y0' 'y1' 'y8' 'yf' 'yo' 'yw' 'zv' - f | 'a0' 'ad' 'ak' 'ao' 'au' 'b0' 'dg' 'dt' 'e9' 'em' 'fg' 'fk' 'fn' 'gq' 'h7' 'hf' 'hm' 'ia' 'id' 'ig' 'iv' 'kv' 'l1' 'lm' 'lz' 'm1' 'mw' 'mz' 'na' 'nh' 'nl' 'nn' 'o1' 'o3' 'om' 'p2' 'p9' 'pj' 'pw' 'pz' 'qk' 'qm' 'qy' 'rs' 'ru' 'ry' 'sf' 'su' 'sw' 'sx' 'td' 'tl' 'tp' 'tw' 'u1' 'ug' 'un' 'uo' 'uq' 'ur' 'v5' 'vk' 'wd' 'wf' 'wj' 'wq' 'wy' 'xg' 'xk' 'xn' 'xq' 'yb' 'z0' 'zt' 'zu' - f | 'a2' 'ag' 'cz' 'd0' 'd3' 'da' 'di' 'ds' 'e5' 'e6' 'e7' 'ej' 'em' 'ex' 'ff' 'fq' 'gm' 'go' 'gt' 'gv' 'gx' 'ho' 'hv' 'i8' 'ic' 'ii' 'il' 'im' 'it' 'ix' 'j1' 'ja' 'jd' 'js' 'kw' 'l6' 'le' 'lk' 'ln' 'mt' 'my' 'ns' 'ol' 'op' 'os' 'p0' 'pu' 'pv' 'pz' 'q4' 'q5' 'qe' 'qh' 'ql' 'qx' 'qy' 'r1' 'rk' 'rr' 'sb' 'sn' 'te' 'tt' 'ui' 'v6' 'w6' 'we' 'ws' 'wy' 'xt' 'xu' 'yf' 'yq' 'yz' 'zy' - f | '1l' '2t' '2z' 'af' 'az' 'be' 'br' 'cm' 'cu' 'cy' 'd2' 'd4' 'dd' 'dx' 'ec' 'ed' 'g2' 'gs' 'gu' 'id' 'ig' 'in' 'it' 'jh' 'jl' 'l6' 'ld' 'lg' 'lr' 'ly' 'nf' 'nj' 'ob' 'ot' 'p4' 'p8' 'pe' 'ph' 'q5' 'qa' 'qd' 'qe' 'qi' 'qo' 'qv' 'qz' 'ra' 'ri' 'rl' 'rr' 's7' 'sj' 'sx' 't0' 'tj' 'to' 'u2' 'ui' 'uq' 'ut' 'uw' 'vg' 'vs' 'w2' 'wa' 'wi' 'wl' 'wr' 'wu' 'ww' 'x7' 'xm' 'ym' 'yn' 'yp' 'zh' - f | 'a0' 'a7' 'ad' 'ae' 'ah' 'ay' 'b8' 'bh' 'dc' 'di' 'do' 'e5' 'ea' 'eb' 'ep' 'ev' 'fn' 'gq' 'gr' 'gs' 'h8' 'hc' 'hj' 'i3' 'ih' 'it' 'iw' 'ja' 'jf' 'kc' 'l8' 'lp' 'lx' 'ly' 'm1' 'og' 'oy' 'p7' 'pp' 'q7' 'qa' 'qm' 'qq' 'qr' 'qu' 'r3' 'rd' 'rg' 'rj' 'rw' 'si' 'sq' 'ss' 't0' 'tb' 'tg' 'tl' 'to' 'tp' 'tx' 'u9' 'ub' 'um' 'up' 'ux' 'vc' 'w0' 'w2' 'w5' 'wl' 'wy' 'xg' 'yb' 'yg' 'yh' 'za' - f | 'af' 'as' 'at' 'aw' 'ax' 'ay' 'az' 'b2' 'bf' 'bl' 'ck' 'cs' 'df' 'dn' 'dv' 'ei' 'ek' 'ev' 'fg' 'fm' 'h0' 'hb' 'hk' 'i0' 'ib' 'if' 'ir' 'lk' 'm5' 'mr' 'np' 'om' 'p5' 'p7' 'pl' 'pq' 'q3' 'qh' 'qi' 'qj' 'qo' 'qp' 'qs' 'qt' 'qu' 'qv' 'qy' 'ra' 'sh' 'sm' 'so' 't2' 'ta' 'ti' 'tv' 'tz' 'u1' 'ug' 'um' 'v5' 'vd' 'w1' 'wn' 'wp' 'wr' 'ws' 'ww' 'xk' 'xx' 'y7' 'yf' 'yh' 'yk' 'yt' 'zr' 'zv' - f | 'ai' 'ak' 'as' 'ax' 'b8' 'bd' 'bq' 'bv' 'd1' 'd8' 'dl' 'e9' 'ew' 'ez' 'fz' 'gj' 'gu' 'hk' 'hq' 'i2' 'ie' 'ip' 'iq' 'is' 'iz' 'jf' 'k0' 'kk' 'l6' 'md' 'mx' 'na' 'nf' 'o5' 'ol' 'p3' 'pe' 'q2' 'qf' 'qj' 'qm' 'qn' 'qs' 'qv' 'r2' 'ra' 'rv' 'ry' 's1' 's5' 'si' 't0' 'tc' 'te' 'tn' 'tq' 'tz' 'u1' 'uj' 'ut' 'w3' 'wa' 'wd' 'wh' 'wj' 'wo' 'wu' 'wv' 'ww' 'xk' 'xo' 'yi' 'yk' 'yl' 'zb' 'zw' - f | '3b' '4p' 'ae' 'aj' 'ap' 'b3' 'bb' 'c0' 'cc' 'cf' 'cn' 'dz' 'e1' 'e9' 'eo' 'ew' 'ey' 'fk' 'fo' 'gd' 'gp' 'hb' 'i8' 'ia' 'ib' 'ie' 'ij' 'iq' 'ir' 'ix' 'jo' 'kp' 'lc' 'ld' 'lp' 'ml' 'on' 'p8' 'pd' 'pf' 'pi' 'pn' 'pr' 'pt' 'pw' 'qe' 'qi' 'qj' 'qp' 'qr' 'qw' 'qz' 'r0' 'rj' 'rn' 'ro' 'rq' 'rr' 's3' 'sg' 'sz' 'tn' 'tw' 'ty' 'ui' 'uj' 'vj' 'vr' 'w5' 'w9' 'wa' 'wp' 'xt' 'ya' 'ym' 'z0' 'zr' - f | 'a7' 'ag' 'aq' 'aw' 'az' 'bf' 'bg' 'ce' 'cp' 'cx' 'dc' 'ek' 'en' 'es' 'fj' 'gb' 'hr' 'hs' 'ie' 'ik' 'is' 'jl' 'jr' 'kc' 'ku' 'l5' 'lj' 'lq' 'lr' 'nu' 'og' 'oj' 'os' 'oy' 'p1' 'p6' 'pc' 'pk' 'pr' 'pu' 'px' 'py' 'q1' 'q4' 'qb' 'qc' 'ql' 'qo' 'qv' 'qz' 'r9' 'ra' 'rg' 'rp' 'sq' 'ts' 'tx' 'u8' 'ue' 'ui' 'uj' 'uk' 'up' 'v4' 'vg' 'vn' 'w2' 'w9' 'wa' 'wk' 'wt' 'y0' 'y9' 'yc' 'ym' 'ys' 'zh' - f | 'aq' 'bo' 'cx' 'dl' 'dm' 'ed' 'ee' 'ei' 'er' 'eu' 'f4' 'fe' 'fw' 'g2' 'gj' 'h7' 'hv' 'im' 'j8' 'jf' 'kl' 'la' 'lj' 'mo' 'oc' 'oh' 'oi' 'ol' 'pa' 'pb' 'pf' 'pk' 'po' 'q9' 'qf' 'qj' 'qq' 'qv' 'r8' 're' 'rg' 'rh' 'rj' 'rr' 'ru' 'ry' 's8' 'sd' 'sg' 'sj' 'sk' 'so' 'sv' 't3' 'tz' 'u5' 'ua' 'uk' 'um' 'vi' 'vt' 'wb' 'we' 'wk' 'wn' 'wt' 'wu' 'wz' 'xk' 'xz' 'yi' 'yl' 'yz' 'z9' 'zd' 'zu' 'zw' - f | '3w' 'aa' 'ad' 'cj' 'cu' 'cv' 'dv' 'ei' 'ej' 'ep' 'er' 'fc' 'fd' 'g5' 'ga' 'go' 'gr' 'gs' 'hq' 'hx' 'hy' 'i1' 'i5' 'ie' 'ip' 'jh' 'jn' 'jq' 'k7' 'ks' 'l0' 'l6' 'l9' 'lm' 'm5' 'mj' 'ng' 'nh' 'o8' 'od' 'on' 'pq' 'q3' 'q8' 'qb' 'qc' 'qf' 'qp' 'qs' 'r1' 'ra' 'rj' 'rw' 'si' 'sp' 't6' 'ta' 'tc' 'tk' 'tm' 'ty' 'ua' 'ud' 'ug' 'v7' 'vg' 'vm' 'w3' 'wc' 'wi' 'wp' 'wy' 'xk' 'xv' 'yi' 'ym' 'ys' 'zl' - f | '5c' 'ag' 'ak' 'ao' 'ap' 'at' 'bo' 'br' 'bs' 'bw' 'cv' 'e9' 'et' 'ex' 'ez' 'fj' 'fu' 'fz' 'gm' 'gq' 'gu' 'h8' 'hl' 'i7' 'ic' 'ik' 'il' 'is' 'j1' 'jc' 'k2' 'k3' 'k6' 'k7' 'kf' 'km' 'kr' 'lj' 'mb' 'md' 'nf' 'om' 'ow' 'p4' 'pk' 'pw' 'q2' 'qe' 'qh' 'qn' 'qq' 'qu' 'qz' 'r0' 'ra' 'rp' 'ru' 'rw' 'rz' 'sv' 'ta' 'ti' 'tq' 'ua' 'up' 'us' 'ut' 'uz' 'vd' 'wl' 'wq' 'xg' 'xk' 'y0' 'yd' 'yw' 'zd' 'zq' - f | 'a0' 'a8' 'b4' 'b9' 'bb' 'cb' 'cn' 'cq' 'ds' 'e2' 'eb' 'ex' 'ez' 'fa' 'fu' 'fz' 'ge' 'gy' 'h0' 'hj' 'ht' 'hu' 'ig' 'in' 'iq' 'is' 'iu' 'jg' 'le' 'ln' 'lr' 'nj' 'nu' 'oe' 'oo' 'ow' 'oz' 'pf' 'ph' 'pj' 'ps' 'pu' 'pv' 'q0' 'q4' 'qf' 'qm' 'qn' 'qo' 'qq' 'qs' 'qw' 'r2' 'r3' 'ra' 'rk' 's7' 'sa' 'sp' 'tl' 'ue' 'ui' 'vo' 'vs' 'wj' 'wk' 'wn' 'wv' 'ww' 'x0' 'xg' 'xh' 'xx' 'y0' 'yd' 'yr' 'zg' 'zn' - f | '18' '5j' '6y' 'ax' 'bw' 'c5' 'de' 'dh' 'dl' 'do' 'dx' 'ek' 'el' 'en' 'et' 'ez' 'f1' 'fe' 'fw' 'ge' 'h1' 'h9' 'ha' 'hb' 'hl' 'it' 'jy' 'kl' 'ky' 'lo' 'nr' 'o3' 'o9' 'p0' 'pe' 'pg' 'ph' 'q1' 'q5' 'qd' 'qg' 'qh' 'qi' 'qj' 'qp' 'qq' 'qr' 'qv' 'qx' 'r0' 'rd' 're' 'rj' 'rm' 'rn' 'rq' 'rv' 'se' 'sv' 'to' 'tz' 'uo' 'v1' 'vm' 'vr' 'wa' 'we' 'wf' 'wh' 'wp' 'wt' 'wy' 'xe' 'xh' 'xz' 'y6' 'yn' 'zm' 'zy' - f | '1s' '95' 'ac' 'aq' 'cb' 'ch' 'cq' 'ct' 'cz' 'dg' 'dj' 'eb' 'ed' 'ee' 'eg' 'en' 'eo' 'ep' 'es' 'fc' 'fl' 'fp' 'gu' 'gw' 'hx' 'i0' 'i2' 'ig' 'ih' 'il' 'in' 'iu' 'iy' 'iz' 'jc' 'k7' 'ka' 'kl' 'km' 'kn' 'lz' 'mx' 'n6' 'nc' 'og' 'pt' 'pw' 'q0' 'qa' 'qe' 'qf' 'ql' 'qm' 'qp' 'qt' 'r7' 'rp' 'ru' 'rw' 'sb' 'sd' 't0' 't7' 'ta' 'tl' 'tn' 'tv' 'ty' 'ui' 'vx' 'wa' 'wc' 'wm' 'wp' 'wy' 'xi' 'xj' 'xk' 'yz' - f | '2o' '2w' 'ac' 'an' 'bb' 'be' 'c3' 'db' 'dj' 'dm' 'dw' 'eb' 'ed' 'eh' 'ew' 'ex' 'fb' 'fi' 'g2' 'gh' 'gv' 'h6' 'hw' 'id' 'ie' 'iz' 'j8' 'jk' 'jv' 'kd' 'lk' 'm8' 'ml' 'mr' 'mx' 'np' 'oe' 'on' 'oz' 'pj' 'pk' 'pm' 'pu' 'qb' 'ql' 'qv' 'qx' 'r0' 're' 'rj' 'rq' 'rw' 't3' 'ta' 'tc' 'td' 'th' 'to' 'tu' 'tx' 'ty' 'tz' 'u3' 'uf' 'vp' 'vz' 'wd' 'wh' 'wk' 'wl' 'wx' 'wy' 'wz' 'xe' 'xm' 'xs' 'ye' 'zd' 'zk' - f | '2v' '7g' 'a1' 'ar' 'bw' 'cs' 'dt' 'ee' 'eg' 'f4' 'fd' 'fl' 'g0' 'ge' 'gx' 'hf' 'hk' 'ht' 'i7' 'il' 'jy' 'ka' 'kl' 'lb' 'lm' 'lv' 'mu' 'nk' 'ns' 'nw' 'oe' 'og' 'oj' 'ol' 'ou' 'pk' 'q2' 'q3' 'qi' 'qn' 'qo' 'qs' 'qy' 'r5' 'rh' 'rj' 'rm' 'rq' 'rr' 'rs' 'rt' 'ru' 'rv' 'ss' 'th' 'tj' 'uj' 'ul' 'um' 'un' 'uo' 'uv' 'w2' 'w6' 'wf' 'wh' 'wk' 'wo' 'wx' 'y4' 'y5' 'yb' 'yn' 'yo' 'ys' 'yz' 'zb' 'zt' 'zw' - f | 'ab' 'ah' 'ax' 'bd' 'ca' 'dh' 'e3' 'ea' 'ed' 'ef' 'en' 'eo' 'er' 'ev' 'ex' 'ez' 'f5' 'fh' 'fo' 'fv' 'ga' 'gb' 'gk' 'id' 'ik' 'in' 'ir' 'jp' 'js' 'jv' 'kf' 'kr' 'lx' 'mu' 'mz' 'n5' 'od' 'on' 'pr' 'pt' 'pv' 'qb' 'qe' 'qj' 'ql' 'qq' 'qu' 'qw' 'rd' 'rl' 'rz' 's4' 'sv' 'ta' 'tj' 'tk' 'u8' 'ui' 'uj' 'uk' 'um' 'ux' 'uz' 'v9' 'vh' 'vl' 'w7' 'wb' 'wi' 'wm' 'ws' 'xi' 'ye' 'yg' 'yr' 'yw' 'zj' 'zn' 'zs' - f | '17' 'bv' 'c5' 'cn' 'd0' 'di' 'dm' 'dz' 'ec' 'ef' 'et' 'ev' 'ew' 'fa' 'fb' 'fh' 'fw' 'fy' 'g8' 'he' 'hf' 'hp' 'hu' 'hz' 'i2' 'ig' 'ix' 'jy' 'lt' 'mi' 'nm' 'nq' 'o5' 'oa' 'od' 'oi' 'oo' 'ox' 'pb' 'po' 'qb' 'qc' 'qf' 'qr' 'qs' 'qt' 'qw' 'qx' 'r0' 'rb' 'rf' 'rh' 'rk' 'ry' 'sb' 'sf' 'sh' 'st' 'tb' 'tj' 'tn' 'tq' 'tu' 'ty' 'u9' 'ud' 'ut' 'uw' 'ux' 'w4' 'wi' 'xm' 'ya' 'yl' 'ys' 'yt' 'yv' 'yy' 'zb' 'zv' - f | '19' '1t' 'aa' 'ah' 'ak' 'an' 'aq' 'bx' 'cc' 'dd' 'ed' 'ee' 'ei' 'ej' 'en' 'er' 'es' 'eu' 'fj' 'fs' 'fy' 'gd' 'gs' 'hw' 'ie' 'ig' 'ix' 'jc' 'jj' 'jk' 'jq' 'kb' 'kv' 'li' 'mr' 'no' 'nq' 'o9' 'oo' 'os' 'oy' 'p2' 'pd' 'ph' 'pn' 'pp' 'ql' 'qr' 'qy' 'r1' 'rb' 'rd' 'rn' 'ru' 's9' 'sf' 'tk' 'to' 'u1' 'u5' 'ub' 'ud' 'uf' 'vh' 'vx' 'w1' 'w8' 'wg' 'wn' 'wq' 'wu' 'wy' 'xc' 'xm' 'xw' 'y8' 'yk' 'yr' 'z3' 'zm' - f | '1n' '2h' 'at' 'ax' 'c0' 'cn' 'dc' 'df' 'dg' 'e1' 'e5' 'ea' 'eq' 'ex' 'ez' 'f7' 'f8' 'fa' 'gt' 'gv' 'gy' 'hb' 'i3' 'i8' 'id' 'iw' 'ix' 'jc' 'jg' 'k0' 'kh' 'ky' 'kz' 'lj' 'lm' 'lq' 'mj' 'o8' 'og' 'op' 'ow' 'p5' 'p9' 'qc' 'qe' 'qf' 'qg' 'qo' 'qq' 'qv' 'qz' 'rq' 'rt' 'ru' 's2' 's5' 'sf' 'sl' 'sm' 'st' 'sw' 'tc' 'tl' 'ts' 'tz' 'ud' 'ur' 've' 'wd' 'wj' 'wn' 'wq' 'xa' 'xb' 'yf' 'yj' 'yn' 'yt' 'yv' 'zf' - f | '5h' 'a2' 'ad' 'ag' 'ar' 'cf' 'ch' 'dd' 'e6' 'ei' 'el' 'em' 'f3' 'fx' 'ga' 'gm' 'h8' 'hi' 'hl' 'hm' 'ix' 'jb' 'l5' 'm2' 'mh' 'mm' 'mu' 'n9' 'nf' 'nq' 'nw' 'o4' 'oe' 'pa' 'pl' 'q1' 'qb' 'qd' 'qi' 'qj' 'qk' 'qu' 'qy' 'r0' 'r3' 'r8' 'rg' 'rj' 'rt' 's1' 'sa' 'ss' 'sx' 'tb' 'tc' 'tj' 'uf' 'uh' 'um' 'uo' 'ut' 'vf' 'vo' 'w4' 'w7' 'wf' 'wk' 'wp' 'wq' 'wr' 'wt' 'wy' 'wz' 'xk' 'xt' 'ye' 'yv' 'zd' 'zp' 'zw' - f | 'a9' 'ad' 'az' 'bo' 'bq' 'by' 'c8' 'cb' 'cp' 'd9' 'de' 'dj' 'dp' 'e4' 'ef' 'ey' 'fn' 'gc' 'ge' 'gp' 'gs' 'gu' 'gx' 'hf' 'ic' 'ie' 'ir' 'kc' 'kp' 'ku' 'l2' 'lv' 'mx' 'n7' 'o3' 'o7' 'oe' 'pb' 'ps' 'q3' 'q4' 'qb' 'qn' 'qo' 'qq' 'qu' 'qw' 'r3' 'ra' 'rk' 'sg' 'si' 'sv' 'sw' 'tb' 'tg' 'tw' 'u2' 'uc' 'uf' 'uj' 'us' 'ut' 'uu' 'v3' 'va' 'wa' 'wd' 'ww' 'wy' 'wz' 'xk' 'y6' 'y8' 'ya' 'yc' 'ye' 'yo' 'yx' 'z3' - f | '2b' 'a4' 'aa' 'ay' 'br' 'cf' 'd3' 'dh' 'dw' 'e1' 'e8' 'eg' 'en' 'ep' 'ff' 'fj' 'g2' 'go' 'gq' 'i4' 'i8' 'if' 'jc' 'jj' 'jo' 'k1' 'kk' 'ks' 'la' 'ld' 'lm' 'm4' 'ml' 'o4' 'oe' 'og' 'os' 'oy' 'pc' 'pd' 'pr' 'px' 'q5' 'q6' 'qd' 'qh' 'qj' 'ql' 'qo' 'qt' 'qu' 'qw' 'rb' 're' 'rk' 'rn' 'rr' 'rw' 'sl' 'sm' 'te' 'tf' 'tk' 'u6' 'ui' 'ul' 'uo' 'ur' 'us' 'va' 'vt' 'w7' 'wf' 'wg' 'wm' 'wn' 'ws' 'xo' 'y8' 'yj' 'zh' - f | '3q' 'am' 'ar' 'az' 'bo' 'cb' 'cv' 'dd' 'ds' 'e1' 'e7' 'en' 'eq' 'er' 'eu' 'ew' 'fq' 'fx' 'g8' 'gm' 'h5' 'ic' 'if' 'jp' 'kw' 'kz' 'l4' 'lf' 'li' 'lm' 'lq' 'lu' 'lz' 'ml' 'nz' 'o4' 'o6' 'op' 'ow' 'po' 'py' 'qj' 'qk' 'qn' 'qp' 'qw' 'qz' 'r6' 'r8' 'rc' 'rl' 'rt' 's6' 'sh' 'sx' 'sz' 'tc' 'ti' 'tt' 'ty' 'uf' 'uh' 'uj' 'um' 'ut' 'uu' 'uv' 'vn' 'vt' 'we' 'wh' 'wi' 'wq' 'wu' 'wz' 'xn' 'y4' 'yu' 'yz' 'z9' 'ze' - f | 'a5' 'ao' 'av' 'b0' 'bh' 'bk' 'cj' 'd0' 'do' 'ds' 'e1' 'ee' 'ep' 'fa' 'fh' 'fv' 'hc' 'hh' 'hq' 'i1' 'if' 'iv' 'ix' 'iy' 'jq' 'jw' 'jz' 'ko' 'll' 'lr' 'lx' 'ns' 'nz' 'og' 'or' 'os' 'oz' 'p0' 'p8' 'pr' 'pv' 'q6' 'q9' 'qb' 'qd' 'qe' 'qh' 'qi' 'qq' 'qt' 'qu' 'qy' 'rd' 'ri' 'rq' 'rr' 'ry' 'sf' 'sg' 'sh' 'si' 'sw' 'sx' 'tc' 'tq' 'u3' 'uh' 'ul' 'us' 'wg' 'wn' 'wx' 'x3' 'xb' 'ya' 'yg' 'yn' 'yo' 'yp' 'yr' 'yw' - f | 'a7' 'ad' 'aq' 'ck' 'cu' 'd8' 'db' 'do' 'dt' 'e7' 'eg' 'em' 'eq' 'ex' 'ez' 'fl' 'gr' 'h4' 'hd' 'he' 'hg' 'id' 'ie' 'if' 'in' 'io' 'j4' 'j7' 'je' 'jm' 'jo' 'k5' 'kg' 'kj' 'kn' 'l9' 'lb' 'lc' 'ld' 'lf' 'li' 'lj' 'na' 'nz' 'oa' 'ok' 'oo' 'pj' 'px' 'q3' 'q4' 'q6' 'qd' 'qi' 'qm' 'qq' 'qt' 'qx' 'r1' 'rg' 'ri' 'rn' 'sm' 'so' 'su' 'sy' 'tq' 'u5' 'uc' 'ue' 'us' 'w3' 'wc' 'we' 'wj' 'wk' 'wt' 'x8' 'xj' 'yc' 'yy' - f | '2l' 'a4' 'ae' 'ag' 'cb' 'cs' 'cu' 'cy' 'dd' 'dj' 'ed' 'ee' 'ef' 'es' 'ex' 'ey' 'ft' 'fy' 'gs' 'h4' 'ho' 'ht' 'i2' 'i7' 'ia' 'iq' 'iz' 'jz' 'ku' 'kv' 'lq' 'mv' 'of' 'oi' 'op' 'pb' 'pd' 'pl' 'pn' 'pq' 'q0' 'q3' 'qe' 'qi' 'qp' 'qs' 'qy' 'qz' 'r3' 'ra' 'rh' 'rk' 'ry' 'sj' 'st' 'ta' 'tc' 'te' 'tf' 'th' 'ti' 'to' 'tt' 'u2' 'u5' 'vf' 'vx' 'w4' 'w7' 'wm' 'wt' 'ww' 'wx' 'x2' 'xi' 'y1' 'ye' 'yq' 'yv' 'za' 'zo' 'zy' - f | '4a' 'aj' 'an' 'aq' 'ax' 'bb' 'bh' 'ci' 'cn' 'd4' 'df' 'ea' 'ef' 'ek' 'en' 'eo' 'ex' 'fc' 'fj' 'fz' 'gb' 'gl' 'h7' 'hg' 'i1' 'i4' 'ia' 'im' 'j9' 'jg' 'ji' 'jj' 'kl' 'l2' 'lb' 'ld' 'lh' 'm1' 'mf' 'n1' 'nd' 'ob' 'od' 'ot' 'ph' 'pk' 'po' 'pv' 'q1' 'q2' 'q4' 'qb' 'qu' 'qz' 'ra' 'rh' 'ro' 'rp' 's1' 'si' 'sl' 'sn' 't7' 't8' 'ta' 'tc' 'ub' 'ue' 'ul' 'v9' 'w7' 'wd' 'xq' 'y5' 'yj' 'yl' 'yp' 'yw' 'z9' 'zb' 'zu' 'zy' - f | '8w' 'ao' 'aw' 'bk' 'by' 'c5' 'ch' 'cw' 'd0' 'dh' 'e2' 'eq' 'ev' 'ha' 'hk' 'ii' 'ir' 'iv' 'j8' 'ki' 'kp' 'lm' 'lx' 'nh' 'ns' 'nt' 'o3' 'on' 'ov' 'ow' 'ox' 'pl' 'q3' 'qc' 'qd' 'qe' 'qh' 'qm' 'qo' 'qr' 'qs' 'qt' 'qx' 'r2' 'r8' 'rc' 'rd' 'rh' 'ri' 'rk' 'rq' 'rt' 's3' 'sm' 'sq' 'sz' 't1' 't6' 'tx' 'u2' 'ue' 'up' 'uq' 'ut' 'ux' 'vg' 'vp' 'w3' 'wb' 'wc' 'wd' 'wr' 'xf' 'xp' 'y1' 'ya' 'yf' 'yg' 'yi' 'yn' 'yq' 'zn' - f | '5i' 'a4' 'a8' 'ae' 'af' 'am' 'ap' 'ax' 'bf' 'cr' 'cw' 'dk' 'dz' 'ea' 'eb' 'ec' 'ef' 'ep' 'er' 'et' 'ex' 'fp' 'fv' 'gh' 'hy' 'hz' 'i8' 'ig' 'io' 'iq' 'ji' 'jw' 'kt' 'le' 'lo' 'mi' 'nb' 'nq' 'o1' 'ol' 'oo' 'oq' 'oy' 'q3' 'q8' 'qi' 'qk' 'qo' 'qq' 'qs' 'r0' 'rg' 'rl' 'rt' 's6' 'sb' 'sk' 'sq' 't9' 'ta' 'tb' 'tg' 'tj' 'tu' 'ty' 'un' 'ur' 'uw' 'vi' 'wf' 'wk' 'wo' 'wt' 'wy' 'xj' 'xq' 'y0' 'y1' 'y9' 'yc' 'yn' 'yu' 'zs' - f | '6q' 'ac' 'ag' 'av' 'aw' 'az' 'bj' 'bq' 'bx' 'cj' 'dc' 'dd' 'dq' 'eg' 'ei' 'ek' 'em' 'f7' 'fg' 'fq' 'fx' 'gb' 'gp' 'hb' 'hk' 'i9' 'ii' 'im' 'ip' 'ir' 'iz' 'jx' 'le' 'lf' 'll' 'lx' 'mp' 'mv' 'of' 'oq' 'pb' 'pg' 'pj' 'py' 'pz' 'qi' 'qk' 'ql' 'qs' 'qu' 'qw' 'r8' 'ra' 'rc' 'rj' 'sj' 'tg' 'tk' 'tl' 'tm' 'tp' 'tw' 'u4' 'uf' 'ui' 'um' 'vu' 'w7' 'wd' 'wn' 'wt' 'wy' 'wz' 'y1' 'y3' 'y9' 'yl' 'yo' 'yq' 'yu' 'zb' 'zu' 'zv' - f | 'a1' 'a6' 'aq' 'bk' 'bu' 'ch' 'dd' 'dj' 'dx' 'e7' 'ef' 'ei' 'ek' 'el' 'ez' 'fp' 'ft' 'gb' 'go' 'hd' 'hh' 'hj' 'ho' 'hs' 'ic' 'il' 'ip' 'jh' 'jn' 'jq' 'jx' 'kq' 'kv' 'la' 'm8' 'mv' 'ng' 'oh' 'or' 'p7' 'p9' 'pe' 'pk' 'px' 'q7' 'qc' 'qd' 'qh' 'qj' 'qk' 'qr' 'qx' 'rd' 'rl' 'rq' 'sl' 'sq' 'st' 'sy' 'th' 'tn' 'u9' 'uc' 'ud' 'ug' 'uy' 'uz' 'vl' 'vn' 'wa' 'wb' 'wd' 'wt' 'xi' 'xs' 'xz' 'y5' 'yi' 'yk' 'yt' 'yw' 'z7' 'zg' - f | 'a2' 'a8' 'ae' 'b3' 'b5' 'bn' 'bq' 'd4' 'd7' 'dh' 'dl' 'dt' 'e6' 'es' 'fa' 'fb' 'ff' 'gd' 'ge' 'h8' 'hj' 'hm' 'hq' 'hw' 'hy' 'im' 'ip' 'kb' 'kh' 'ku' 'lm' 'ly' 'mf' 'nn' 'o0' 'oi' 'ok' 'or' 'pn' 'po' 'pq' 'pt' 'pv' 'q4' 'qc' 'qd' 'qu' 'qv' 'rd' 'rh' 'ri' 'rk' 'rp' 'rr' 'ru' 'si' 'sk' 'sq' 'ss' 'sx' 'sy' 'ta' 'tm' 'tq' 'tx' 'ul' 'um' 'uv' 'vj' 'vt' 'w7' 'wh' 'wi' 'wl' 'wn' 'wp' 'wq' 'ww' 'xb' 'y6' 'yq' 'z2' 'zn' - f | 'a3' 'b3' 'bv' 'ck' 'cl' 'cs' 'cv' 'cy' 'dc' 'dl' 'dq' 'dr' 'du' 'eg' 'el' 'en' 'ex' 'fv' 'fx' 'gd' 'gk' 'gu' 'h7' 'ic' 'jf' 'jz' 'lf' 'lq' 'nl' 'nm' 'o6' 'ob' 'ol' 'pe' 'po' 'q2' 'qa' 'qk' 'qo' 'qp' 'qr' 'qv' 'qz' 'r5' 're' 'rk' 'ss' 'sv' 't2' 'tf' 'ti' 'tj' 'ua' 'ud' 'v8' 'vb' 'vg' 'vh' 'vi' 'w1' 'w5' 'wb' 'wd' 'we' 'wf' 'wj' 'wr' 'ws' 'wv' 'xq' 'xs' 'y1' 'ye' 'yi' 'yp' 'yr' 'ys' 'yu' 'yw' 'z7' 'z8' 'zf' 'zq' - f | 'a7' 'a9' 'al' 'aq' 'ar' 'as' 'b2' 'cj' 'cl' 'co' 'cu' 'cv' 'dc' 'e3' 'e5' 'e7' 'ea' 'ee' 'eh' 'en' 'es' 'ev' 'ez' 'fl' 'fp' 'gm' 'hm' 'it' 'iu' 'iv' 'j8' 'jd' 'jo' 'kn' 'ko' 'ky' 'ln' 'mr' 'nc' 'o2' 'ok' 'ot' 'pc' 'pe' 'pz' 'q2' 'qe' 'qi' 'qq' 'qr' 'qs' 'r8' 'rm' 'rn' 'ro' 'rx' 'sz' 'te' 'tn' 'tq' 'tx' 'u1' 'ub' 'ul' 'up' 'uq' 'wf' 'wh' 'wk' 'wl' 'wo' 'wq' 'wt' 'wu' 'wv' 'xh' 'xz' 'y5' 'y9' 'yv' 'yx' 'yz' 'zj' - f | 'a8' 'am' 'as' 'bb' 'cn' 'co' 'dd' 'di' 'dx' 'dz' 'e3' 'e5' 'ea' 'ef' 'eh' 'el' 'en' 'eo' 'ew' 'fe' 'fg' 'gc' 'gi' 'gj' 'gp' 'ha' 'hl' 'hy' 'i3' 'id' 'ij' 'ir' 'is' 'k9' 'kc' 'kf' 'l9' 'ln' 'ls' 'lw' 'm0' 'm8' 'my' 'nn' 'on' 'p9' 'ph' 'pm' 'pw' 'q1' 'q3' 'qc' 'qm' 'qq' 'qv' 'rg' 'ro' 'se' 'sw' 'sx' 'ta' 'tl' 'tz' 'u4' 'uo' 'vg' 'vl' 'vm' 'vn' 'w3' 'wd' 'wm' 'wo' 'wp' 'wt' 'yg' 'yj' 'yv' 'yx' 'yz' 'zu' 'zx' 'zy' - f | 'ag' 'ao' 'ay' 'bk' 'bl' 'bw' 'cq' 'dp' 'dv' 'e5' 'ee' 'eu' 'fv' 'fx' 'gg' 'h8' 'ha' 'hk' 'ii' 'im' 'iq' 'it' 'je' 'jf' 'jr' 'js' 'km' 'ks' 'lo' 'lz' 'o8' 'ot' 'pg' 'pl' 'q3' 'q9' 'qa' 'qd' 'qe' 'qf' 'qh' 'qn' 'qq' 'qy' 'qz' 'r7' 're' 'ri' 'rl' 'ro' 'rr' 'rx' 'ry' 'sh' 'sl' 'ss' 'sw' 't5' 'ta' 'te' 'th' 'tr' 'tt' 'tu' 'tv' 'u7' 'uk' 'um' 'up' 'uu' 'vl' 'vw' 'w0' 'wl' 'ws' 'wt' 'ww' 'xe' 'ya' 'yb' 'ye' 'yf' 'za' - f | '2i' 'ap' 'aw' 'bd' 'bs' 'c9' 'db' 'di' 'dl' 'dq' 'dr' 'e5' 'ej' 'el' 'ez' 'f9' 'ga' 'gp' 'hm' 'if' 'im' 'j9' 'k9' 'ka' 'kd' 'kj' 'lc' 'lf' 'lk' 'mf' 'mq' 'mr' 'o3' 'oe' 'oj' 'op' 'p5' 'p7' 'p8' 'pb' 'pv' 'py' 'qc' 'qd' 'qf' 'qi' 'qk' 'qq' 'qu' 'qw' 'qz' 'r2' 'r8' 'r9' 'rj' 'rk' 'ry' 'rz' 'sd' 'sk' 'sm' 't2' 't6' 't7' 'tp' 'tq' 'tt' 'tw' 'u7' 'uh' 'uv' 'vh' 'w0' 'wc' 'we' 'wh' 'wk' 'wl' 'yf' 'yk' 'ys' 'zi' 'zq' 'zs' - f | '39' 'a9' 'ag' 'ap' 'ax' 'b9' 'c3' 'dn' 'e4' 'ea' 'f7' 'f9' 'g2' 'gi' 'h4' 'ht' 'ie' 'ij' 'ir' 'is' 'it' 'jd' 'jj' 'jv' 'kp' 'kt' 'kv' 'lh' 'ln' 'ls' 'mg' 'mh' 'nc' 'nh' 'o0' 'o2' 'od' 'ox' 'pb' 'pn' 'po' 'pz' 'q1' 'q8' 'qc' 'qd' 'qf' 'qh' 'qi' 'qk' 'qz' 'rd' 'rg' 'ro' 'rz' 's7' 'sa' 'sf' 'sn' 'sw' 'tk' 'tu' 'tw' 'u7' 'uu' 'vh' 'wa' 'wd' 'wk' 'wr' 'wu' 'wy' 'wz' 'xh' 'xm' 'xt' 'yb' 'yi' 'yj' 'yn' 'yr' 'z1' 'z8' 'zn' - f | '14' '1t' 'aa' 'ab' 'ag' 'aq' 'ax' 'bc' 'cb' 'cm' 'd1' 'db' 'ea' 'ek' 'en' 'ey' 'f0' 'g6' 'g7' 'gx' 'ha' 'ho' 'i1' 'i6' 'i8' 'it' 'iw' 'jl' 'jp' 'ko' 'kt' 'la' 'le' 'mb' 'mk' 'mt' 'n6' 'na' 'ob' 'oc' 'od' 'on' 'op' 'or' 'ot' 'q6' 'qd' 'qf' 'qk' 'qp' 'qq' 'qr' 'qw' 'qy' 'r5' 'rf' 'rk' 'se' 'sp' 'sx' 't0' 'tj' 'tk' 'tq' 'tv' 'tx' 'u0' 'ub' 'ue' 'uf' 'um' 'us' 'uu' 'v9' 'vi' 'wb' 'wo' 'wu' 'wy' 'x9' 'xs' 'xu' 'y0' 'yo' 'zi' - f | '1a' '3r' 'ai' 'b8' 'bp' 'd6' 'do' 'dp' 'du' 'e0' 'e1' 'ek' 'es' 'eu' 'ey' 'ez' 'fq' 'fs' 'fy' 'gd' 'gm' 'h1' 'hu' 'i0' 'is' 'iv' 'iz' 'jd' 'jn' 'jz' 'kc' 'lf' 'lp' 'lt' 'ly' 'm2' 'mb' 'mt' 'n1' 'nr' 'of' 'oi' 'oq' 'ow' 'pe' 'pm' 'pn' 'q0' 'qb' 'qg' 'qk' 'qo' 'qz' 'r4' 'r8' 'ra' 'rn' 'rs' 's7' 'sa' 'sj' 'so' 'su' 'sv' 'sx' 'ti' 'tl' 'ts' 'ty' 'u4' 'up' 'vr' 'w2' 'wg' 'wi' 'wk' 'wl' 'ws' 'xl' 'xx' 'yf' 'yk' 'yn' 'yq' 'yv' - f | '1r' '2z' '3d' '6d' 'ay' 'bb' 'bh' 'bq' 'bv' 'cn' 'cr' 'cs' 'd5' 'd9' 'dh' 'dq' 'dw' 'dz' 'e5' 'ea' 'eb' 'eh' 'er' 'fu' 'g0' 'ga' 'gc' 'h4' 'hf' 'hh' 'hx' 'ih' 'io' 'jz' 'kk' 'ko' 'li' 'lz' 'n1' 'n4' 'nn' 'nt' 'o8' 'oa' 'oe' 'oh' 'ov' 'oz' 'p2' 'p5' 'pg' 'pt' 'px' 'qa' 'qe' 'qg' 'qm' 'qt' 'qv' 'qx' 'ro' 'rp' 'rv' 's6' 'sh' 'sn' 'sx' 'sz' 't6' 'th' 'tj' 'tu' 'u5' 'uk' 'uq' 'wg' 'wp' 'wv' 'xe' 'xv' 'y4' 'yf' 'yo' 'yx' 'zr' - f | '27' '4g' 'a2' 'al' 'bp' 'ca' 'cp' 'da' 'dt' 'e8' 'ee' 'ef' 'eg' 'ej' 'eq' 'eu' 'ev' 'fe' 'gn' 'gq' 'gy' 'i5' 'ic' 'il' 'io' 'ir' 'iw' 'iz' 'j2' 'kz' 'l2' 'l8' 'lh' 'ln' 'lt' 'np' 'ns' 'oi' 'oj' 'or' 'ph' 'pr' 'pt' 'qa' 'qh' 'qi' 'qs' 'qt' 'qu' 'r1' 'rd' 'ry' 's8' 'sj' 'sk' 'sl' 'sq' 'td' 'te' 'tg' 'tj' 'tq' 'tx' 'u0' 'ub' 'ul' 'uu' 'ux' 'uy' 'v0' 've' 'vg' 'vk' 'wa' 'wj' 'ws' 'wu' 'yb' 'yd' 'yi' 'yk' 'yo' 'yr' 'ys' 'zp' - f | '2a' '3o' '6w' '9h' 'ag' 'ak' 'cu' 'db' 'e3' 'ei' 'ep' 'eq' 'f6' 'fm' 'fq' 'fz' 'g5' 'gi' 'gk' 'gn' 'gp' 'gx' 'hb' 'hw' 'ia' 'ic' 'id' 'ig' 'ih' 'im' 'jh' 'jy' 'k3' 'kh' 'kv' 'l4' 'lf' 'lh' 'lu' 'ni' 'o7' 'oe' 'oi' 'op' 'os' 'ow' 'p0' 'p1' 'pj' 'pp' 'qa' 'qe' 'qi' 'qm' 'qz' 'rb' 're' 'rh' 'sa' 'sn' 'te' 'th' 'u3' 'ui' 'uj' 'us' 'vh' 'w1' 'w7' 'wc' 'wg' 'wh' 'wo' 'wx' 'xr' 'xw' 'xz' 'y5' 'y6' 'y9' 'ye' 'yo' 'ze' 'zg' 'zp' - f | '2i' 'am' 'ar' 'bl' 'by' 'ci' 'da' 'db' 'dc' 'dg' 'ea' 'ed' 'ei' 'ek' 'em' 'en' 'er' 'f2' 'f7' 'fi' 'fj' 'fu' 'fy' 'g0' 'g8' 'gn' 'gr' 'hl' 'hr' 'i2' 'it' 'ix' 'jg' 'jr' 'ju' 'jx' 'lh' 'lj' 'm8' 'n7' 'o1' 'o5' 'ob' 'og' 'ok' 'oz' 'pg' 'pl' 'pz' 'q4' 'q9' 'qf' 'qh' 'qn' 'qt' 'qu' 'qw' 're' 'ri' 'rn' 'ro' 'rp' 'rz' 's7' 'st' 'sz' 't3' 'tk' 'tw' 'u1' 'u5' 'uh' 'uy' 've' 'vh' 'w1' 'x2' 'xi' 'y4' 'ya' 'yi' 'yk' 'yn' 'zb' 'zo' - f | '2v' '7j' '7r' 'a7' 'aj' 'av' 'br' 'cg' 'd8' 'ei' 'en' 'ez' 'f5' 'fb' 'go' 'h0' 'hf' 'hk' 'hm' 'i9' 'ia' 'ig' 'ik' 'im' 'iq' 'it' 'iv' 'ja' 'je' 'ji' 'ks' 'lo' 'mo' 'mq' 'nj' 'o5' 'of' 'on' 'ot' 'ox' 'p1' 'p6' 'pb' 'ph' 'pj' 'pn' 'pp' 'pq' 'pr' 'qa' 'qh' 'qj' 'qm' 'qn' 'qo' 'qu' 'r2' 'ro' 's1' 'sa' 'se' 'sp' 't6' 'ti' 'tu' 'u2' 'ug' 'uh' 'ui' 'ur' 'uy' 'v9' 'wl' 'wn' 'wt' 'wv' 'wy' 'wz' 'xm' 'xs' 'ya' 'yi' 'yu' 'z8' 'zl' - f | '68' 'af' 'as' 'at' 'cf' 'ci' 'cx' 'dl' 'dt' 'dv' 'e4' 'ea' 'eg' 'es' 'fd' 'fi' 'fo' 'g1' 'gd' 'gy' 'he' 'hu' 'hw' 'hz' 'i0' 'ia' 'if' 'io' 'it' 'iv' 'iz' 'j0' 'jc' 'jj' 'jm' 'kb' 'ki' 'kk' 'ku' 'ky' 'lr' 'nv' 'oi' 'om' 'p3' 'pa' 'pt' 'qe' 'qh' 'qj' 'qm' 'qq' 'qs' 'qt' 'qu' 'r3' 'rp' 'rr' 'rw' 'rx' 's2' 'sc' 'sp' 'ti' 'tn' 'tv' 'ty' 'ue' 'uk' 'ul' 'up' 'ut' 'uu' 'uw' 'vf' 'vp' 'w2' 'w7' 'wb' 'wl' 'wq' 'xg' 'yc' 'yh' 'z3' - f | 'a8' 'ad' 'ai' 'al' 'as' 'av' 'ay' 'az' 'bf' 'bg' 'c6' 'dc' 'dd' 'dz' 'ef' 'eh' 'eu' 'ft' 'g0' 'g4' 'gf' 'gm' 'hq' 'im' 'iw' 'ix' 'j5' 'jj' 'jn' 'jv' 'k1' 'ko' 'ma' 'n0' 'nw' 'o9' 'om' 'op' 'ox' 'p4' 'pa' 'pe' 'pm' 'ps' 'q2' 'qb' 'qc' 'qf' 'qi' 'ql' 'qu' 'qw' 'qy' 'qz' 'rl' 'rn' 'rs' 'ru' 'se' 'sf' 'sm' 't8' 'tc' 'tg' 'uq' 'uz' 'va' 'vj' 'wa' 'we' 'wg' 'wk' 'wq' 'wu' 'wy' 'xb' 'xo' 'y3' 'ye' 'ym' 'ys' 'yt' 'z8' 'zd' 'zo' - f | 'aa' 'af' 'bp' 'c2' 'cd' 'ci' 'dl' 'du' 'dz' 'es' 'fn' 'fo' 'g7' 'ga' 'gc' 'gj' 'gx' 'hc' 'ht' 'iu' 'jl' 'jm' 'ke' 'kk' 'kr' 'lc' 'le' 'lr' 'lw' 'ms' 'n2' 'nq' 'nw' 'nz' 'o1' 'op' 'oq' 'ot' 'ow' 'p0' 'pb' 'pp' 'pr' 'qc' 'qh' 'qp' 'qr' 'qu' 'qy' 'r3' 'r9' 'rc' 'rk' 'rr' 'ry' 'sb' 'sf' 'sk' 'sl' 't1' 'ta' 'tb' 'tk' 'tz' 'ud' 'ur' 've' 'vn' 'vt' 'vw' 'w6' 'w9' 'wg' 'wk' 'xg' 'yg' 'yh' 'yj' 'ys' 'yt' 'yv' 'yy' 'z3' 'zo' 'zq' - f | '1o' '1v' '4v' 'ab' 'at' 'b5' 'bw' 'bz' 'cm' 'dc' 'dh' 'dj' 'dk' 'dq' 'e1' 'e5' 'ee' 'el' 'em' 'eo' 'ep' 'ew' 'fi' 'fy' 'gd' 'gh' 'gw' 'h7' 'i6' 'ia' 'ii' 'ip' 'is' 'iu' 'jk' 'k5' 'kb' 'mf' 'mp' 'mw' 'mx' 'n1' 'na' 'nm' 'nq' 'ob' 'of' 'pa' 'pb' 'pd' 'pu' 'q4' 'qb' 'qe' 'qf' 'qj' 'qo' 'qr' 're' 'rj' 'ro' 'rt' 'rv' 'sb' 'sp' 'st' 'ub' 'ue' 'um' 'vz' 'w3' 'w7' 'w8' 'wh' 'wo' 'wt' 'x5' 'x9' 'xd' 'xj' 'xm' 'ya' 'yb' 'yg' 'zr' 'zv' - f | '2d' '2w' 'ag' 'aj' 'am' 'ce' 'd2' 'd4' 'dd' 'dn' 'e0' 'e6' 'ef' 'ej' 'ek' 'er' 'f9' 'fd' 'fe' 'fq' 'ft' 'fv' 'gq' 'gy' 'he' 'ij' 'iq' 'it' 'iy' 'jf' 'jl' 'jr' 'ju' 'kq' 'ku' 'lm' 'nj' 'nw' 'nx' 'oa' 'oe' 'oj' 'p2' 'pa' 'pm' 'pq' 'pv' 'q5' 'q9' 'qb' 'qd' 'qg' 'qj' 'qk' 'ql' 'qm' 'qu' 'qx' 'qz' 'rp' 'rq' 'rs' 's5' 'sk' 'ss' 'sy' 't2' 'ta' 'ts' 'tw' 'tx' 'u5' 'w3' 'w7' 'wa' 'wp' 'ws' 'wu' 'wy' 'wz' 'xz' 'y4' 'yn' 'yy' 'zt' 'zw' - f | '3h' 'aw' 'az' 'b2' 'bl' 'bq' 'bt' 'c0' 'cd' 'ck' 'cm' 'cw' 'cy' 'd3' 'e0' 'ef' 'eo' 'ew' 'fj' 'fk' 'gb' 'gc' 'gm' 'gt' 'gz' 'hv' 'in' 'ix' 'jb' 'jg' 'jl' 'jn' 'js' 'k7' 'kg' 'ku' 'kx' 'kz' 'ld' 'nb' 'ni' 'o3' 'o6' 'od' 'om' 'p3' 'p4' 'pn' 'pu' 'q7' 'qd' 'qh' 'qp' 'qq' 'qr' 'qv' 'qy' 'qz' 'rq' 'sb' 'se' 'sf' 'sh' 'sm' 't0' 'tg' 'tj' 'tl' 'tq' 'ua' 'uk' 'us' 'wd' 'we' 'wj' 'wn' 'wq' 'wv' 'wz' 'yd' 'yg' 'ym' 'yn' 'yr' 'zc' 'zz' - f | 'ac' 'al' 'am' 'ap' 'ar' 'av' 'c1' 'cl' 'cz' 'dm' 'dp' 'dt' 'du' 'e0' 'es' 'eu' 'ev' 'ez' 'fa' 'fc' 'fj' 'fw' 'fz' 'gm' 'gx' 'ha' 'i6' 'i7' 'if' 'iz' 'j0' 'jd' 'jx' 'k0' 'kr' 'mj' 'mk' 'mp' 'n4' 'nm' 'om' 'ot' 'pl' 'px' 'qa' 'qb' 'qg' 'qk' 'qn' 'qo' 'qr' 'qu' 'r7' 'rc' 'rk' 's3' 's4' 'se' 'ss' 'su' 't8' 'ta' 'te' 'tj' 'tk' 'ts' 'tz' 'ub' 'v2' 'wb' 'wd' 'we' 'wf' 'wj' 'wq' 'wr' 'ws' 'wv' 'wx' 'y6' 'ym' 'yn' 'yq' 'ys' 'yv' 'zt' - f | '20' 'ag' 'az' 'b9' 'bg' 'bh' 'c2' 'dd' 'di' 'do' 'dw' 'e1' 'e3' 'e8' 'eb' 'ev' 'fc' 'fj' 'fu' 'fw' 'fx' 'gb' 'gf' 'hu' 'hw' 'hz' 'ih' 'ik' 'iq' 'jc' 'jq' 'ka' 'kf' 'kh' 'kw' 'ky' 'ly' 'lz' 'mc' 'n8' 'o3' 'o7' 'oa' 'os' 'ou' 'pf' 'q5' 'q6' 'qg' 'ql' 'qo' 'qy' 'r2' 'rf' 'rp' 'ru' 'ry' 's1' 's5' 'se' 'si' 'sn' 'su' 'ta' 'tn' 'tu' 'tv' 'tw' 'u9' 'ua' 'uh' 'uu' 'vo' 'w8' 'wa' 'wc' 'we' 'wi' 'wu' 'ww' 'wy' 'x3' 'xl' 'yb' 'yg' 'yk' 'yv' - f | '5f' 'ac' 'ad' 'aj' 'av' 'bg' 'ch' 'ci' 'dg' 'di' 'dl' 'dx' 'ep' 'ew' 'f8' 'fp' 'fr' 'fs' 'g4' 'g6' 'gb' 'h5' 'he' 'hk' 'hx' 'i1' 'ic' 'im' 'ji' 'jm' 'js' 'k9' 'kq' 'lf' 'ln' 'mj' 'n2' 'ni' 'no' 'o5' 'oi' 'ot' 'oy' 'pa' 'pj' 'qk' 'qq' 'qt' 'qu' 'r4' 'r8' 'rb' 'rh' 'rj' 'rm' 'ro' 'rp' 'rq' 'rt' 'ru' 'sb' 'th' 'tu' 'tz' 'u9' 'un' 'uo' 'us' 've' 'vi' 'vq' 'vx' 'wf' 'wg' 'wi' 'wq' 'wr' 'wt' 'x5' 'xz' 'y6' 'yd' 'yo' 'yq' 'yx' 'z3' 'zl' - f | 'a5' 'av' 'dc' 'dm' 'dt' 'dw' 'e2' 'ea' 'en' 'et' 'ez' 'f2' 'fg' 'fn' 'fv' 'gn' 'gu' 'h5' 'ht' 'i2' 'ie' 'ik' 'il' 'in' 'it' 'iv' 'iy' 'ji' 'jj' 'ju' 'kf' 'kn' 'ko' 'ku' 'l6' 'ls' 'nz' 'o2' 'o4' 'of' 'on' 'op' 'p7' 'q0' 'q2' 'qa' 'qt' 'r1' 'r9' 'ri' 'rq' 'rv' 'se' 'sp' 'sx' 't0' 't2' 't9' 'te' 'tq' 'tu' 'u8' 'uc' 'ue' 'ug' 'ui' 'ut' 'uu' 'v0' 'v7' 'vs' 'w0' 'wc' 'wh' 'wn' 'wo' 'ws' 'y1' 'y3' 'y5' 'ya' 'yc' 'yi' 'ys' 'yt' 'zf' 'zq' - f | 'ab' 'ag' 'aw' 'b4' 'd4' 'ds' 'e1' 'ea' 'eb' 'ee' 'ef' 'eh' 'ek' 'el' 'en' 'eu' 'ex' 'fv' 'fx' 'gi' 'gp' 'gt' 'i3' 'ie' 'jd' 'jk' 'jn' 'kv' 'l7' 'n8' 'ns' 'nx' 'o2' 'o3' 'oh' 'os' 'ow' 'p2' 'pr' 'pu' 'q3' 'qd' 'qe' 'qk' 'qq' 'qz' 'r9' 'rb' 'ri' 'rm' 'rr' 'rw' 'ry' 'sc' 'sj' 'sl' 'sx' 'tk' 'tr' 'tw' 'u4' 'ub' 'ud' 'uf' 'uh' 'un' 'uv' 'v6' 'vc' 'vf' 'vi' 'wg' 'wr' 'ws' 'wv' 'x7' 'xf' 'xo' 'y0' 'yi' 'yo' 'yt' 'yy' 'za' 'zm' 'zu' 'zx' - f | 'ad' 'af' 'aj' 'am' 'aq' 'ba' 'bo' 'c0' 'c5' 'cx' 'da' 'dc' 'dk' 'ed' 'ek' 'en' 'eo' 'ep' 'ew' 'fb' 'g4' 'gd' 'h2' 'hm' 'ho' 'hy' 'ib' 'if' 'io' 'ir' 'iv' 'iz' 'jc' 'jp' 'jt' 'kr' 'lo' 'me' 'na' 'nc' 'nh' 'o6' 'ok' 'oq' 'or' 'ow' 'pn' 'ps' 'q6' 'qa' 'qj' 'qo' 'qx' 'r2' 'r6' 'rd' 're' 'rg' 'rw' 'ta' 'tk' 'uj' 'uo' 'vn' 'wc' 'wh' 'wj' 'wl' 'wu' 'wv' 'ww' 'wx' 'xe' 'xl' 'xn' 'xo' 'xs' 'yb' 'yl' 'yp' 'yw' 'zg' 'zq' 'zr' 'zu' 'zx' 'zy' - f | 'am' 'as' 'b1' 'd3' 'db' 'dl' 'ea' 'ed' 'ee' 'el' 'em' 'ep' 'er' 'ew' 'ez' 'f4' 'fm' 'go' 'h9' 'he' 'hl' 'i0' 'ie' 'ii' 'iz' 'ji' 'kl' 'kn' 'lc' 'lr' 'm7' 'mb' 'mt' 'my' 'no' 'nu' 'oo' 'or' 'ov' 'ox' 'pe' 'pq' 'q7' 'qc' 'qd' 'qi' 'qj' 'qo' 'qr' 'qs' 'qv' 'qw' 'r0' 'r8' 're' 'rm' 'rn' 'rp' 's0' 'so' 'sp' 'sw' 'sy' 'te' 'tg' 'tl' 'tr' 'uq' 'uz' 'vk' 'wk' 'wl' 'wp' 'wq' 'wr' 'wu' 'ww' 'xs' 'y0' 'yb' 'yn' 'yr' 'ys' 'yt' 'yy' 'zh' 'zr' - f | '1v' '44' 'aj' 'al' 'am' 'ba' 'bi' 'br' 'bt' 'c0' 'd6' 'dg' 'di' 'e1' 'e4' 'ei' 'ek' 'er' 'et' 'eu' 'ev' 'ex' 'fa' 'fc' 'fe' 'fh' 'fr' 'ga' 'gh' 'gu' 'hp' 'hq' 'ib' 'if' 'io' 'ix' 'jo' 'jv' 'kd' 'l1' 'me' 'mh' 'mn' 'mo' 'mt' 'n7' 'nf' 'nk' 'oe' 'oy' 'pa' 'pg' 'pj' 'pm' 'pn' 'pq' 'py' 'q1' 'qd' 'qe' 'qi' 'qj' 'qr' 'qs' 'qz' 'rb' 'rs' 'su' 'sw' 't5' 'ti' 'tx' 'uc' 'ug' 'ui' 'uq' 'uz' 'v6' 'vp' 'we' 'wf' 'wj' 'wm' 'wx' 'xe' 'y7' 'yt' 'yy' - f | '1x' 'a2' 'ah' 'az' 'bb' 'be' 'bz' 'd1' 'dd' 'du' 'ee' 'eh' 'en' 'eu' 'ey' 'ez' 'fl' 'fs' 'g4' 'gm' 'gr' 'ha' 'hf' 'hg' 'hr' 'hw' 'i6' 'id' 'ij' 'j2' 'jv' 'k9' 'lg' 'm9' 'md' 'me' 'mg' 'mp' 'nd' 'nf' 'ng' 'nj' 'ny' 'nz' 'oj' 'os' 'pi' 'pj' 'pt' 'q9' 'qa' 'qf' 'qh' 'qk' 'qn' 'qq' 'qs' 'rz' 'sb' 't7' 't8' 'tw' 'ty' 'u2' 'u3' 'ua' 'ub' 'uy' 'uz' 'w1' 'w3' 'w5' 'wa' 'wc' 'we' 'wh' 'wp' 'wr' 'xd' 'xs' 'y3' 'yd' 'yk' 'yl' 'yo' 'ze' 'zh' 'zo' - f | '4n' '6a' 'a3' 'a5' 'aa' 'ae' 'ag' 'b9' 'ca' 'cf' 'd1' 'da' 'dr' 'dz' 'ee' 'el' 'et' 'ey' 'fj' 'fs' 'gl' 'hk' 'hl' 'hn' 'ie' 'ih' 'im' 'ix' 'j1' 'jr' 'kf' 'kk' 'lc' 'lk' 'lp' 'lx' 'mh' 'mt' 'mx' 'my' 'nr' 'nu' 'o6' 'og' 'oo' 'p4' 'p7' 'pj' 'pr' 'q3' 'qc' 'qd' 'qj' 'qk' 'ql' 'qp' 'qt' 'qv' 'qx' 'r8' 're' 'rm' 'rs' 'ru' 'rv' 'sp' 'sw' 'td' 'tk' 'to' 'tw' 'tz' 'u8' 'uf' 'vf' 'vw' 'w7' 'wq' 'wr' 'xe' 'ym' 'yo' 'yr' 'ys' 'yz' 'zc' 'zn' 'zs' - f | 'a3' 'ag' 'ar' 'au' 'ax' 'be' 'cy' 'd7' 'dd' 'do' 'e3' 'eb' 'eo' 'er' 'ev' 'ey' 'fp' 'gj' 'hk' 'hw' 'hy' 'if' 'ig' 'ii' 'in' 'io' 'iq' 'is' 'kh' 'll' 'n8' 'nd' 'np' 'nz' 'og' 'ot' 'ox' 'oy' 'pe' 'px' 'qa' 'qb' 'qe' 'qf' 'qh' 'qj' 'qm' 'qn' 'qt' 'qu' 'qw' 'qx' 'r6' 'r9' 'rb' 'rc' 'rd' 'rn' 'rq' 'se' 't7' 'tb' 'ti' 'tq' 'u0' 'u6' 'ub' 'ud' 'vd' 'vj' 'vl' 'vo' 'wd' 'wg' 'wh' 'wi' 'wk' 'wo' 'wv' 'wz' 'xy' 'y9' 'yl' 'ym' 'yo' 'yp' 'ys' 'yu' - f | 'ad' 'av' 'be' 'bi' 'bk' 'bu' 'ce' 'cv' 'd8' 'df' 'dh' 'du' 'e1' 'em' 'ep' 'ex' 'ey' 'f1' 'fd' 'fh' 'fp' 'gn' 'gu' 'h8' 'i8' 'ii' 'il' 'iz' 'j0' 'ji' 'jv' 'jz' 'k2' 'k9' 'ky' 'l9' 'lo' 'm6' 'mc' 'md' 'ng' 'nz' 'ot' 'p2' 'pa' 'pj' 'ps' 'pv' 'q5' 'qd' 'qf' 'qm' 'qq' 'qr' 'qt' 'qv' 'qx' 'rc' 'rh' 'rp' 'rq' 'rs' 'sb' 'sc' 'te' 'to' 'ts' 'tw' 'up' 'vn' 'vw' 'wa' 'wg' 'wh' 'wn' 'wp' 'wz' 'xc' 'xf' 'xt' 'y5' 'yh' 'yn' 'yy' 'yz' 'za' 'zh' 'zy' - f | 'aj' 'ak' 'aq' 'ba' 'bd' 'bj' 'bk' 'bx' 'cr' 'cx' 'd0' 'dj' 'dl' 'ef' 'ei' 'ej' 'ek' 'em' 'eq' 'er' 'f3' 'f7' 'fi' 'fp' 'gv' 'h7' 'ha' 'hc' 'hh' 'hk' 'hn' 'ia' 'id' 'im' 'iq' 'is' 'iw' 'ji' 'jk' 'js' 'ju' 'jx' 'l0' 'm1' 'mq' 'ng' 'o8' 'ot' 'ov' 'oy' 'p1' 'pu' 'pv' 'pz' 'qc' 'qh' 'qm' 'qq' 'qs' 'qt' 'qw' 'rc' 'rq' 'rt' 'sd' 't5' 'ta' 'tb' 'tj' 'tp' 'uk' 'un' 'v9' 'vt' 'wa' 'wd' 'wf' 'wm' 'wn' 'wv' 'wz' 'xp' 'ya' 'yg' 'ym' 'yn' 'yr' 'zi' - f | 'aj' 'as' 'aw' 'bc' 'bj' 'bm' 'cd' 'd7' 'd9' 'di' 'ef' 'er' 'es' 'ew' 'fi' 'ft' 'g2' 'gq' 'gx' 'h9' 'he' 'hg' 'hu' 'i8' 'ie' 'ik' 'im' 'iq' 'ix' 'iy' 'jf' 'ji' 'ka' 'kl' 'kt' 'la' 'lf' 'm8' 'm9' 'nh' 'nw' 'o7' 'oi' 'pi' 'ps' 'q6' 'qa' 'qb' 'qf' 'qm' 'qo' 'qq' 'qs' 'r9' 'rb' 'rf' 'rk' 'rx' 'sa' 'sb' 'sp' 't1' 'tb' 'tl' 'tm' 'u6' 'uc' 'um' 'un' 'ur' 'w3' 'wj' 'wu' 'xm' 'xr' 'xy' 'ya' 'yd' 'ye' 'yf' 'yj' 'yo' 'ys' 'zc' 'zg' 'zl' 'zv' 'zz' - f | '1g' '3e' 'a5' 'ae' 'ar' 'bt' 'cb' 'ck' 'co' 'd5' 'dh' 'dm' 'dr' 'dt' 'e0' 'e9' 'eh' 'el' 'em' 'f2' 'f3' 'fb' 'ff' 'fg' 'gu' 'hb' 'he' 'hr' 'hx' 'i7' 'ib' 'if' 'ig' 'io' 'iu' 'jc' 'jo' 'jv' 'kh' 'kx' 'lb' 'lh' 'lw' 'lx' 'ly' 'mp' 'oa' 'oc' 'oz' 'pa' 'q9' 'qi' 'qp' 'qq' 'qw' 'r3' 'rb' 'rd' 're' 'rn' 'ry' 'rz' 's5' 'su' 'sz' 'td' 'tf' 'tt' 'u9' 'uh' 'um' 'un' 'va' 'vh' 'vt' 'w7' 'wb' 'wc' 'wg' 'wh' 'wx' 'xd' 'xz' 'y8' 'yf' 'yg' 'yh' 'ym' 'zy' - f | '1t' 'am' 'aw' 'b4' 'bi' 'bm' 'bx' 'cd' 'cj' 'cv' 'e5' 'ek' 'eo' 'er' 'ex' 'f5' 'f8' 'fp' 'fr' 'fw' 'ga' 'gy' 'hm' 'hs' 'ip' 'ir' 'is' 'it' 'iv' 'iz' 'jf' 'jm' 'jp' 'ju' 'k6' 'kk' 'kr' 'lb' 'le' 'lt' 'mj' 'nk' 'np' 'nr' 'oa' 'oj' 'pm' 'pn' 'q1' 'q3' 'qb' 'qc' 'qf' 'qh' 'qi' 'qj' 'qq' 'qt' 'qw' 'qy' 'r2' 'r8' 'rf' 'rh' 'rw' 's9' 't3' 'te' 'ti' 'tn' 'tq' 'uu' 'uv' 'uw' 'w0' 'w1' 'w3' 'w7' 'wc' 'wi' 'wk' 'wn' 'wr' 'wt' 'xm' 'y5' 'ya' 'z7' 'zu' - f | '1f' '2j' '2q' '5c' 'am' 'ap' 'aq' 'az' 'bu' 'bx' 'cr' 'e0' 'e7' 'ea' 'eb' 'ec' 'eg' 'ew' 'ey' 'fh' 'fk' 'g2' 'go' 'h4' 'h8' 'ha' 'hb' 'hm' 'ia' 'ic' 'ie' 'ik' 'iu' 'iv' 'j7' 'ja' 'k9' 'kw' 'lp' 'ly' 'mb' 'ns' 'ny' 'o0' 'of' 'ok' 'oz' 'pe' 'pr' 'qb' 'qd' 'qg' 'qt' 'qx' 'qy' 'rg' 'ro' 'rq' 'rz' 'sh' 'si' 'sl' 'sm' 'td' 'tg' 'tx' 'uh' 'um' 'up' 'uw' 've' 'vl' 'vp' 'vw' 'w2' 'w5' 'wc' 'wf' 'wi' 'wn' 'wq' 'ws' 'wv' 'wy' 'wz' 'x1' 'xr' 'y1' 'zs' 'zz' - f | '1q' '2n' '4p' '8n' 'a3' 'aa' 'bn' 'bq' 'bu' 'cg' 'db' 'dl' 'dx' 'dy' 'e2' 'e3' 'e4' 'ea' 'eq' 'ff' 'fk' 'fn' 'fv' 'gf' 'gh' 'h1' 'hs' 'hu' 'ij' 'in' 'iv' 'kc' 'ke' 'ln' 'm2' 'mc' 'mf' 'o2' 'og' 'oo' 'ov' 'p8' 'pl' 'pm' 'ps' 'pv' 'pz' 'qa' 'qc' 'qe' 'qh' 'qi' 'qj' 'qm' 'qo' 'qq' 'qv' 'qw' 'qy' 're' 'rz' 'sd' 'si' 'sj' 'sp' 'su' 'tc' 'tk' 'tn' 'tq' 'u7' 'ut' 'uv' 'v5' 'vr' 'wb' 'we' 'wm' 'ws' 'wv' 'xc' 'xy' 'y9' 'yj' 'yq' 'yv' 'yw' 'zb' 'zw' 'zz' - f | '2t' '4d' 'bb' 'bd' 'cg' 'co' 'd6' 'db' 'dg' 'dn' 'do' 'dy' 'e1' 'e3' 'e6' 'ec' 'em' 'en' 'ep' 'es' 'ev' 'ew' 'fu' 'fz' 'gc' 'gt' 'hg' 'hl' 'hy' 'id' 'in' 'io' 'ir' 'iu' 'iw' 'jl' 'jo' 'jp' 'jw' 'k4' 'ke' 'ku' 'ld' 'lg' 'li' 'lj' 'lr' 'ls' 'm7' 'n8' 'o6' 'oi' 'op' 'oy' 'pi' 'pq' 'qk' 'qm' 'qn' 'qp' 'qr' 'qu' 'qv' 'qz' 'r3' 're' 'rj' 'rk' 'rq' 'ru' 'rw' 'rz' 'sh' 'si' 'ta' 'to' 'uq' 'ux' 'vc' 'vr' 'wb' 'wh' 'wn' 'wo' 'wy' 'xg' 'y7' 'yb' 'yd' 'yo' - f | '4w' 'ad' 'ak' 'as' 'b3' 'bd' 'cy' 'dt' 'du' 'dy' 'e2' 'e4' 'en' 'es' 'et' 'ew' 'ex' 'f2' 'fl' 'fr' 'ft' 'gv' 'h1' 'ha' 'hd' 'hh' 'hs' 'hv' 'ic' 'io' 'ja' 'jm' 'k2' 'ku' 'kw' 'ld' 'ls' 'lx' 'ma' 'nu' 'ny' 'ov' 'pm' 'ps' 'q6' 'qa' 'qd' 'qo' 'qs' 'qv' 'qy' 'ra' 'rs' 'rt' 'rv' 'rw' 's8' 'sa' 'sw' 'tj' 'ug' 'ui' 'ur' 'ut' 'ux' 'v6' 'vt' 'w4' 'w7' 'wa' 'wb' 'wc' 'wg' 'wh' 'wi' 'wt' 'wu' 'wy' 'wz' 'x2' 'xb' 'xm' 'yg' 'yh' 'yi' 'yu' 'yy' 'z5' 'zg' 'zo' - f | '97' 'ac' 'ah' 'bn' 'c8' 'dl' 'ds' 'dw' 'e2' 'eb' 'eg' 'ej' 'ep' 'eu' 'f1' 'fe' 'g9' 'gs' 'h0' 'ha' 'he' 'hh' 'hy' 'i0' 'i8' 'i9' 'ia' 'ij' 'ip' 'jr' 'jt' 'jw' 'kq' 'lo' 'lq' 'm7' 'me' 'na' 'ns' 'o1' 'od' 'oe' 'oi' 'om' 'oz' 'pc' 'pj' 'pk' 'qh' 'qi' 'qj' 'ql' 'qo' 'qq' 'qr' 'qs' 'qt' 'qw' 'qz' 'rc' 're' 'rh' 'ri' 'rr' 'sz' 'tc' 'tf' 'tk' 'tl' 'ue' 'uh' 'uj' 'un' 'uz' 'vq' 'vr' 'vv' 'w3' 'w4' 'we' 'wk' 'wt' 'wx' 'xa' 'xh' 'xl' 'yg' 'yi' 'yj' 'ym' - f | 'ae' 'av' 'bj' 'bk' 'c3' 'd3' 'dj' 'eb' 'ed' 'ef' 'ei' 'ej' 'en' 'ep' 'fl' 'fp' 'fr' 'fu' 'fz' 'g8' 'gd' 'h6' 'ht' 'hu' 'i7' 'if' 'im' 'j0' 'j1' 'je' 'jx' 'ku' 'l6' 'l7' 'll' 'lp' 'mc' 'ns' 'o4' 'oi' 'op' 'oz' 'q4' 'qa' 'qf' 'qh' 'ql' 'qn' 'qv' 'r0' 'r1' 'r4' 'rh' 'rj' 'rw' 'sd' 'sn' 'so' 't6' 'ti' 'tw' 'uc' 'uf' 'uk' 'ul' 'uw' 'v3' 'v8' 'vd' 'vr' 'w7' 'w9' 'wa' 'wb' 'wg' 'wh' 'wk' 'wl' 'wn' 'wo' 'wz' 'xf' 'xg' 'xo' 'xv' 'y2' 'yl' 'zr' 'zs' 'zw' - f | '4k' 'af' 'ah' 'b7' 'bj' 'by' 'ci' 'df' 'ds' 'eh' 'em' 'eq' 'ey' 'ez' 'fu' 'fv' 'g1' 'ga' 'go' 'gs' 'gy' 'gz' 'ho' 'i0' 'ie' 'ir' 'iv' 'k5' 'kj' 'ks' 'kw' 'l4' 'li' 'm0' 'mo' 'o8' 'ol' 'oq' 'os' 'ox' 'pk' 'pl' 'pq' 'q3' 'q6' 'qa' 'qg' 'qj' 'qn' 'qq' 'qr' 'qu' 'r5' 'r9' 'rb' 'ri' 'rl' 'rs' 'ry' 's7' 's9' 'sa' 'sd' 'sx' 'ta' 'tc' 'td' 'tf' 'tg' 'th' 'ti' 'to' 'tq' 'tv' 'uo' 'ut' 've' 'vt' 'w8' 'wg' 'wm' 'ws' 'wy' 'xy' 'y8' 'ya' 'yc' 'yf' 'ys' 'yu' 'zy' - f | 'aa' 'av' 'ay' 'bn' 'by' 'cm' 'da' 'dd' 'dj' 'dk' 'dr' 'dz' 'eu' 'ev' 'ez' 'f1' 'f8' 'gr' 'he' 'hp' 'hs' 'hw' 'iz' 'k1' 'kc' 'km' 'ko' 'kt' 'ln' 'ls' 'mx' 'n3' 'nl' 'oe' 'oj' 'om' 'os' 'oy' 'pg' 'pr' 'pt' 'pv' 'qa' 'qc' 'qe' 'qi' 'qj' 'qm' 'qn' 'qq' 'qu' 'qx' 'qz' 'r0' 'r1' 'rf' 'rs' 'rx' 'ry' 's4' 'sg' 't2' 'tq' 'tt' 'tv' 'ty' 'ua' 'uj' 'uq' 'vi' 'vk' 'vx' 'wc' 'we' 'wi' 'wl' 'wo' 'wp' 'wv' 'ww' 'xi' 'xr' 'xs' 'y9' 'yg' 'ym' 'yr' 'yt' 'yz' 'zo' 'zw' - f | 'ag' 'bm' 'co' 'cr' 'd7' 'dk' 'do' 'e6' 'e9' 'eb' 'ed' 'ef' 'eh' 'ep' 'eq' 'ew' 'f4' 'fd' 'fr' 'gc' 'gg' 'gq' 'gt' 'gv' 'h0' 'h4' 'hi' 'hm' 'i4' 'ib' 'im' 'is' 'j0' 'jc' 'jd' 'jo' 'ka' 'kk' 'kx' 'm3' 'nj' 'nr' 'nu' 'o2' 'ob' 'oe' 'oi' 'om' 'ow' 'oz' 'p3' 'pd' 'pf' 'ph' 'qi' 'qj' 'qu' 'qw' 'qy' 'r2' 'ra' 'rg' 'rj' 'rm' 'rn' 'ro' 'rp' 'rr' 'ry' 'rz' 's3' 'sa' 'so' 't6' 't7' 'ta' 'tc' 'u3' 'ub' 'vj' 'vm' 'vn' 'w7' 'wb' 'wf' 'wk' 'wn' 'x2' 'yi' 'zf' 'zh' - f | 'av' 'aw' 'bl' 'bt' 'cj' 'cx' 'df' 'ea' 'ed' 'ee' 'ef' 'ew' 'f5' 'fq' 'ft' 'fu' 'g6' 'gl' 'gs' 'ha' 'hj' 'hr' 'i8' 'ia' 'ic' 'ir' 'iz' 'j0' 'j5' 'jp' 'ju' 'k6' 'ki' 'lk' 'lz' 'mh' 'nl' 'o4' 'ob' 'oo' 'op' 'pf' 'po' 'pq' 'pr' 'q2' 'q3' 'q6' 'qd' 'qj' 'qk' 'qn' 'qt' 'qw' 'ro' 'rr' 'ru' 'rv' 'sc' 'sw' 'sy' 'sz' 't2' 'tb' 'tg' 'tl' 'tp' 'tv' 'u4' 'u9' 'um' 'uu' 'vj' 'vt' 'vu' 'wg' 'wh' 'wj' 'wo' 'wq' 'wt' 'xd' 'y1' 'yc' 'ye' 'yl' 'yw' 'zb' 'zd' 'zm' 'zr' - f | '41' '7r' 'a2' 'ad' 'aj' 'ak' 'ao' 'as' 'b1' 'b2' 'cn' 'db' 'eb' 'ec' 'ee' 'em' 'ev' 'ex' 'f8' 'fa' 'fo' 'fs' 'fu' 'gh' 'gk' 'gr' 'gx' 'iq' 'iv' 'j9' 'jb' 'jc' 'jr' 'kf' 'lg' 'm1' 'mq' 'mt' 'ne' 'nv' 'o5' 'od' 'os' 'ox' 'pc' 'pj' 'pq' 'q1' 'q6' 'q7' 'q8' 'qk' 'qm' 'qn' 'qp' 'qs' 'rj' 'rk' 's5' 'sb' 'sk' 'sn' 'sv' 't4' 'tb' 'th' 'ti' 'uc' 'uj' 'ut' 'vh' 'vp' 'wg' 'wi' 'wk' 'wp' 'ws' 'wv' 'wx' 'wy' 'x6' 'xa' 'xg' 'xr' 'xy' 'y6' 'yj' 'yo' 'yv' 'yw' 'zt' 'zw' - f | '5z' 'ad' 'al' 'ax' 'b4' 'bx' 'd7' 'da' 'dp' 'dz' 'e3' 'ef' 'en' 'es' 'et' 'ev' 'ex' 'f6' 'fj' 'fr' 'ft' 'fv' 'h9' 'hn' 'hx' 'i0' 'ii' 'it' 'jf' 'jk' 'jl' 'ka' 'kr' 'l9' 'lf' 'm3' 'mm' 'nc' 'nd' 'nr' 'o8' 'oe' 'ok' 'om' 'pd' 'ph' 'pz' 'qd' 'qf' 'qo' 'qp' 'qw' 'qx' 'qy' 'r6' 'rc' 'rg' 'rw' 'rx' 's8' 'se' 't3' 'tb' 'tc' 'ti' 'tl' 'to' 'tw' 'ub' 'ue' 'uf' 'uy' 'vf' 'vh' 'vr' 'w0' 'w9' 'wd' 'wf' 'wh' 'wp' 'wu' 'x0' 'x8' 'y2' 'ye' 'yq' 'ys' 'yt' 'z6' 'zf' 'zp' - f | '7u' '7z' 'a0' 'a5' 'c6' 'cj' 'cx' 'do' 'e4' 'eg' 'eh' 'eq' 'ey' 'ez' 'fe' 'fu' 'gk' 'gy' 'h9' 'ih' 'ik' 'il' 'j0' 'jt' 'jy' 'k1' 'k5' 'ke' 'lp' 'lx' 'ly' 'ma' 'mj' 'mm' 'mt' 'nb' 'nm' 'o3' 'o5' 'of' 'oi' 'ov' 'p2' 'pf' 'ph' 'pt' 'q8' 'qj' 'qk' 'qp' 'qs' 'r1' 'r8' 'rl' 'rp' 'rt' 's5' 'sb' 'sh' 'sk' 'so' 'sr' 'su' 'tc' 'tn' 'tr' 'tx' 'u4' 'uh' 'ul' 'up' 'ur' 'uw' 'uy' 'vm' 'w5' 'wb' 'wj' 'wm' 'wq' 'wy' 'x6' 'xq' 'xs' 'y8' 'y9' 'yi' 'yj' 'yv' 'yx' 'zi' 'zy' - f | 'a7' 'a9' 'ab' 'af' 'ah' 'bc' 'bg' 'bi' 'c2' 'cj' 'cl' 'dj' 'dn' 'do' 'dv' 'dw' 'e5' 'ec' 'es' 'f4' 'fa' 'fk' 'fv' 'h9' 'hi' 'hu' 'i5' 'ic' 'ig' 'im' 'ir' 'ji' 'jt' 'k2' 'kc' 'kd' 'ki' 'km' 'kz' 'l3' 'lh' 'li' 'lv' 'ml' 'ne' 'ni' 'nt' 'nx' 'o9' 'pm' 'pn' 'pr' 'px' 'q0' 'qa' 'qe' 'qj' 'r6' 'ra' 'rj' 'rn' 'rq' 'ru' 'ry' 's0' 'sq' 'tb' 'to' 'tw' 'ua' 'ub' 'uf' 'ui' 'un' 'uu' 'vi' 'vo' 'vv' 'w5' 'w7' 'w8' 'wo' 'wv' 'wy' 'xy' 'y2' 'ya' 'yh' 'yj' 'yo' 'za' 'zh' - f | '0h' '42' 'ak' 'al' 'bf' 'bz' 'co' 'd3' 'dc' 'dt' 'dy' 'ed' 'ee' 'eg' 'ev' 'ew' 'fg' 'fr' 'fz' 'gi' 'gy' 'ha' 'hb' 'hd' 'hl' 'hn' 'hs' 'ht' 'ij' 'io' 'iv' 'jh' 'jl' 'jr' 'kc' 'kj' 'kt' 'ku' 'ky' 'ln' 'ml' 'mu' 'ng' 'nm' 'o0' 'o1' 'of' 'oh' 'or' 'p8' 'q8' 'qb' 'qp' 'qr' 'qt' 'qu' 'qv' 'r8' 'rj' 'rl' 'rp' 'rw' 'sk' 'ss' 'sw' 't9' 'th' 'tl' 'u5' 'uj' 'us' 'vg' 'vt' 'vz' 'w4' 'wb' 'we' 'wl' 'ww' 'wx' 'wy' 'wz' 'xj' 'yb' 'yg' 'yn' 'yp' 'ys' 'yw' 'yz' 'zk' 'zp' 'zu' - f | '2s' 'a7' 'ae' 'am' 'bx' 'd5' 'de' 'do' 'ds' 'dt' 'e1' 'e3' 'e7' 'ed' 'ee' 'eg' 'ek' 'em' 'es' 'ev' 'fi' 'fw' 'g2' 'gf' 'gs' 'gu' 'gy' 'h7' 'hh' 'hi' 'hm' 'hu' 'ih' 'in' 'io' 'jr' 'js' 'jw' 'k0' 'k5' 'kb' 'l4' 'lu' 'm6' 'm8' 'mc' 'nb' 'od' 'ox' 'pc' 'pg' 'pv' 'py' 'q5' 'qc' 'qe' 'qf' 'qh' 'ql' 'qm' 'qo' 'qq' 'ra' 'rk' 'ro' 'rp' 'rr' 'ru' 'rw' 'se' 'sh' 't9' 'to' 'tq' 'tt' 'tw' 'u1' 'u4' 'ui' 'un' 'uq' 'vr' 'vy' 'vz' 'wh' 'wp' 'ws' 'wt' 'wy' 'xt' 'y8' 'yd' 'zp' - f | '3g' 'af' 'aj' 'ak' 'ap' 'as' 'au' 'cp' 'cx' 'dh' 'dn' 'dr' 'ds' 'ej' 'en' 'eo' 'et' 'eu' 'ex' 'fq' 'fs' 'ft' 'gw' 'h5' 'i5' 'ix' 'j6' 'jc' 'jg' 'k1' 'kb' 'kh' 'kn' 'kv' 'lj' 'lu' 'mc' 'mi' 'na' 'nq' 'ns' 'o4' 'o7' 'of' 'q5' 'q7' 'qe' 'qh' 'qi' 'qk' 'qr' 'qu' 'qw' 'r9' 'rb' 'ri' 'rx' 's3' 'sf' 'sm' 'so' 'sq' 'ss' 'su' 'sy' 't7' 'ta' 'ti' 'tn' 'tr' 'tx' 'u1' 'u3' 'ue' 'uh' 'up' 'uw' 'uy' 'va' 'w6' 'wg' 'wm' 'wp' 'ws' 'wy' 'x1' 'y4' 'y6' 'yk' 'ys' 'yy' 'zf' 'zv' - f | 'a4' 'ae' 'ax' 'bb' 'bg' 'ca' 'ch' 'cq' 'cv' 'dm' 'dn' 'en' 'ep' 'eu' 'ev' 'f0' 'g3' 'gk' 'gm' 'hd' 'ho' 'hp' 'hy' 'ij' 'im' 'iy' 'jl' 'jr' 'jy' 'kj' 'kt' 'ku' 'lp' 'mo' 'mr' 'mz' 'n4' 'nk' 'oc' 'ol' 'oo' 'os' 'oy' 'oz' 'p8' 'p9' 'ps' 'qb' 'qd' 'qg' 'qi' 'qv' 'qx' 'r1' 'ra' 'rf' 'rg' 'rm' 'ro' 'rr' 'rv' 'rz' 's7' 'sm' 'ss' 'tl' 'tr' 'tu' 'ty' 'u5' 'ui' 'un' 'uq' 'uv' 'vn' 'w1' 'w2' 'w6' 'wd' 'we' 'wg' 'wn' 'wp' 'wy' 'y2' 'y6' 'yc' 'yd' 'yt' 'yw' 'z8' 'ze' 'zs' - f | 'ai' 'cg' 'cs' 'dc' 'dg' 'di' 'dj' 'dk' 'dp' 'du' 'eb' 'ec' 'ee' 'ei' 'ek' 'eu' 'f2' 'fh' 'fm' 'fy' 'hc' 'hm' 'i2' 'ia' 'jj' 'ke' 'kl' 'lb' 'lc' 'ln' 'me' 'nc' 'nf' 'o6' 'oc' 'ok' 'os' 'pc' 'po' 'px' 'q2' 'q8' 'qa' 'qb' 'qd' 'qe' 'qg' 'qi' 'qo' 'qs' 'qt' 'qv' 'qx' 'r4' 'r9' 'ri' 'rn' 'rq' 'rr' 'rz' 's9' 'sf' 'sr' 'su' 'sw' 'sy' 'sz' 't2' 'ti' 'tv' 'ud' 'uv' 'wa' 'wc' 'wi' 'wk' 'wm' 'ws' 'wu' 'wv' 'wz' 'x2' 'xa' 'xf' 'y8' 'yb' 'yd' 'yl' 'yp' 'yr' 'ys' 'z5' 'zh' - f | '1a' 'a0' 'ae' 'av' 'be' 'bj' 'bv' 'bx' 'bz' 'ck' 'd4' 'do' 'ds' 'du' 'ee' 'eu' 'fg' 'fm' 'fy' 'g1' 'gh' 'h8' 'h9' 'ha' 'he' 'hl' 'hy' 'i6' 'ic' 'io' 'jb' 'jk' 'jq' 'k1' 'kf' 'km' 'kv' 'l8' 'n0' 'n3' 'oh' 'oz' 'po' 'q9' 'qg' 'qh' 'qi' 'ql' 'qp' 'qr' 'qu' 'qw' 'qx' 'rb' 're' 'rf' 'ri' 'rj' 'rv' 'sj' 'sm' 'sz' 't1' 'tl' 'tm' 'tw' 'tz' 'ua' 'uc' 'ug' 'ut' 'uv' 'ux' 'v7' 'wa' 'wc' 'wf' 'wi' 'wl' 'wp' 'wq' 'wr' 'x3' 'x6' 'xk' 'xx' 'y4' 'yp' 'yr' 'yw' 'yy' 'ze' 'zk' 'zy' - f | '1z' 'aq' 'cf' 'cl' 'e1' 'e5' 'ee' 'eg' 'eo' 'er' 'ff' 'fh' 'fx' 'g5' 'ga' 'gd' 'gm' 'gn' 'hc' 'hf' 'hi' 'hk' 'ho' 'ib' 'ik' 'in' 'iw' 'ix' 'jd' 'kl' 'ky' 'lp' 'm3' 'm5' 'n4' 'ng' 'o0' 'oc' 'oj' 'ot' 'ou' 'p2' 'pa' 'pd' 'pg' 'ps' 'pw' 'q1' 'q8' 'qb' 'qc' 'qd' 'qi' 'qr' 'qs' 'qu' 'qv' 'ra' 'rg' 'rt' 'rz' 's7' 'sm' 'sn' 't0' 'tb' 'th' 'tx' 'tz' 'ub' 'ud' 'ue' 'un' 'ur' 'ut' 'vb' 'vj' 'wg' 'wh' 'wj' 'wl' 'wn' 'ww' 'wz' 'x0' 'xc' 'xq' 'xr' 'xw' 'y8' 'y9' 'yr' 'z7' 'zt' - f | '27' '3p' '6r' 'a8' 'an' 'ap' 'au' 'ay' 'bi' 'c6' 'cy' 'dh' 'ds' 'eg' 'eh' 'ej' 'es' 'eu' 'fm' 'fp' 'fq' 'fx' 'g2' 'gg' 'gv' 'hz' 'ij' 'il' 'iq' 'iw' 'j8' 'j9' 'k3' 'kr' 'lq' 'm7' 'm9' 'mj' 'n0' 'n6' 'nr' 'nx' 'ox' 'pq' 'qc' 'qd' 'qe' 'qg' 'qh' 'qj' 'qk' 'qm' 'qn' 'qr' 'qu' 'qv' 'qw' 'qx' 're' 'rg' 'rr' 'rz' 's1' 'sj' 'sl' 'sy' 't4' 't7' 'tb' 'tr' 'uc' 'un' 'uq' 'ut' 'vf' 'w7' 'w9' 'wg' 'wh' 'wp' 'wq' 'ws' 'wx' 'x8' 'xp' 'xy' 'y0' 'y1' 'ya' 'yu' 'yz' 'z2' 'z5' 'zz' - f | '3k' 'ag' 'ak' 'bi' 'bl' 'bw' 'by' 'ch' 'cm' 'dw' 'e1' 'e2' 'ed' 'ej' 'ek' 'er' 'eu' 'ez' 'f8' 'fd' 'fi' 'fl' 'gi' 'gm' 'gx' 'gy' 'h2' 'h8' 'hl' 'hn' 'ij' 'ip' 'iq' 'it' 'jb' 'jl' 'jn' 'k7' 'kh' 'kl' 'kn' 'kt' 'nh' 'nk' 'pa' 'pe' 'pg' 'pp' 'q5' 'q7' 'qi' 'qk' 'ql' 'qs' 'r8' 'ri' 'rj' 'rl' 'rw' 'rx' 'so' 'tb' 'tj' 'tm' 'to' 'tu' 'tv' 'tz' 'u4' 'ue' 'ul' 'uv' 'v1' 'vj' 'vy' 'wc' 'wr' 'wt' 'wx' 'wz' 'xh' 'xj' 'xp' 'xt' 'y4' 'yb' 'yf' 'ym' 'yo' 'yq' 'yy' 'zp' 'zq' 'zt' - f | '9a' 'aa' 'bo' 'br' 'bv' 'bz' 'c1' 'c7' 'cz' 'db' 'dh' 'dj' 'e4' 'e8' 'eh' 'ew' 'gd' 'gg' 'gp' 'gu' 'gx' 'hc' 'ho' 'ht' 'hv' 'ia' 'ii' 'ij' 'ir' 'iw' 'j5' 'jk' 'jv' 'la' 'lc' 'lp' 'lt' 'lw' 'na' 'nt' 'nv' 'nw' 'o8' 'o9' 'ou' 'oy' 'p0' 'pc' 'pq' 'py' 'q4' 'q8' 'qi' 'qk' 'qn' 'qr' 'qt' 'qw' 'r5' 'r8' 're' 'rj' 'rt' 'rv' 'ry' 'rz' 's8' 'sg' 'sv' 'ta' 'td' 'tl' 'tm' 'ul' 'up' 'ut' 'uz' 'vf' 'w1' 'wb' 'wc' 'wd' 'wf' 'wi' 'wl' 'wu' 'x8' 'xg' 'xm' 'y1' 'yl' 'yr' 'ys' 'z8' - f | 'a3' 'a5' 'ai' 'av' 'ay' 'c3' 'cp' 'd3' 'dn' 'du' 'dz' 'e9' 'ed' 'ei' 'ek' 'em' 'eo' 'ev' 'f2' 'gh' 'gl' 'gy' 'h6' 'h8' 'hb' 'he' 'hg' 'hm' 'hp' 'hy' 'hz' 'id' 'ie' 'it' 'ix' 'j2' 'ja' 'jc' 'jf' 'k5' 'ki' 'kk' 'kv' 'l4' 'l6' 'ls' 'm9' 'mg' 'mt' 'nb' 'ng' 'oh' 'ot' 'oz' 'pw' 'py' 'pz' 'q0' 'q3' 'q8' 'qb' 'qh' 'qj' 'qk' 'ql' 'qp' 'qq' 'qr' 'qt' 'qw' 'rb' 'rz' 's3' 'sl' 'tb' 'u0' 'u5' 'uo' 'us' 'vc' 'wg' 'wh' 'wj' 'wr' 'wt' 'wu' 'wy' 'x1' 'y1' 'yh' 'z7' 'zf' 'zs' 'zu' - f | 'af' 'aq' 'at' 'bh' 'cc' 'd3' 'dd' 'dg' 'dq' 'e1' 'e6' 'ec' 'ee' 'ex' 'ey' 'go' 'gt' 'h3' 'h8' 'hd' 'he' 'hm' 'i7' 'ii' 'ik' 'io' 'ip' 'it' 'iz' 'jg' 'k0' 'k5' 'k7' 'kb' 'ks' 'kw' 'ln' 'lp' 'lx' 'm3' 'nf' 'nh' 'of' 'og' 'oj' 'ok' 'ow' 'p7' 'pd' 'pj' 'pm' 'q1' 'q7' 'qg' 'qh' 'qk' 'qq' 'qr' 'qt' 'qw' 'rr' 'rs' 'ry' 's4' 's7' 'sd' 'si' 'sl' 'sp' 'sw' 'sz' 't6' 't8' 'tc' 'u2' 'um' 'uo' 'ux' 'vb' 'w2' 'wb' 'wi' 'wj' 'ws' 'wt' 'wx' 'x3' 'xf' 'xt' 'yb' 'ym' 'zb' 'ze' 'zm' - f | '1s' 'a0' 'ac' 'aj' 'am' 'ao' 'aw' 'bi' 'bm' 'by' 'ca' 'cu' 'dc' 'di' 'dp' 'e0' 'e9' 'eg' 'eq' 'fh' 'fi' 'gc' 'gi' 'gq' 'gx' 'h4' 'he' 'hk' 'i2' 'ix' 'jc' 'jl' 'jm' 'k1' 'kf' 'kg' 'kn' 'kq' 'la' 'nj' 'nw' 'o9' 'og' 'p0' 'p6' 'pj' 'pn' 'pp' 'q4' 'qf' 'ql' 'qm' 'qs' 'qt' 'qu' 'qz' 'r5' 'r6' 'ri' 'ro' 'rw' 'sv' 'sx' 't5' 'th' 'tl' 'tp' 'u0' 'ue' 'uf' 'ug' 'un' 'uq' 'vx' 'wd' 'we' 'wg' 'wj' 'wk' 'wp' 'wr' 'ws' 'wt' 'x2' 'xq' 'xt' 'xw' 'xx' 'ya' 'yk' 'yl' 'yr' 'yt' 'za' 'zh' - f | '2o' 'af' 'ah' 'aj' 'al' 'ap' 'as' 'bc' 'd2' 'di' 'dm' 'do' 'ds' 'dt' 'dy' 'eb' 'ed' 'ee' 'ej' 'es' 'ev' 'ex' 'ey' 'gs' 'hk' 'hr' 'hw' 'hz' 'ig' 'is' 'jp' 'jv' 'jx' 'k4' 'kh' 'kv' 'lb' 'll' 'lw' 'na' 'no' 'o1' 'o8' 'oh' 'oo' 'op' 'pb' 'po' 'q2' 'q4' 'q7' 'qa' 'qc' 'qe' 'qg' 'qm' 'qo' 'qt' 'qw' 'qy' 'qz' 'r4' 'r7' 'r8' 'rp' 'rs' 's2' 'sb' 'sg' 'sl' 't1' 'tj' 'to' 'ts' 'tx' 'u5' 'uz' 'v6' 'w4' 'w5' 'we' 'wh' 'wn' 'wr' 'wu' 'wv' 'x1' 'y1' 'y8' 'ya' 'yk' 'ym' 'yw' 'z5' 'zx' - f | '2s' '4q' '8x' 'aa' 'ac' 'ah' 'av' 'aw' 'c4' 'c5' 'ce' 'd1' 'dj' 'dq' 'e7' 'e9' 'ea' 'ef' 'fc' 'fs' 'fz' 'gb' 'ge' 'gx' 'hf' 'hl' 'hm' 'i5' 'i7' 'ie' 'ii' 'iw' 'kf' 'ko' 'kx' 'lb' 'm6' 'mh' 'ob' 'od' 'oe' 'og' 'oq' 'or' 'pn' 'ps' 'pu' 'q4' 'qf' 'qg' 'qj' 'ql' 'qn' 'qt' 'qy' 'r3' 'ra' 'ri' 'rj' 'rv' 'sr' 'tg' 'th' 'tl' 'tx' 'u7' 'ub' 'uh' 'uq' 'uu' 'uy' 'vn' 'vt' 'wa' 'we' 'wh' 'wn' 'wp' 'wr' 'ww' 'xg' 'xk' 'xs' 'ya' 'yc' 'yd' 'yf' 'yl' 'ym' 'ys' 'z6' 'zj' 'zm' 'zv' 'zw' - f | '4d' '5a' 'ab' 'ae' 'ah' 'ai' 'be' 'cb' 'd6' 'dw' 'ea' 'eu' 'fb' 'fd' 'fe' 'fj' 'fk' 'g0' 'g9' 'gf' 'hc' 'hh' 'hk' 'i4' 'ik' 'iv' 'iz' 'j1' 'j3' 'j5' 'ju' 'jz' 'kc' 'md' 'ng' 'nk' 'o1' 'o9' 'oi' 'om' 'p4' 'pl' 'pn' 'ps' 'q8' 'qa' 'qb' 'qh' 'qj' 'qm' 'qn' 'qo' 'qq' 'qr' 'qt' 'qu' 'qy' 'qz' 'ro' 'rs' 's2' 'sl' 'sm' 'sp' 'ti' 'tn' 'tu' 'tw' 'u7' 'ug' 'ui' 'ul' 'us' 'ut' 'vc' 've' 'vj' 'vk' 'w0' 'w2' 'w8' 'wi' 'wk' 'wl' 'wm' 'wt' 'x6' 'xh' 'xy' 'yr' 'ys' 'yt' 'yy' 'yz' 'zr' - f | 'a1' 'ab' 'ad' 'ag' 'bd' 'bf' 'bo' 'bt' 'cl' 'cn' 'cq' 'cz' 'd4' 'db' 'dc' 'dk' 'dn' 'dq' 'ea' 'el' 'em' 'ev' 'ew' 'ex' 'fr' 'ft' 'ga' 'gc' 'gd' 'gg' 'gl' 'gp' 'hp' 'hu' 'id' 'ij' 'jn' 'kl' 'kt' 'lh' 'lk' 'lm' 'mi' 'mw' 'n4' 'nk' 'oj' 'ok' 'on' 'ph' 'pk' 'pt' 'q0' 'q8' 'qa' 'qe' 'qf' 'qg' 'qh' 'qi' 'qr' 'qt' 'qv' 'qw' 'qx' 'rd' 're' 'rg' 'rq' 'rr' 'sb' 'sg' 'te' 'tj' 'us' 'w5' 'w9' 'wb' 'wf' 'wh' 'wp' 'wq' 'ws' 'wt' 'wu' 'wv' 'y7' 'yb' 'ye' 'yf' 'yq' 'yu' 'yy' 'yz' 'zx' - f | 'ah' 'ao' 'aq' 'aw' 'az' 'b5' 'bd' 'bg' 'bj' 'bq' 'bu' 'cc' 'cv' 'dt' 'e0' 'e2' 'e4' 'ec' 'eu' 'fd' 'fi' 'gg' 'gu' 'ha' 'hb' 'he' 'hj' 'hy' 'ih' 'in' 'iv' 'ji' 'js' 'ks' 'l1' 'la' 'lg' 'mh' 'mi' 'mk' 'o7' 'oh' 'oj' 'om' 'on' 'op' 'oq' 'or' 'os' 'p1' 'pe' 'pq' 'pv' 'q8' 'qe' 'qg' 'qj' 'ql' 'qm' 'qn' 'qo' 'qr' 'qs' 'qt' 'qv' 'qy' 'r1' 'r9' 'rb' 'rl' 'ro' 'ru' 'rw' 'ry' 's0' 's6' 'sn' 't0' 't1' 'tr' 'ty' 'u0' 'u8' 'up' 'uv' 'vn' 'wb' 'wg' 'ww' 'x1' 'yb' 'yj' 'ym' 'yx' 'zz' - f | '2k' '2v' '4h' '7c' 'a3' 'aa' 'al' 'an' 'aq' 'aw' 'b7' 'bb' 'bl' 'bn' 'by' 'ca' 'cf' 'cu' 'dd' 'dn' 'e6' 'ej' 'en' 'et' 'eu' 'ey' 'g8' 'gg' 'go' 'gw' 'hj' 'ho' 'i3' 'ii' 'iy' 'ke' 'kh' 'le' 'lf' 'lu' 'ml' 'ny' 'oa' 'oc' 'og' 'oh' 'os' 'pf' 'pl' 'qb' 'qd' 'qf' 'qj' 'qn' 'qu' 'qw' 'r8' 'rd' 're' 'rh' 'sc' 'sn' 'ss' 't0' 't7' 'tl' 'tt' 'tx' 'tz' 'u9' 'uc' 'ud' 'ul' 'us' 'ux' 'vb' 'vf' 'vg' 'vp' 'w5' 'w6' 'wk' 'wt' 'wy' 'xd' 'xk' 'xm' 'xn' 'xr' 'xx' 'y0' 'yf' 'yl' 'yo' 'yw' 'zv' - f | '3k' '3v' 'a0' 'ah' 'am' 'an' 'ay' 'bd' 'cs' 'cu' 'cw' 'db' 'dp' 'e3' 'et' 'ey' 'fi' 'gl' 'gq' 'hh' 'hk' 'hx' 'i8' 'ih' 'ii' 'iv' 'iz' 'j5' 'jf' 'jo' 'jr' 'kd' 'km' 'lg' 'li' 'lt' 'm4' 'mo' 'mv' 'mw' 'n3' 'nh' 'nn' 'o9' 'od' 'of' 'oh' 'ok' 'or' 'pc' 'pg' 'pm' 'pn' 'q6' 'qj' 'qn' 'qr' 'qs' 'qw' 'r7' 'r9' 're' 'rm' 'rr' 'rt' 'rz' 'sb' 'sf' 'td' 'tu' 'uc' 'ug' 'uj' 'uu' 'w3' 'wa' 'wc' 'wg' 'wi' 'wm' 'wp' 'ww' 'wx' 'wz' 'xb' 'xi' 'y0' 'y9' 'ya' 'yd' 'yg' 'yh' 'yv' 'yy' 'zo' 'zz' - f | '3z' 'ah' 'ai' 'aq' 'bn' 'ck' 'cm' 'cq' 'ct' 'cy' 'd1' 'db' 'dn' 'e2' 'e7' 'ed' 'ee' 'eh' 'eq' 'er' 'ew' 'fc' 'fn' 'fp' 'fs' 'fw' 'fx' 'gl' 'gp' 'gw' 'ha' 'hq' 'hw' 'hx' 'i2' 'ir' 'j9' 'jb' 'jn' 'jr' 'jx' 'jy' 'kk' 'kl' 'kx' 'kz' 'li' 'm2' 'me' 'mj' 'mr' 'na' 'nu' 'oe' 'ol' 'on' 'po' 'pv' 'q7' 'qa' 'qb' 'qd' 'qh' 'qp' 'qv' 'rh' 'rz' 'sf' 'sp' 't6' 'tl' 'to' 'tx' 'ty' 'ue' 'uf' 'un' 'uu' 'uw' 'vb' 'vq' 'wg' 'wp' 'wt' 'wu' 'xb' 'xp' 'y1' 'y9' 'yi' 'yj' 'ys' 'yu' 'z7' 'zf' 'zt' - f | '4d' '9f' 'aa' 'ad' 'av' 'b1' 'bb' 'bd' 'bk' 'd2' 'd6' 'd7' 'de' 'do' 'ds' 'dx' 'e4' 'eh' 'en' 'ep' 'ew' 'fd' 'fg' 'fo' 'fp' 'ga' 'gw' 'gz' 'h2' 'h4' 'hc' 'hk' 'ic' 'ih' 'io' 'iv' 'iy' 'jj' 'jt' 'jz' 'kb' 'ke' 'kl' 'lh' 'lm' 'nc' 'ni' 'nl' 'o9' 'oi' 'oo' 'pd' 'pj' 'po' 'pr' 'qa' 'qf' 'qi' 'qm' 'qn' 'qp' 'qt' 'qu' 'rb' 're' 'rf' 'rn' 'rz' 'sz' 't0' 'tg' 'ti' 'ut' 'v9' 'vg' 'wb' 'wc' 'wf' 'wx' 'x0' 'xb' 'xv' 'y6' 'yb' 'yg' 'yh' 'yk' 'ym' 'yn' 'yw' 'yx' 'yz' 'z8' 'zp' 'zs' 'zw' - f | '5t' 'al' 'av' 'ax' 'by' 'cr' 'cx' 'df' 'dh' 'dp' 'dq' 'e4' 'e7' 'en' 'ff' 'fg' 'fo' 'g4' 'ge' 'gg' 'gm' 'gr' 'gt' 'hc' 'hn' 'hq' 'hs' 'i0' 'i1' 'id' 'ig' 'im' 'jb' 'jv' 'k9' 'kl' 'km' 'lh' 'lp' 'lq' 'mm' 'ne' 'nk' 'nu' 'od' 'os' 'pj' 'pw' 'pz' 'q0' 'q1' 'qa' 'qd' 'qe' 'qf' 'qk' 'ql' 'qn' 'qp' 'qt' 'r0' 'rx' 'sx' 'sy' 't0' 't1' 't8' 'th' 'ti' 'tp' 'tv' 'tz' 'ub' 'uc' 'ul' 'v5' 'vp' 'vu' 'w8' 'wd' 'we' 'wf' 'wh' 'wm' 'wq' 'wr' 'ws' 'wy' 'wz' 'xg' 'y9' 'yg' 'yh' 'yq' 'yz' 'z0' - f | '6w' 'a1' 'aa' 'ab' 'af' 'ao' 'aq' 'ay' 'cd' 'cg' 'dn' 'dp' 'dy' 'e0' 'e6' 'eb' 'ej' 'fh' 'fi' 'fs' 'g0' 'gc' 'gi' 'gs' 'gv' 'gw' 'hd' 'ho' 'i8' 'i9' 'ia' 'ic' 'ie' 'ik' 'ja' 'jd' 'ji' 'jm' 'k0' 'kj' 'lj' 'm8' 'n0' 'nh' 'nl' 'nn' 'o8' 'of' 'ou' 'oz' 'ph' 'po' 'q0' 'q3' 'q6' 'q7' 'qa' 'qc' 'qd' 'qn' 'qp' 'qq' 'qu' 'qw' 'qz' 'rd' 'rm' 'rn' 'rp' 'rs' 'sj' 'tg' 'tj' 'tr' 'tv' 'u0' 'uf' 'uh' 'uk' 'vv' 'wa' 'wc' 'wf' 'wg' 'wn' 'wo' 'wp' 'wr' 'ww' 'xt' 'y3' 'y7' 'yc' 'yf' 'yp' 'yy' - f | '9y' 'a0' 'a6' 'ad' 'aj' 'az' 'bw' 'by' 'cg' 'ci' 'dc' 'dk' 'dm' 'dw' 'e0' 'e4' 'ee' 'ef' 'eg' 'en' 'eu' 'fc' 'fg' 'fm' 'fx' 'g7' 'gm' 'go' 'hw' 'hy' 'i4' 'i7' 'ip' 'iq' 'ir' 'jr' 'ju' 'jx' 'kr' 'ky' 'la' 'lk' 'lq' 'm9' 'mg' 'mp' 'my' 'n6' 'nv' 'nz' 'o1' 'o3' 'oe' 'oy' 'pj' 'pv' 'pz' 'qg' 'ql' 'qp' 'qt' 'qy' 'r4' 'rf' 'rg' 'rk' 'ro' 'rw' 'ry' 's3' 'sd' 'sf' 'sm' 'tf' 'tg' 'tq' 'tu' 'ty' 'tz' 'ub' 'uc' 'uf' 'um' 'vi' 'vn' 'wa' 'wc' 'we' 'xo' 'xr' 'xs' 'yb' 'yi' 'yw' 'zn' 'zo' - f | 'ag' 'ai' 'az' 'br' 'c2' 'cd' 'ck' 'db' 'du' 'e0' 'e7' 'eb' 'ee' 'em' 'ep' 'eq' 'es' 'eu' 'ex' 'fh' 'fi' 'ga' 'gm' 'gn' 'hj' 'hq' 'if' 'ig' 'ii' 'ix' 'jk' 'kd' 'kg' 'kk' 'kr' 'kt' 'ku' 'lx' 'mp' 'mq' 'nx' 'o9' 'oa' 'om' 'oq' 'p5' 'pd' 'pr' 'pu' 'pw' 'q3' 'qa' 'qd' 'qe' 'qf' 'qi' 'qj' 'ql' 'qn' 'qo' 'qr' 'qt' 'qu' 'qv' 'qx' 'rc' 'rf' 'rl' 'rm' 'rn' 'rp' 'rr' 's7' 'se' 'st' 'sx' 't8' 'to' 'u1' 'ua' 'uq' 'ux' 'w6' 'w8' 'wf' 'wt' 'xb' 'xf' 'xk' 'xn' 'y3' 'ym' 'yp' 'yz' 'zi' 'zk' - f | '1k' '1p' 'a6' 'ah' 'ap' 'ay' 'az' 'bi' 'bj' 'cv' 'd1' 'd2' 'd5' 'db' 'dm' 'dn' 'e7' 'eg' 'eh' 'el' 'en' 'eo' 'er' 'f4' 'fy' 'gf' 'gp' 'hd' 'hj' 'hp' 'i2' 'i7' 'ij' 'ik' 'jq' 'jy' 'kg' 'kh' 'kw' 'lr' 'ls' 'n5' 'ne' 'o7' 'od' 'oi' 'oo' 'os' 'oy' 'pd' 'ph' 'pm' 'pw' 'px' 'qa' 'qb' 'qc' 'qk' 'ql' 'qm' 'qn' 'qr' 'qs' 'qu' 'qx' 'r5' 'rh' 'ri' 'rk' 'rn' 'ry' 's0' 'sa' 'si' 'sr' 'st' 'ub' 'uc' 'uf' 'uq' 'ur' 'v9' 'vn' 'w9' 'wd' 'we' 'wk' 'wt' 'wv' 'ww' 'xj' 'xk' 'yh' 'yk' 'zd' 'zw' 'zy' - f | '2u' '3q' '7p' 'b4' 'b8' 'bb' 'bq' 'bu' 'ce' 'dk' 'dn' 'e0' 'ea' 'ed' 'ef' 'eg' 'eh' 'ej' 'ek' 'em' 'en' 'eo' 'et' 'fh' 'fr' 'fw' 'gj' 'gx' 'ho' 'i7' 'i9' 'if' 'ii' 'il' 'in' 'iq' 'iw' 'jy' 'k0' 'k1' 'km' 'kr' 'lh' 'lq' 'lw' 'ma' 'mb' 'md' 'mj' 'nh' 'ni' 'nj' 'ob' 'p5' 'p7' 'pc' 'pk' 'pl' 'pu' 'q2' 'qa' 'qc' 'qe' 'qi' 'qm' 'qn' 'qt' 'rb' 'rp' 'rx' 'sd' 't6' 'tb' 'tl' 'tw' 'u5' 'u7' 'ug' 'uh' 'ut' 'vs' 'wi' 'wj' 'wp' 'ws' 'wu' 'wv' 'xm' 'xs' 'y2' 'y6' 'yd' 'yh' 'yl' 'yr' 'yx' 'yz' - f | '2u' 'au' 'bz' 'cd' 'cj' 'cm' 'cq' 'ct' 'cw' 'd1' 'ds' 'dw' 'dz' 'ec' 'ei' 'eo' 'fk' 'fq' 'fx' 'g6' 'gl' 'gs' 'i5' 'if' 'im' 'iq' 'jd' 'k0' 'k5' 'kr' 'lv' 'lx' 'n5' 'na' 'ny' 'ob' 'ot' 'ox' 'pa' 'pi' 'ps' 'qa' 'qc' 'qg' 'qh' 'qj' 'ql' 'qm' 'qo' 'qr' 'qs' 'r2' 'rh' 'rl' 'rr' 'rw' 'rx' 's0' 's8' 'sb' 'sc' 'sg' 'si' 'sl' 'so' 'sv' 't7' 't9' 'tc' 'te' 'tl' 'tn' 'to' 'tr' 'tx' 'ty' 'un' 'uz' 'vg' 'vw' 'ws' 'wt' 'wu' 'xj' 'xy' 'y0' 'y1' 'ya' 'ye' 'yl' 'yn' 'yr' 'ys' 'yt' 'z3' 'z6' 'zw' - f | '3a' '5k' 'a2' 'ah' 'au' 'ba' 'bb' 'bh' 'bu' 'cb' 'cj' 'cv' 'cx' 'df' 'dy' 'e4' 'e6' 'ed' 'ep' 'et' 'ev' 'ez' 'f1' 'fb' 'fl' 'fx' 'fz' 'gg' 'h5' 'hj' 'io' 'iz' 'ja' 'k7' 'kf' 'lu' 'lv' 'md' 'ne' 'nh' 'oh' 'on' 'ow' 'p0' 'p8' 'pc' 'px' 'q3' 'qa' 'qf' 'qg' 'qj' 'qr' 'qs' 'qt' 'qv' 'qx' 'qz' 'r3' 'rm' 'rq' 'rt' 'rv' 'sa' 'sf' 'so' 't1' 't7' 'tb' 'tn' 'tq' 'tr' 'ts' 'tu' 'tz' 'u7' 'uf' 'uk' 'um' 'ut' 'va' 'vj' 'vm' 'vx' 'vz' 'wa' 'we' 'wf' 'xe' 'xg' 'ya' 'yb' 'yu' 'zg' 'zo' 'zt' 'zz' - f | '3t' '5u' 'aa' 'ab' 'b9' 'd8' 'di' 'dj' 'dq' 'dx' 'dy' 'ea' 'ek' 'el' 'es' 'ev' 'ey' 'ez' 'fk' 'fp' 'g3' 'gb' 'gd' 'gg' 'gi' 'go' 'hj' 'hz' 'i3' 'i5' 'in' 'io' 'is' 'jv' 'kb' 'kr' 'lu' 'md' 'nd' 'ny' 'o6' 'oe' 'ok' 'ow' 'ox' 'p4' 'p7' 'p8' 'pb' 'pu' 'q1' 'q3' 'q4' 'qd' 'qj' 'qk' 'qq' 'qx' 'qy' 'r6' 'ro' 'rw' 'rx' 'ry' 'se' 'sp' 't0' 't1' 'th' 'tl' 'tn' 'tp' 'tr' 'tx' 'u5' 'uf' 'uo' 'ux' 'vc' 'vz' 'w2' 'w5' 'wa' 'wb' 'we' 'wi' 'wj' 'ws' 'wt' 'xh' 'y1' 'yb' 'yg' 'yi' 'zd' 'zm' 'zt' - f | '4g' '8w' 'ab' 'aq' 'at' 'bc' 'bi' 'c7' 'cb' 'cj' 'cs' 'd2' 'd3' 'di' 'dm' 'dx' 'dz' 'ed' 'ex' 'fj' 'gs' 'h1' 'h6' 'he' 'hj' 'hr' 'i1' 'ia' 'ie' 'il' 'ix' 'iy' 'j2' 'jd' 'jo' 'jy' 'kx' 'la' 'lv' 'ma' 'mh' 'mp' 'mt' 'n9' 'na' 'nf' 'ng' 'np' 'o7' 'ob' 'on' 'ou' 'ov' 'p9' 'pg' 'po' 'pq' 'q0' 'q4' 'q5' 'qc' 'qj' 'qp' 'qq' 'qt' 'ra' 'rb' 'rq' 'ru' 'sl' 'sp' 't8' 'ta' 'te' 'tl' 'tz' 'u1' 'ud' 'ui' 'uv' 'uw' 'vf' 'vt' 'vu' 'vz' 'w0' 'w7' 'wc' 'wg' 'wh' 'wq' 'wr' 'wz' 'y6' 'y7' 'ye' 'yh' - f | '6u' 'a5' 'ai' 'au' 'b1' 'be' 'bg' 'by' 'ce' 'co' 'cw' 'db' 'dw' 'e8' 'ec' 'em' 'en' 'er' 'f7' 'fh' 'fu' 'gb' 'gq' 'h4' 'hf' 'hy' 'hz' 'ig' 'iq' 'kj' 'kk' 'kp' 'ky' 'ld' 'lr' 'na' 'nd' 'ny' 'o8' 'o9' 'og' 'oh' 'ok' 'ot' 'ou' 'ow' 'ox' 'p8' 'pl' 'pp' 'ps' 'px' 'q0' 'q1' 'qa' 'qm' 'qr' 'qs' 'qx' 'r9' 'rd' 're' 'rf' 'rl' 'rn' 'rr' 'ru' 'rz' 'sc' 'sn' 'so' 't1' 'tl' 'tr' 'ts' 'tt' 'tx' 'ub' 'um' 'un' 'uo' 'ut' 'vr' 'wa' 'wb' 'wc' 'wj' 'wl' 'wp' 'wq' 'ws' 'wx' 'wz' 'yp' 'yt' 'yy' 'zc' - f | '7h' '7k' 'bi' 'c8' 'cc' 'cj' 'cs' 'd7' 'dh' 'dl' 'dp' 'dt' 'e9' 'ea' 'eh' 'ei' 'ej' 'el' 'ew' 'fo' 'fp' 'ge' 'gg' 'gi' 'gk' 'h2' 'h7' 'hk' 'hs' 'hy' 'ii' 'j4' 'kd' 'kh' 'kp' 'ks' 'm2' 'n1' 'n3' 'nk' 'nr' 'od' 'ok' 'om' 'oy' 'pb' 'ph' 'pm' 'pp' 'pt' 'q2' 'q8' 'q9' 'qc' 'qf' 'qg' 'qh' 'qj' 'qk' 'qn' 'qo' 'qq' 'qv' 'qx' 'r4' 'rc' 'rg' 'rj' 's9' 'sb' 'sg' 'sj' 'ss' 't6' 'ta' 'tc' 'tm' 'tv' 'us' 'uu' 'uy' 'w7' 'wf' 'wh' 'wk' 'wz' 'x2' 'xr' 'ya' 'yc' 'yk' 'yp' 'ys' 'yz' 'z4' 'zg' 'zn' - f | '15' '4o' '7h' 'aa' 'av' 'b0' 'cb' 'da' 'dh' 'di' 'dr' 'e0' 'ee' 'eo' 'ep' 'ey' 'fi' 'fo' 'fq' 'fx' 'fz' 'g9' 'ge' 'hd' 'hh' 'hs' 'i4' 'i9' 'iq' 'it' 'iw' 'ix' 'iy' 'j8' 'jr' 'ld' 'm1' 'mo' 'nx' 'ob' 'ol' 'ot' 'pj' 'qf' 'qj' 'qk' 'qp' 'qv' 'qy' 'r4' 'r5' 'rl' 'rm' 'rq' 'rz' 's8' 'sc' 'sd' 'sf' 'sh' 'sn' 'ss' 't5' 't7' 'tj' 'to' 'tw' 'u6' 'uc' 'ud' 'ug' 'ui' 'uk' 'ut' 'uy' 'uz' 'vi' 'w0' 'w9' 'wb' 'wg' 'wl' 'ww' 'wx' 'wy' 'xa' 'xb' 'xo' 'ya' 'yi' 'yu' 'yv' 'yz' 'zm' 'zv' 'zx' 'zy' 'zz' - f | '1g' '6b' 'az' 'be' 'c5' 'dp' 'dt' 'e6' 'eg' 'en' 'es' 'et' 'f1' 'fi' 'fn' 'ft' 'fz' 'g0' 'gj' 'gv' 'h8' 'hp' 'hs' 'hu' 'hw' 'hz' 'ia' 'im' 'is' 'iv' 'iw' 'iy' 'jd' 'kw' 'ky' 'l4' 'l7' 'mn' 'nn' 'nr' 'ny' 'ot' 'p2' 'p8' 'pt' 'q8' 'qa' 'qb' 'qf' 'qh' 'qi' 'qm' 'qt' 'qu' 'qv' 'qy' 'ra' 'rl' 'ro' 'rw' 'rx' 's8' 's9' 'sk' 'sn' 'st' 'sv' 'th' 'tl' 'to' 'tp' 'tu' 'ua' 'uk' 'un' 'uv' 'v8' 've' 'vt' 'vu' 'vv' 'w4' 'wc' 'wh' 'wo' 'wq' 'wy' 'xe' 'xm' 'xp' 'xu' 'yc' 'yn' 'yq' 'zf' 'zj' 'zs' 'zt' - f | '1t' 'a0' 'ah' 'ar' 'at' 'be' 'bs' 'bt' 'co' 'd9' 'e9' 'ea' 'ec' 'ei' 'eo' 'er' 'ez' 'fa' 'fz' 'gl' 'gt' 'h8' 'h9' 'hb' 'hv' 'ia' 'ic' 'if' 'im' 'io' 'iq' 'ix' 'k9' 'kq' 'lo' 'm4' 'md' 'mo' 'mz' 'ni' 'nr' 'nz' 'o8' 'ox' 'pk' 'pr' 'q1' 'q3' 'q8' 'qa' 'qb' 'qf' 'qg' 'qi' 'ql' 'qr' 'r3' 'rc' 'rf' 'rg' 'rr' 's0' 'sf' 'tg' 'tw' 'u2' 'uh' 'un' 'ur' 'ux' 'vb' 'vr' 'w7' 'w9' 'wd' 'wh' 'wm' 'wo' 'wr' 'ws' 'wv' 'x4' 'xj' 'xx' 'y3' 'y8' 'yd' 'yl' 'yo' 'yq' 'yr' 'yw' 'z8' 'za' 'zb' 'zg' 'zo' 'zs' - f | '2n' 'aa' 'ab' 'ae' 'ah' 'aj' 'as' 'av' 'ax' 'bc' 'be' 'bi' 'by' 'cg' 'ck' 'cm' 'cx' 'dq' 'dr' 'e5' 'ed' 'ef' 'ei' 'em' 'eu' 'fd' 'fq' 'fu' 'gd' 'gl' 'gs' 'he' 'ia' 'iz' 'jc' 'je' 'jt' 'k7' 'km' 'ko' 'l4' 'lh' 'mk' 'nl' 'ny' 'oa' 'oh' 'op' 'p1' 'pj' 'pm' 'ps' 'q1' 'q7' 'qc' 'qg' 'qj' 'qo' 'qr' 'qt' 'qu' 'qv' 'qw' 'rf' 'ri' 'rl' 'rw' 'sf' 'su' 't4' 'tc' 'tn' 'to' 'tq' 'tr' 'tt' 'tv' 'tz' 'u0' 'u6' 'ub' 'ug' 'up' 'uv' 'ux' 'uy' 'vv' 'w2' 'wx' 'xi' 'xu' 'xw' 'yj' 'yt' 'yw' 'z3' 'z7' 'zb' - f | '2y' '4v' 'ac' 'at' 'av' 'bd' 'bs' 'c3' 'ca' 'cf' 'cg' 'cq' 'cw' 'cz' 'db' 'dx' 'e7' 'eg' 'ei' 'el' 'et' 'ey' 'fo' 'fq' 'fw' 'fx' 'gd' 'gl' 'gw' 'h6' 'hd' 'hp' 'hy' 'il' 'ir' 'is' 'j5' 'k4' 'k8' 'kc' 'kp' 'kz' 'l0' 'l1' 'm4' 'm6' 'mn' 'mr' 'nx' 'ov' 'ox' 'pn' 'pq' 'q3' 'qd' 'qj' 'qk' 'qo' 'qw' 'qx' 'rd' 're' 'rg' 'rq' 'rr' 'rx' 'rz' 'sb' 't0' 't3' 't4' 'tc' 'tk' 'tp' 'tr' 'tv' 'u7' 'uf' 'um' 'uo' 'uq' 'va' 'vc' 'vi' 'vy' 'vz' 'w1' 'w6' 'wo' 'wq' 'xh' 'xn' 'y5' 'yo' 'ys' 'yt' 'zs' 'zu' - f | '4y' '6h' 'a0' 'a6' 'bo' 'bq' 'co' 'cv' 'dv' 'ec' 'ee' 'eh' 'ei' 'en' 'er' 'ew' 'f1' 'fk' 'fq' 'fy' 'ga' 'gj' 'gp' 'gv' 'gx' 'hv' 'i0' 'i5' 'ij' 'ik' 'in' 'kb' 'ks' 'kw' 'kz' 'la' 'lh' 'lq' 'ls' 'mu' 'nl' 'og' 'oi' 'om' 'pf' 'pu' 'pv' 'q6' 'q9' 'qe' 'qk' 'ql' 'qo' 'qq' 'qu' 'qw' 'r5' 'rb' 'rn' 'rr' 'rt' 's4' 'sc' 'sg' 'si' 'sm' 'sn' 'sp' 'ss' 'tb' 'tc' 'tg' 'tk' 'tm' 'tr' 'tt' 'tu' 'tv' 'u0' 'u3' 'uj' 'un' 'uz' 'vp' 'w6' 'w8' 'wc' 'we' 'wi' 'wt' 'wx' 'xp' 'xz' 'y0' 'y4' 'z0' 'zj' 'zy' - f | '5e' 'a1' 'ak' 'at' 'av' 'bi' 'bj' 'c7' 'dl' 'do' 'e0' 'ee' 'el' 'em' 'es' 'fl' 'fy' 'g2' 'g6' 'g7' 'gr' 'gv' 'h3' 'hg' 'hr' 'ht' 'ii' 'il' 'is' 'j8' 'jm' 'jq' 'jv' 'k0' 'ka' 'kk' 'l3' 'la' 'lh' 'ms' 'mz' 'no' 'oa' 'os' 'ox' 'pa' 'pc' 'pl' 'pq' 'px' 'q1' 'q3' 'q5' 'q8' 'qb' 'qi' 'ql' 'qw' 'qy' 'qz' 're' 'ri' 'rp' 'rx' 's1' 's3' 'sh' 'sq' 't1' 't5' 'ta' 'td' 'tj' 'to' 'tt' 'ub' 'uc' 'ur' 'uv' 'v1' 'vs' 'w7' 'wb' 'wd' 'wj' 'wl' 'wm' 'wq' 'xr' 'xt' 'ym' 'yu' 'yx' 'yz' 'z8' 'zp' 'zr' 'zs' - f | '5o' 'ar' 'at' 'c1' 'ch' 'cr' 'dp' 'dy' 'ed' 'el' 'er' 'f0' 'f7' 'fc' 'fh' 'fm' 'fx' 'g8' 'gd' 'gf' 'gh' 'gl' 'gp' 'gv' 'gy' 'hs' 'ht' 'hv' 'hy' 'i2' 'ia' 'lc' 'lu' 'lv' 'ly' 'mb' 'mq' 'nn' 'nz' 'oa' 'oq' 'ot' 'ox' 'oz' 'p8' 'pc' 'pn' 'py' 'q2' 'q7' 'qe' 'qj' 'qn' 'qs' 'qv' 'qw' 'r1' 'r4' 'rh' 'rn' 'rq' 'rr' 's1' 'sa' 'sd' 'se' 'sr' 't6' 'ta' 'tf' 'ts' 'tw' 'tx' 'uk' 'ul' 'uw' 'v7' 'vu' 'vz' 'w7' 'wc' 'wd' 'wk' 'wl' 'wo' 'wr' 'wv' 'x9' 'xe' 'xj' 'xl' 'xu' 'yi' 'ym' 'yq' 'z1' 'z5' 'zm' - f | '8d' 'a2' 'af' 'ap' 'as' 'cg' 'cr' 'd4' 'dg' 'du' 'dv' 'eg' 'ei' 'el' 'em' 'fg' 'ft' 'g1' 'gt' 'h1' 'hg' 'i5' 'i8' 'ih' 'im' 'ir' 'iw' 'jd' 'jf' 'js' 'jw' 'jz' 'k8' 'ko' 'ky' 'la' 'lr' 'mq' 'no' 'ox' 'p8' 'p9' 'pb' 'pv' 'pw' 'px' 'q0' 'q1' 'q5' 'q7' 'qd' 'qh' 'qj' 'ql' 'qn' 'qo' 'qq' 'qx' 'qy' 'rc' 'rd' 'rf' 'rh' 'rr' 'rs' 'sd' 'sw' 'sz' 't3' 'tf' 'tn' 'to' 'tv' 'tx' 'ty' 'ua' 'ub' 'ud' 'uk' 'ul' 'us' 'uu' 'ux' 'vl' 'vs' 'wf' 'wg' 'wl' 'wq' 'wt' 'wu' 'wx' 'x6' 'xi' 'yg' 'yh' 'z7' 'ze' - f | 'a3' 'ai' 'ap' 'ar' 'at' 'be' 'br' 'bz' 'cw' 'd2' 'd6' 'df' 'dk' 'do' 'dp' 'dr' 'dw' 'e2' 'ef' 'eg' 'ej' 'eq' 'fb' 'fd' 'fq' 'ge' 'gr' 'h0' 'hp' 'i8' 'ih' 'ir' 'jb' 'jd' 'kj' 'kz' 'l4' 'lm' 'lt' 'mj' 'mz' 'nc' 'ni' 'nw' 'of' 'oh' 'ol' 'ot' 'ov' 'p5' 'pf' 'pn' 'pp' 'pv' 'py' 'q2' 'q3' 'q7' 'qb' 'qc' 'qj' 'qo' 'qp' 'qr' 'qs' 'r6' 'ra' 'rc' 'rd' 'rx' 'se' 'sn' 'sr' 'sx' 't2' 't9' 'tm' 'tr' 'uj' 'vh' 'vn' 'vs' 'w0' 'w2' 'w3' 'wd' 'we' 'wj' 'wq' 'wr' 'wv' 'xe' 'xo' 'ya' 'ye' 'yq' 'yt' 'yw' - f | '1h' '4r' 'a9' 'ag' 'ah' 'ak' 'as' 'at' 'b2' 'be' 'cs' 'd6' 'd7' 'da' 'dg' 'dl' 'dr' 'dy' 'dz' 'e8' 'e9' 'eg' 'eh' 'ek' 'eq' 'es' 'f3' 'f9' 'fs' 'gh' 'go' 'gq' 'gz' 'ha' 'hc' 'hi' 'hp' 'if' 'ig' 'im' 'iv' 'jd' 'jt' 'kp' 'kt' 'l6' 'le' 'lg' 'lo' 'mz' 'ns' 'oc' 'ok' 'p6' 'pa' 'pt' 'pv' 'px' 'q1' 'q5' 'qa' 'qe' 'qg' 'qh' 'qi' 'qn' 'qp' 'qs' 'qu' 'qv' 'qy' 'rh' 'rl' 'rp' 'rv' 'rx' 'rz' 's8' 'sc' 'sr' 'su' 't6' 'tb' 'tq' 'tw' 'u9' 'vr' 'w7' 'wb' 'wj' 'wq' 'ww' 'xr' 'xx' 'yb' 'yl' 'yn' 'yr' 'zw' - f | '1r' '2q' 'aa' 'ae' 'ah' 'aq' 'at' 'bx' 'c6' 'ca' 'cl' 'da' 'dp' 'ds' 'e0' 'e6' 'ea' 'eb' 'eg' 'er' 'ev' 'ew' 'fs' 'fw' 'gm' 'gx' 'hl' 'hv' 'hw' 'ib' 'iw' 'jc' 'jn' 'jr' 'ju' 'k0' 'k5' 'k8' 'kf' 'kn' 'l1' 'ln' 'lp' 'ly' 'mz' 'n3' 'nc' 'ng' 'nk' 'o8' 'oe' 'oi' 'ol' 'ot' 'pb' 'pe' 'pk' 'q0' 'qa' 'qe' 'qg' 'qi' 'ql' 'qo' 'qv' 'r0' 'r2' 'r4' 'rg' 'rj' 'rm' 'sv' 'tk' 'to' 'tt' 'u7' 'ud' 'uo' 'ur' 'us' 'uv' 'vf' 'vm' 'vn' 'w6' 'w7' 'wa' 'wb' 'wf' 'wg' 'wi' 'wv' 'ww' 'wx' 'xk' 'xy' 'yg' 'yh' 'ym' - f | '6i' 'a3' 'ac' 'ax' 'br' 'ck' 'dc' 'dj' 'dn' 'dq' 'dt' 'dv' 'eb' 'ed' 'ei' 'en' 'eq' 'ey' 'f4' 'fk' 'fm' 'fp' 'fw' 'gn' 'gw' 'he' 'ho' 'io' 'ja' 'jj' 'jn' 'ju' 'jy' 'kc' 'kk' 'l1' 'l4' 'lt' 'lx' 'm1' 'm7' 'mc' 'o6' 'ol' 'p3' 'pc' 'pe' 'pp' 'pv' 'px' 'q0' 'qb' 'qe' 'qr' 'qs' 'qt' 'qu' 'qv' 'qy' 'rg' 'rq' 's3' 'sm' 'so' 't5' 't9' 'tb' 'tp' 'ty' 'tz' 'u6' 'ua' 'ui' 'uv' 'vm' 'vw' 'w2' 'wa' 'wf' 'wi' 'wj' 'wt' 'ww' 'wz' 'xg' 'y1' 'ya' 'yb' 'yd' 'yg' 'yh' 'yi' 'yk' 'yp' 'yz' 'zb' 'zk' 'zm' 'zo' - f | '0e' 'a0' 'a1' 'a5' 'ad' 'ao' 'bp' 'bw' 'c0' 'ca' 'cw' 'cx' 'cy' 'dg' 'e8' 'eb' 'ef' 'ek' 'fd' 'fk' 'fv' 'g3' 'gj' 'gn' 'gw' 'hr' 'if' 'ij' 'is' 'ix' 'j7' 'km' 'kp' 'kq' 'ks' 'kt' 'kv' 'lk' 'mj' 'mn' 'mv' 'mz' 'nn' 'o5' 'oa' 'ol' 'on' 'os' 'pa' 'pb' 'pf' 'po' 'pq' 'pr' 'pv' 'py' 'q1' 'q6' 'qf' 'qh' 'qj' 'qn' 'qt' 'qu' 'qw' 'rf' 'rk' 'rt' 's5' 'sq' 'ss' 't5' 'ta' 'th' 'tj' 'to' 'ts' 'tx' 'tz' 'u0' 'ua' 'ug' 'ui' 'uw' 'ux' 'vh' 'vp' 'we' 'wg' 'wi' 'wj' 'wp' 'wq' 'xm' 'y6' 'yr' 'yt' 'yu' 'z8' 'zs' - f | '1l' '24' '2o' 'ad' 'am' 'ao' 'bf' 'bl' 'ca' 'ce' 'cn' 'cw' 'cy' 'd4' 'dm' 'e9' 'ed' 'ee' 'ei' 'ep' 'f0' 'fk' 'fu' 'fw' 'g3' 'g4' 'gm' 'gn' 'gu' 'ha' 'hh' 'id' 'iq' 'it' 'iw' 'ix' 'iy' 'j4' 'j6' 'jo' 'jp' 'js' 'jv' 'k6' 'kb' 'ke' 'kh' 'l9' 'lh' 'lr' 'mr' 'n7' 'n8' 'nd' 'o6' 'of' 'oi' 'op' 'ox' 'p6' 'q2' 'qb' 'qd' 'qi' 'qk' 'qn' 'qq' 'qt' 'qy' 'r2' 're' 'rf' 'rk' 's4' 'sb' 'se' 'sh' 't4' 'tb' 'tn' 'tq' 'ty' 'u4' 'u5' 'ub' 'ue' 'uf' 'uk' 'uo' 'vc' 'vs' 'vu' 'w7' 'wd' 'wh' 'xm' 'yk' 'yv' 'zl' 'zz' - f | '1m' '37' 'a8' 'ab' 'at' 'ce' 'cx' 'd6' 'dk' 'e9' 'eh' 'ei' 'el' 'eo' 'eu' 'ew' 'ex' 'f4' 'fh' 'fr' 'fs' 'fy' 'g6' 'gf' 'gl' 'gm' 'gp' 'h8' 'ha' 'hf' 'hi' 'hl' 'hr' 'ie' 'ir' 'j6' 'jd' 'jr' 'jv' 'kz' 'l1' 'lg' 'ln' 'lp' 'ls' 'm0' 'ma' 'mi' 'mo' 'mu' 'na' 'np' 'o8' 'oc' 'oe' 'oh' 'ot' 'oy' 'ps' 'pw' 'q5' 'qa' 'qe' 'qg' 'qh' 'qi' 'qq' 'qz' 'rd' 'rg' 'ru' 'rv' 's9' 'sr' 'sv' 'sy' 'sz' 'ts' 'ty' 'uc' 'uh' 'uj' 'uk' 'up' 'v9' 'vc' 'vg' 'vt' 'vy' 'wl' 'wm' 'wv' 'wy' 'wz' 'xg' 'yg' 'yv' 'yw' 'yy' 'z0' - f | '2s' '3k' '4b' '6s' 'a1' 'b2' 'b9' 'bi' 'bz' 'c0' 'c3' 'ck' 'cl' 'cu' 'de' 'dl' 'do' 'dp' 'e8' 'ec' 'ee' 'ei' 'ek' 'eu' 'ez' 'fi' 'fo' 'fr' 'fu' 'fx' 'gh' 'gk' 'go' 'gq' 'h4' 'hd' 'hm' 'hz' 'ia' 'id' 'iv' 'jb' 'jh' 'km' 'kp' 'l4' 'l7' 'l8' 'ld' 'lj' 'nb' 'o7' 'oc' 'oh' 'os' 'p4' 'p9' 'pd' 'q6' 'q7' 'q8' 'qc' 'qh' 'qj' 'qk' 'qw' 'rg' 'rj' 'rm' 'rn' 'rp' 'ru' 'rz' 's5' 'si' 'sv' 'sy' 'tc' 'tg' 'tk' 'tv' 'ue' 'uf' 'ux' 'uz' 'vl' 'w2' 'w6' 'wf' 'wm' 'wu' 'wx' 'xh' 'ya' 'yc' 'ye' 'z7' 'zi' 'zo' 'zq' - f | '3s' '6q' 'ad' 'am' 'as' 'ay' 'cd' 'ci' 'cl' 'dw' 'eb' 'en' 'ep' 'ew' 'ey' 'fh' 'g6' 'gb' 'gk' 'h5' 'hk' 'hp' 'hy' 'i8' 'ia' 'ic' 'ih' 'in' 'is' 'iw' 'jd' 'je' 'kh' 'kp' 'ks' 'lg' 'lz' 'mx' 'my' 'nf' 'nh' 'no' 'nv' 'o4' 'oa' 'oc' 'op' 'p2' 'p5' 'pa' 'pb' 'pt' 'pw' 'q2' 'q4' 'qb' 'qg' 'qh' 'qq' 'qs' 'qt' 'qx' 'qy' 'qz' 'r7' 'rc' 'rk' 'rt' 'sr' 'sx' 'sy' 'tc' 'tn' 'tr' 'tv' 'u1' 'u7' 'ua' 'uh' 'uz' 'vi' 'wd' 'wr' 'x3' 'x5' 'xa' 'xe' 'y5' 'y7' 'yb' 'yg' 'yk' 'ym' 'yr' 'yt' 'z7' 'zc' 'zf' 'zj' 'zp' - f | '5k' 'a1' 'ab' 'an' 'ar' 'as' 'av' 'ax' 'br' 'c2' 'c7' 'd5' 'da' 'dl' 'dr' 'dz' 'e3' 'ec' 'ed' 'ek' 'el' 'em' 'eu' 'ew' 'fd' 'ff' 'fw' 'g8' 'gb' 'gl' 'hh' 'hs' 'hz' 'i6' 'ia' 'ig' 'ii' 'ik' 'iq' 'ix' 'j0' 'j9' 'jf' 'jl' 'jo' 'jw' 'ko' 'kt' 'lm' 'nl' 'nm' 'ov' 'p3' 'p6' 'p7' 'pg' 'pl' 'pn' 'pp' 'qd' 'qe' 'qf' 'qn' 'qo' 'qq' 'qs' 'qt' 'qv' 'r9' 'rf' 'rj' 'rt' 'rw' 'sa' 'sl' 't2' 'tg' 'tk' 'tq' 'ty' 'ua' 'ud' 'vi' 'vm' 'w2' 'w6' 'wb' 'wd' 'wf' 'wi' 'wl' 'wq' 'wr' 'wy' 'x5' 'x8' 'y0' 'y1' 'ys' 'yx' - f | '6f' 'a1' 'ag' 'ak' 'ap' 'au' 'b1' 'b5' 'bi' 'c1' 'cu' 'd5' 'dc' 'dr' 'dv' 'eg' 'ej' 'ek' 'em' 'et' 'fe' 'fr' 'fz' 'ga' 'gb' 'gk' 'gu' 'gv' 'h5' 'hh' 'ho' 'hy' 'ii' 'ik' 'ip' 'iv' 'ja' 'jg' 'jz' 'k0' 'kt' 'm6' 'mj' 'nd' 'o0' 'o1' 'oj' 'or' 'ot' 'ov' 'oz' 'ph' 'pm' 'pv' 'qa' 'qe' 'qf' 'qg' 'qh' 'qj' 'qn' 'qt' 'qy' 'r5' 'rd' 'rg' 'rs' 'ru' 'rz' 's7' 'sf' 'si' 'sl' 'sw' 'sy' 't0' 't3' 'th' 'tn' 'tq' 'tu' 'ub' 'us' 'ux' 'vb' 'vo' 'wa' 'wd' 'wn' 'wq' 'wt' 'ww' 'wx' 'y1' 'yh' 'yo' 'yq' 'yz' 'z7' 'ze' - f | 'an' 'as' 'cm' 'dh' 'dk' 'do' 'ds' 'dv' 'e1' 'eh' 'ek' 'el' 'er' 'ew' 'ff' 'fo' 'fq' 'g2' 'gz' 'h0' 'hi' 'hk' 'hm' 'hs' 'i4' 'ij' 'iy' 'j5' 'jj' 'jk' 'jw' 'kq' 'kx' 'ky' 'lo' 'lp' 'lw' 'ly' 'm2' 'm6' 'md' 'mv' 'ng' 'of' 'om' 'oy' 'p7' 'pr' 'q7' 'qc' 'ql' 'qq' 'qs' 'qy' 'qz' 'r2' 'r9' 'ra' 'rn' 'ro' 'rq' 'rv' 'rx' 'ry' 'rz' 's0' 'si' 'sy' 'sz' 't6' 't7' 'to' 'tt' 'tv' 'tx' 'ub' 'ur' 'uv' 've' 'vj' 'w3' 'w8' 'w9' 'wa' 'wb' 'wc' 'we' 'wm' 'wt' 'wu' 'wx' 'wy' 'y9' 'yd' 'yg' 'ym' 'yp' 'yt' 'yu' 'yy' - f | '1h' '1j' '1o' '2d' 'ac' 'ak' 'b6' 'cx' 'd3' 'ds' 'dv' 'e7' 'es' 'ev' 'ez' 'fs' 'fx' 'fy' 'gc' 'gh' 'gs' 'gt' 'hk' 'ho' 'i8' 'id' 'ig' 'ii' 'iq' 'j5' 'jd' 'je' 'jg' 'jk' 'jl' 'jo' 'jr' 'kf' 'kl' 'kq' 'lf' 'lh' 'lz' 'mb' 'mu' 'ni' 'nz' 'o4' 'ol' 'p7' 'pl' 'pm' 'pv' 'q5' 'qa' 'qe' 'qh' 'qi' 'ql' 'qq' 'qw' 'ra' 'rb' 'rn' 's5' 'sf' 'sh' 'so' 'sp' 'sq' 'sw' 'tj' 'tu' 'ty' 'u3' 'ue' 'uo' 'ux' 'v8' 'vo' 'vr' 'vz' 'w4' 'w8' 'wa' 'wk' 'wl' 'wo' 'wr' 'wu' 'ww' 'xs' 'xx' 'yb' 'yf' 'yn' 'yt' 'yw' 'zf' 'zg' 'zs' - f | '1l' '2l' 'ag' 'aj' 'al' 'an' 'ay' 'bo' 'cg' 'cs' 'cw' 'cx' 'db' 'dd' 'e1' 'e8' 'ee' 'ef' 'ej' 'eu' 'ew' 'fc' 'ff' 'fh' 'fo' 'fv' 'fz' 'g1' 'gj' 'gk' 'gw' 'ha' 'i3' 'ib' 'iu' 'jv' 'k9' 'kq' 'ku' 'kw' 'lv' 'lw' 'ly' 'mp' 'my' 'nf' 'ng' 'nw' 'o6' 'oe' 'or' 'ou' 'oz' 'p4' 'pc' 'ph' 'pn' 'py' 'q0' 'qd' 'qe' 'qp' 'qs' 'qu' 'qv' 'qz' 'r6' 'rg' 'rj' 'rt' 'ru' 'rv' 'rw' 'sx' 't2' 'tl' 'tn' 'tq' 'tx' 'tz' 'ue' 'ui' 'ul' 'uo' 'uu' 'w0' 'w1' 'w4' 'w6' 'w8' 'w9' 'wh' 'wi' 'wp' 'ws' 'wt' 'wx' 'yd' 'zd' 'zf' 'zx' - f | '1x' '3m' 'a1' 'a5' 'a7' 'al' 'aq' 'ar' 'c8' 'cc' 'cd' 'd0' 'dt' 'e6' 'ei' 'em' 'ez' 'f6' 'fc' 'ff' 'fg' 'fp' 'gm' 'h3' 'ha' 'hg' 'ho' 'hs' 'ib' 'ie' 'il' 'it' 'ix' 'j3' 'jl' 'jt' 'jv' 'jw' 'kd' 'ki' 'l1' 'lk' 'lr' 'lv' 'm0' 'mx' 'my' 'n6' 'ni' 'o2' 'o4' 'o9' 'om' 'p0' 'p6' 'pi' 'pj' 'q5' 'qa' 'qc' 'qg' 'qp' 'qs' 'qu' 'qw' 'rg' 'rt' 'rz' 'se' 'sj' 'sp' 'su' 'sw' 'te' 'tp' 'tq' 'tt' 'tu' 'tx' 'u3' 'un' 'uo' 'up' 'uq' 'vc' 'vj' 'vu' 'w2' 'wh' 'wj' 'ws' 'wv' 'ww' 'xg' 'yc' 'yf' 'yv' 'z2' 'zl' 'zw' 'zy' - f | '5q' '7c' 'a1' 'ac' 'ao' 'az' 'bw' 'cz' 'd8' 'dr' 'dy' 'ec' 'eg' 'ej' 'el' 'es' 'et' 'fa' 'fh' 'g2' 'gm' 'go' 'gp' 'gy' 'h6' 'hd' 'he' 'ho' 'ij' 'ik' 'it' 'jc' 'ji' 'k8' 'ku' 'l8' 'lf' 'mw' 'n8' 'nb' 'nd' 'o3' 'ok' 'ol' 'ot' 'p8' 'pi' 'pl' 'pn' 'ps' 'qd' 'qe' 'qk' 'ql' 'qt' 'qz' 'r0' 'r7' 'rd' 'rg' 'rh' 'rm' 'ro' 'rp' 'ru' 'rz' 'sj' 'sm' 'sr' 'su' 'sv' 'sx' 't1' 'tc' 'tl' 'tq' 'uj' 'um' 'uv' 'uw' 'wa' 'wb' 'wi' 'wo' 'wr' 'ws' 'wv' 'wz' 'xb' 'xg' 'ya' 'yb' 'yh' 'yi' 'ym' 'yn' 'yr' 'yv' 'z6' 'zf' 'zz' - f | 'ab' 'ad' 'al' 'cm' 'cw' 'dh' 'dn' 'dy' 'e8' 'ea' 'ed' 'ej' 'eq' 'er' 'eu' 'ev' 'f5' 'fb' 'fj' 'fr' 'gc' 'gp' 'h2' 'h6' 'hc' 'hw' 'i3' 'i5' 'id' 'ir' 'ix' 'ka' 'kj' 'ko' 'lv' 'lx' 'mj' 'mv' 'nq' 'ns' 'oa' 'od' 'ok' 'om' 'os' 'p1' 'p5' 'pb' 'pg' 'pj' 'pl' 'ps' 'pu' 'q1' 'qb' 'qd' 'qe' 'qj' 'qk' 'ql' 'qr' 'qs' 'rd' 'rl' 'ro' 'rs' 'rx' 's1' 'sf' 'sg' 't0' 'tb' 'te' 'tf' 'tg' 'tl' 'tp' 'tq' 'u3' 'uf' 'ug' 'up' 'uq' 'vx' 'vy' 'w5' 'wa' 'wc' 'wr' 'ws' 'wu' 'wx' 'wy' 'wz' 'yf' 'ym' 'yz' 'ze' 'zs' 'zy' 'zz' - f | '13' '6k' 'ae' 'ah' 'an' 'at' 'aw' 'cc' 'cn' 'du' 'dx' 'ek' 'el' 'em' 'en' 'es' 'ey' 'f2' 'fe' 'fh' 'fk' 'fv' 'g6' 'g9' 'gw' 'hj' 'hn' 'hs' 'hu' 'hv' 'ia' 'im' 'iq' 'ix' 'jh' 'jm' 'ki' 'lj' 'lo' 'lt' 'lw' 'm1' 'nv' 'nw' 'ny' 'oj' 'oo' 'ot' 'ov' 'ox' 'pd' 'pv' 'q1' 'q7' 'qc' 'qd' 'qj' 'ql' 'qp' 'qq' 'qv' 'qw' 'qx' 'r7' 'ry' 'so' 'st' 'su' 'sx' 't9' 'tf' 'th' 'ty' 'uh' 'uo' 'ut' 'uv' 'uy' 'vj' 'vp' 'vu' 'vw' 'w3' 'w5' 'w8' 'wc' 'we' 'wg' 'wi' 'wl' 'wv' 'xh' 'xx' 'yb' 'yd' 'yj' 'yn' 'yo' 'yr' 'yy' 'zk' 'zx' - f | '4m' 'a3' 'ac' 'ag' 'ai' 'au' 'av' 'bd' 'bu' 'cl' 'd0' 'dc' 'dy' 'ej' 'el' 'eo' 'es' 'eu' 'ex' 'fr' 'fx' 'g6' 'g9' 'gk' 'gq' 'gr' 'gw' 'gx' 'gz' 'hv' 'ii' 'ix' 'jj' 'jy' 'k0' 'kl' 'kn' 'kw' 'l5' 'lo' 'lz' 'm1' 'n5' 'n9' 'nf' 'nm' 'nz' 'ol' 'os' 'ot' 'oy' 'p1' 'p9' 'pa' 'pj' 'pl' 'pp' 'qc' 'qh' 'qj' 'qr' 'qs' 'qw' 'qz' 'rg' 'ri' 'rl' 'rn' 'rp' 'rt' 'rx' 'rz' 'se' 'sl' 'sv' 'sy' 't8' 'td' 'th' 'tr' 'tx' 'u3' 'u7' 'uc' 'ud' 'ul' 'un' 'up' 'uv' 've' 'vz' 'w7' 'wr' 'y0' 'y6' 'yh' 'yo' 'yu' 'yy' 'yz' 'z6' 'zm' - f | '6p' 'an' 'aq' 'au' 'br' 'bz' 'c3' 'ca' 'cg' 'cn' 'db' 'dk' 'dq' 'e0' 'e8' 'ek' 'eo' 'er' 'ez' 'fd' 'ft' 'g0' 'gd' 'gh' 'gk' 'gn' 'gr' 'gv' 'gy' 'hb' 'hc' 'he' 'ht' 'ii' 'ip' 'iu' 'j9' 'jn' 'jo' 'jq' 'jz' 'kh' 'kn' 'ko' 'l3' 'ls' 'lz' 'nh' 'nk' 'ok' 'oy' 'p7' 'pd' 'ph' 'pu' 'pw' 'py' 'q7' 'qa' 'qi' 'qk' 'ql' 'qn' 'qq' 'qr' 'qs' 'qv' 'qx' 'qy' 'rg' 'rs' 'ru' 'sg' 'sl' 'sq' 'sr' 'su' 'ts' 'tt' 'ui' 'um' 'ut' 'uu' 'v0' 'v5' 'w7' 'wb' 'wc' 'wf' 'wi' 'wm' 'xd' 'xs' 'xz' 'y3' 'y4' 'y9' 'yi' 'yp' 'yx' 'z0' 'zf' - f | '1f' '3z' '43' 'am' 'b9' 'bg' 'cc' 'ct' 'cx' 'd0' 'de' 'dm' 'dr' 'e0' 'e8' 'ef' 'en' 'ev' 'fd' 'ff' 'fx' 'ga' 'gm' 'gp' 'gr' 'gs' 'gy' 'h1' 'hi' 'hl' 'hs' 'i4' 'i5' 'ic' 'jb' 'jj' 'kj' 'lh' 'ng' 'ni' 'nn' 'ns' 'nx' 'o1' 'oa' 'oe' 'og' 'p4' 'pd' 'ph' 'pj' 'pl' 'pw' 'q9' 'qa' 'qd' 'qh' 'ql' 'qr' 'qs' 'qw' 'qx' 'ro' 'rs' 'rw' 'ry' 'rz' 'sb' 'sj' 'sm' 'so' 't0' 't6' 'tc' 'ti' 'tk' 'tn' 'uk' 'um' 'uw' 'uy' 'v9' 'vd' 'vg' 'w3' 'wf' 'wg' 'wi' 'wk' 'wm' 'wx' 'xe' 'xm' 'xn' 'y5' 'ye' 'yk' 'yq' 'z3' 'zj' 'zk' 'zt' 'zz' - f | '1t' 'ai' 'cu' 'cw' 'cx' 'dd' 'de' 'ds' 'e0' 'e2' 'e6' 'eb' 'ei' 'eq' 'eu' 'ev' 'ez' 'f5' 'f8' 'fc' 'g2' 'gd' 'gs' 'gv' 'hu' 'hy' 'id' 'ig' 'ij' 'ir' 'iv' 'ju' 'ka' 'kj' 'kl' 'ks' 'kv' 'kw' 'kx' 'la' 'lh' 'lm' 'ls' 'lv' 'lz' 'mg' 'mh' 'mp' 'ns' 'nt' 'nu' 'nx' 'o6' 'o9' 'oc' 'oj' 'pa' 'pj' 'pl' 'pv' 'q3' 'q5' 'qb' 'qh' 'ql' 'qn' 'qr' 'qs' 'qz' 'rb' 'rf' 'rh' 'rm' 'sm' 'sn' 'sw' 't5' 't9' 'tq' 'ty' 'uj' 'v2' 'vz' 'w6' 'wc' 'wg' 'wi' 'wj' 'wk' 'wm' 'wn' 'wt' 'wv' 'ww' 'wz' 'x0' 'xb' 'xc' 'yb' 'yt' 'yv' 'zp' 'zy' - f | 'a3' 'ad' 'ar' 'at' 'bb' 'bf' 'bt' 'cg' 'cx' 'd6' 'de' 'df' 'e4' 'e6' 'eg' 'et' 'ex' 'f0' 'fe' 'fg' 'fj' 'fo' 'gh' 'hb' 'hj' 'hq' 'hr' 'hv' 'ia' 'id' 'is' 'it' 'iy' 'ja' 'jj' 'jq' 'jw' 'l2' 'l7' 'lc' 'lu' 'nc' 'no' 'np' 'nt' 'ob' 'od' 'og' 'oo' 'os' 'pe' 'pj' 'pl' 'po' 'pq' 'pu' 'q2' 'qa' 'qc' 'qf' 'qr' 'qt' 'qx' 're' 'rk' 'rn' 'ro' 'ru' 'rw' 'rx' 's7' 's8' 'sy' 'tc' 'tf' 'tg' 'th' 'tm' 'to' 'tv' 'tx' 'tz' 'va' 'vp' 'w0' 'w1' 'wh' 'wl' 'wq' 'wr' 'wt' 'ww' 'wy' 'x7' 'xl' 'xy' 'y0' 'ye' 'yr' 'yy' 'z3' 'zf' 'zo' - f | 'ad' 'ar' 'az' 'b7' 'cf' 'cm' 'ct' 'cw' 'cy' 'dh' 'dn' 'ds' 'ef' 'en' 'eo' 'er' 'fh' 'fi' 'fq' 'fr' 'fx' 'gp' 'gq' 'gu' 'gx' 'h8' 'hf' 'hj' 'hk' 'ho' 'hw' 'hy' 'i3' 'i4' 'ik' 'iu' 'iy' 'jj' 'kn' 'l8' 'lb' 'lg' 'lo' 'm7' 'mx' 'nj' 'nt' 'o6' 'ob' 'oh' 'ok' 'ot' 'pr' 'pz' 'q7' 'q8' 'qa' 'qd' 'qg' 'qh' 'qi' 'qn' 'qz' 'r7' 're' 'ri' 'rk' 'ry' 's4' 'sa' 'sd' 'sm' 'sn' 'sp' 'sw' 'sy' 'tf' 'th' 'to' 'tr' 'tv' 'tz' 'u0' 'u1' 'u5' 'ue' 'uk' 'uq' 'vb' 'vp' 'vr' 'vu' 'wa' 'wb' 'wg' 'wi' 'wk' 'wm' 'wt' 'ya' 'yj' 'yl' 'z3' - f | '12' 'ac' 'ay' 'bc' 'bj' 'bm' 'bo' 'ce' 'cf' 'ck' 'cr' 'db' 'do' 'du' 'dy' 'ea' 'ej' 'ek' 'eo' 'ep' 'et' 'f2' 'fc' 'fl' 'fs' 'fy' 'g9' 'gf' 'hj' 'hk' 'hp' 'i1' 'i5' 'ih' 'ii' 'im' 'jp' 'jx' 'k9' 'kf' 'ky' 'mb' 'mj' 'mk' 'nb' 'nc' 'o0' 'o9' 'oc' 'oj' 'oq' 'pm' 'ps' 'pt' 'pu' 'pv' 'py' 'qd' 'qi' 'qj' 'qk' 'ql' 'qm' 'qs' 'qz' 'ra' 'rc' 'rd' 'rh' 'ri' 'ro' 's0' 's3' 's7' 's9' 'sv' 't2' 't7' 'tm' 'tp' 'tw' 'tx' 'ty' 'u3' 'u6' 'uf' 'ug' 'ui' 'uk' 'ut' 'ux' 'v5' 'w3' 'wk' 'wt' 'xo' 'xq' 'xr' 'xu' 'ye' 'yl' 'yn' 'yt' 'yv' 'zm' - f | '1o' '7o' 'a1' 'aq' 'b4' 'bi' 'bp' 'c9' 'cb' 'cd' 'cf' 'co' 'cp' 'd3' 'd8' 'ds' 'dv' 'dy' 'eb' 'ee' 'ef' 'eh' 'em' 'eo' 'es' 'ez' 'fi' 'fk' 'fl' 'gj' 'gn' 'gt' 'h1' 'hn' 'ho' 'hp' 'hu' 'ic' 'ik' 'il' 'im' 'ir' 'iw' 'ji' 'jp' 'k4' 'ks' 'm0' 'ml' 'n0' 'ns' 'oj' 'on' 'or' 'os' 'ov' 'pr' 'pv' 'px' 'py' 'q6' 'qa' 'qc' 'qg' 'qj' 'qm' 'qn' 'qp' 'qq' 'qs' 'qv' 'qz' 'r0' 'ra' 's0' 'te' 'tg' 'tm' 'to' 'tw' 'u1' 'u7' 'ug' 'um' 'uw' 'ux' 'uz' 'vm' 'vu' 'w4' 'wg' 'wh' 'wj' 'wk' 'wm' 'wo' 'wq' 'ws' 'wt' 'wz' 'x0' 'x5' 'yg' 'yn' 'z6' - f | '2l' '9f' 'a2' 'ak' 'as' 'av' 'bi' 'c1' 'cf' 'ct' 'cu' 'db' 'dp' 'du' 'dv' 'e3' 'e9' 'ee' 'eg' 'el' 'fm' 'gu' 'hc' 'he' 'i3' 'i9' 'ib' 'ik' 'im' 'ir' 'j7' 'jp' 'jv' 'ki' 'kl' 'l6' 'lj' 'lo' 'lw' 'md' 'mj' 'mk' 'ms' 'mw' 'na' 'nq' 'o2' 'od' 'ox' 'p3' 'pj' 'pp' 'q6' 'q7' 'q9' 'qb' 'qg' 'qr' 'qs' 'r7' 'r8' 'rd' 're' 'rf' 'rg' 'rp' 'rv' 'rw' 's6' 'sc' 'sq' 't6' 'tb' 'tc' 'te' 'tj' 'tn' 'tx' 'tz' 'uh' 'uq' 'uu' 'v4' 'vw' 'w3' 'w8' 'wa' 'wj' 'wk' 'wp' 'x3' 'xg' 'xy' 'y1' 'y6' 'yc' 'yi' 'yn' 'yo' 'yw' 'yz' 'z6' 'z8' 'zk' 'zz' - f | '80' 'ak' 'al' 'an' 'aq' 'as' 'at' 'bg' 'bn' 'bq' 'bx' 'd4' 'db' 'dg' 'dq' 'dv' 'ef' 'ej' 'en' 'eu' 'ex' 'f9' 'fu' 'g1' 'g2' 'g3' 'gj' 'gn' 'gs' 'gt' 'gx' 'h4' 'h8' 'hb' 'hn' 'hx' 'hy' 'i5' 'im' 'ji' 'jo' 'jy' 'k1' 'kb' 'kh' 'kp' 'kv' 'l4' 'lf' 'lt' 'ml' 'mv' 'na' 'ny' 'o2' 'oj' 'oq' 'or' 'os' 'pp' 'q3' 'q8' 'qc' 'qf' 'qp' 'qt' 'qv' 'rc' 'rg' 'rm' 's4' 'sa' 'sk' 'sp' 'su' 'sv' 'sy' 't5' 'te' 'tm' 'tn' 'to' 'tt' 'tw' 'tx' 'tz' 'ue' 'ug' 'ul' 'un' 'uq' 'us' 'v3' 'wd' 'wr' 'wu' 'wy' 'x3' 'x8' 'xk' 'yf' 'yj' 'yk' 'yp' 'z0' - f | '1b' '42' 'a7' 'ab' 'ak' 'ap' 'at' 'av' 'ay' 'b0' 'b9' 'bb' 'bp' 'bu' 'bz' 'cq' 'da' 'de' 'dn' 'e0' 'eb' 'ef' 'eg' 'ek' 'eq' 'er' 'eu' 'ey' 'fn' 'ft' 'gg' 'h4' 'hk' 'hl' 'i7' 'ig' 'ik' 'ip' 'ir' 'iu' 'iw' 'jr' 'jw' 'jx' 'kg' 'lc' 'lg' 'm0' 'na' 'np' 'om' 'on' 'oz' 'pg' 'pn' 'ps' 'pt' 'pz' 'q3' 'q6' 'qa' 'qb' 'ql' 'qq' 'qt' 'qv' 'qw' 'qy' 'r8' 'rf' 'ri' 'rk' 'rl' 'rw' 'sg' 'si' 'sp' 'sw' 'ta' 'th' 'ua' 'uj' 'uu' 'uv' 'uz' 'vj' 'vk' 'vm' 'wc' 'wf' 'wh' 'wn' 'wo' 'ww' 'xb' 'xk' 'xt' 'xw' 'y7' 'ye' 'yl' 'yt' 'yw' 'z4' 'z7' 'zc' 'zw' - f | '1h' '3s' 'ab' 'ae' 'ax' 'b1' 'bz' 'cy' 'dk' 'dq' 'ds' 'du' 'e8' 'ef' 'ej' 'ek' 'ex' 'f1' 'fe' 'ff' 'fn' 'fo' 'ft' 'fx' 'ge' 'go' 'gz' 'h6' 'hz' 'i2' 'iv' 'iy' 'j5' 'j6' 'ke' 'kf' 'lh' 'lr' 'mc' 'mj' 'na' 'ng' 'oh' 'om' 'oy' 'p2' 'pi' 'pk' 'py' 'q3' 'qb' 'qc' 'qg' 'qn' 'qo' 'qq' 'qu' 'qw' 'qx' 'qy' 'qz' 'r1' 'rk' 'rl' 'rq' 'rs' 'rt' 'ry' 'rz' 'sk' 'sl' 'so' 't9' 'td' 'te' 'tn' 'tw' 'tz' 'ud' 'uk' 'uo' 'uq' 'uw' 'ux' 'uy' 'v1' 'vg' 'vq' 'w4' 'w9' 'wa' 'wg' 'wj' 'wm' 'wo' 'wr' 'ww' 'wy' 'xf' 'xg' 'y9' 'yh' 'yi' 'yk' 'ym' 'yq' 'yv' 'zm' - t | -(514 rows) - -drop index wowidx; -create index wowidx on test_tsvector using gin (a); -set enable_seqscan=off; -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; - count -------- - 158 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; - count -------- - 17 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; - count -------- - 6 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; - count -------- - 98 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; - count -------- - 23 -(1 row) - -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; - count -------- - 39 -(1 row) - diff --git a/contrib/tsearch2/sql/tsearch2.sql b/contrib/tsearch2/sql/tsearch2.sql deleted file mode 100644 index 53e3073af4..0000000000 --- a/contrib/tsearch2/sql/tsearch2.sql +++ /dev/null @@ -1,334 +0,0 @@ -CREATE EXTENSION tsearch2; - ---tsvector -SELECT '1'::tsvector; -SELECT '1 '::tsvector; -SELECT ' 1'::tsvector; -SELECT ' 1 '::tsvector; -SELECT '1 2'::tsvector; -SELECT '''1 2'''::tsvector; -SELECT E'''1 \\''2'''::tsvector; -SELECT E'''1 \\''2''3'::tsvector; -SELECT E'''1 \\''2'' 3'::tsvector; -SELECT E'''1 \\''2'' '' 3'' 4 '::tsvector; -select '''w'':4A,3B,2C,1D,5 a:8'; -select 'a:3A b:2a'::tsvector || 'ba:1234 a:1B'; -select setweight('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd zxc:81,567,222A'::tsvector, 'c'); -select strip('w:12B w:13* w:12,5,6 a:1,3* a:3 w asd:1dc asd'::tsvector); - - ---tsquery -SELECT '1'::tsquery; -SELECT '1 '::tsquery; -SELECT ' 1'::tsquery; -SELECT ' 1 '::tsquery; -SELECT '''1 2'''::tsquery; -SELECT E'''1 \\''2'''::tsquery; -SELECT '!1'::tsquery; -SELECT '1|2'::tsquery; -SELECT '1|!2'::tsquery; -SELECT '!1|2'::tsquery; -SELECT '!1|!2'::tsquery; -SELECT '!(!1|!2)'::tsquery; -SELECT '!(!1|2)'::tsquery; -SELECT '!(1|!2)'::tsquery; -SELECT '!(1|2)'::tsquery; -SELECT '1&2'::tsquery; -SELECT '!1&2'::tsquery; -SELECT '1&!2'::tsquery; -SELECT '!1&!2'::tsquery; -SELECT '(1&2)'::tsquery; -SELECT '1&(2)'::tsquery; -SELECT '!(1)&2'::tsquery; -SELECT '!(1&2)'::tsquery; -SELECT '1|2&3'::tsquery; -SELECT '1|(2&3)'::tsquery; -SELECT '(1|2)&3'::tsquery; -SELECT '1|2&!3'::tsquery; -SELECT '1|!2&3'::tsquery; -SELECT '!1|2&3'::tsquery; -SELECT '!1|(2&3)'::tsquery; -SELECT '!(1|2)&3'::tsquery; -SELECT '(!1|2)&3'::tsquery; -SELECT '1|(2|(4|(5|6)))'::tsquery; -SELECT '1|2|4|5|6'::tsquery; -SELECT '1&(2&(4&(5&6)))'::tsquery; -SELECT '1&2&4&5&6'::tsquery; -SELECT '1&(2&(4&(5|6)))'::tsquery; -SELECT '1&(2&(4&(5|!6)))'::tsquery; -SELECT E'1&(''2''&('' 4''&(\\|5 | ''6 \\'' !|&'')))'::tsquery; -SELECT '''the wether'':dc & '' sKies '':BC & a:d b:a'; - -select 'a' < 'b & c'::tsquery; -select 'a' > 'b & c'::tsquery; -select 'a | f' < 'b & c'::tsquery; -select 'a | ff' < 'b & c'::tsquery; -select 'a | f | g' < 'b & c'::tsquery; - -select numnode( 'new'::tsquery ); -select numnode( 'new & york'::tsquery ); -select numnode( 'new & york | qwery'::tsquery ); - -create table test_tsquery (txtkeyword text, txtsample text); -\set ECHO none -\copy test_tsquery from stdin -'New York' new & york | big & apple | nyc -Moscow moskva | moscow -'Sanct Peter' Peterburg | peter | 'Sanct Peterburg' -'foo bar qq' foo & (bar | qq) & city -\. -\set ECHO all - -alter table test_tsquery add column keyword tsquery; -update test_tsquery set keyword = to_tsquery('english', txtkeyword); -alter table test_tsquery add column sample tsquery; -update test_tsquery set sample = to_tsquery('english', txtsample::text); - -create unique index bt_tsq on test_tsquery (keyword); - -select count(*) from test_tsquery where keyword < 'new & york'; -select count(*) from test_tsquery where keyword <= 'new & york'; -select count(*) from test_tsquery where keyword = 'new & york'; -select count(*) from test_tsquery where keyword >= 'new & york'; -select count(*) from test_tsquery where keyword > 'new & york'; - -set enable_seqscan=off; - -select count(*) from test_tsquery where keyword < 'new & york'; -select count(*) from test_tsquery where keyword <= 'new & york'; -select count(*) from test_tsquery where keyword = 'new & york'; -select count(*) from test_tsquery where keyword >= 'new & york'; -select count(*) from test_tsquery where keyword > 'new & york'; - -set enable_seqscan=on; - -select rewrite('foo & bar & qq & new & york', 'new & york'::tsquery, 'big & apple | nyc | new & york & city'); - -select rewrite('moscow', 'select keyword, sample from test_tsquery'::text ); -select rewrite('moscow & hotel', 'select keyword, sample from test_tsquery'::text ); -select rewrite('bar & new & qq & foo & york', 'select keyword, sample from test_tsquery'::text ); - -select rewrite( ARRAY['moscow', keyword, sample] ) from test_tsquery; -select rewrite( ARRAY['moscow & hotel', keyword, sample] ) from test_tsquery; -select rewrite( ARRAY['bar & new & qq & foo & york', keyword, sample] ) from test_tsquery; - - -select keyword from test_tsquery where keyword @> 'new'; -select keyword from test_tsquery where keyword @> 'moscow'; -select keyword from test_tsquery where keyword <@ 'new'; -select keyword from test_tsquery where keyword <@ 'moscow'; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; - -create index qq on test_tsquery using gist (keyword gist_tp_tsquery_ops); -set enable_seqscan='off'; - -select keyword from test_tsquery where keyword @> 'new'; -select keyword from test_tsquery where keyword @> 'moscow'; -select keyword from test_tsquery where keyword <@ 'new'; -select keyword from test_tsquery where keyword <@ 'moscow'; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where keyword <@ query; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'moscow & hotel') as query where query @> keyword; -select rewrite( ARRAY[query, keyword, sample] ) from test_tsquery, to_tsquery('english', 'bar & new & qq & foo & york') as query where query @> keyword; -set enable_seqscan='on'; - - - -select lexize('simple', 'ASD56 hsdkf'); -select lexize('english_stem', 'SKIES Problems identity'); - -select * from token_type('default'); -select * from parse('default', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf <fr>qwer jf sdjk<we hjwer <werrwe> ewr1> ewri2 <a href="qwe<qwe>"> -/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 -<i <b> wow < jqw <> qwerty'); - -SELECT to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf <fr>qwer jf sdjk<we hjwer <werrwe> ewr1> ewri2 <a href="qwe<qwe>"> -/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 -<i <b> wow < jqw <> qwerty'); - -SELECT length(to_tsvector('english', '345 qw')); - -SELECT length(to_tsvector('english', '345 qwe@efd.r '' http://www.com/ http://aew.werc.ewr/?ad=qwe&dw 1aew.werc.ewr/?ad=qwe&dw 2aew.werc.ewr http://3aew.werc.ewr/?ad=qwe&dw http://4aew.werc.ewr http://5aew.werc.ewr:8100/? ad=qwe&dw 6aew.werc.ewr:8100/?ad=qwe&dw 7aew.werc.ewr:8100/?ad=qwe&dw=%20%32 +4.0e-10 qwe qwe qwqwe 234.435 455 5.005 teodor@stack.net qwe-wer asdf <fr>qwer jf sdjk<we hjwer <werrwe> ewr1> ewri2 <a href="qwe<qwe>"> -/usr/local/fff /awdf/dwqe/4325 rewt/ewr wefjn /wqe-324/ewr gist.h gist.h.c gist.c. readline 4.2 4.2. 4.2, readline-4.2 readline-4.2. 234 -<i <b> wow < jqw <> qwerty')); - - -select to_tsquery('english', 'qwe & sKies '); -select to_tsquery('simple', 'qwe & sKies '); -select to_tsquery('english', '''the wether'':dc & '' sKies '':BC '); -select to_tsquery('english', 'asd&(and|fghj)'); -select to_tsquery('english', '(asd&and)|fghj'); -select to_tsquery('english', '(asd&!and)|fghj'); -select to_tsquery('english', '(the|and&(i&1))&fghj'); - -select plainto_tsquery('english', 'the and z 1))& fghj'); -select plainto_tsquery('english', 'foo bar') && plainto_tsquery('english', 'asd'); -select plainto_tsquery('english', 'foo bar') || plainto_tsquery('english', 'asd fg'); -select plainto_tsquery('english', 'foo bar') || !!plainto_tsquery('english', 'asd fg'); -select plainto_tsquery('english', 'foo bar') && 'asd | fg'; - -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca'; -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:B'; -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:A'; -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:C'; -select 'a b:89 ca:23A,64b d:34c'::tsvector @@ 'd:AC & ca:CB'; - -CREATE TABLE test_tsvector( t text, a tsvector ); - -\copy test_tsvector from 'data/test_tsearch.data' - -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; - -create index wowidx on test_tsvector using gist (a); -set enable_seqscan=off; - -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; - -select set_curcfg('english'); - -CREATE TRIGGER tsvectorupdate -BEFORE UPDATE OR INSERT ON test_tsvector -FOR EACH ROW EXECUTE PROCEDURE tsearch2(a, t); - -SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - -INSERT INTO test_tsvector (t) VALUES ('345 qwerty'); - -SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - -UPDATE test_tsvector SET t = null WHERE t = '345 qwerty'; - -SELECT count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); - -insert into test_tsvector (t) values ('345 qwerty copyright'); -select count(*) FROM test_tsvector WHERE a @@ to_tsquery('345&qwerty'); -select count(*) FROM test_tsvector WHERE a @@ to_tsquery('copyright'); - -select rank(' a:1 s:2C d g'::tsvector, 'a | s'); -select rank(' a:1 s:2B d g'::tsvector, 'a | s'); -select rank(' a:1 s:2 d g'::tsvector, 'a | s'); -select rank(' a:1 s:2C d g'::tsvector, 'a & s'); -select rank(' a:1 s:2B d g'::tsvector, 'a & s'); -select rank(' a:1 s:2 d g'::tsvector, 'a & s'); - -insert into test_tsvector (t) values ('foo bar foo the over foo qq bar'); -drop trigger tsvectorupdate on test_tsvector; -select * from stat('select a from test_tsvector') order by ndoc desc, nentry desc, word collate "C"; -insert into test_tsvector values ('1', 'a:1a,2,3b b:5a,6a,7c,8'); -insert into test_tsvector values ('1', 'a:1a,2,3c b:5a,6b,7c,8b'); -select * from stat('select a from test_tsvector','a') order by ndoc desc, nentry desc, word collate "C"; -select * from stat('select a from test_tsvector','b') order by ndoc desc, nentry desc, word collate "C"; -select * from stat('select a from test_tsvector','c') order by ndoc desc, nentry desc, word collate "C"; -select * from stat('select a from test_tsvector','d') order by ndoc desc, nentry desc, word collate "C"; -select * from stat('select a from test_tsvector','ad') order by ndoc desc, nentry desc, word collate "C"; - -select to_tsquery('english', 'skies & books'); - -select rank_cd(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea&thousand&years')); - -select rank_cd(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('granite&sea')); - -select rank_cd(to_tsvector('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -'), to_tsquery('sea')); - -select headline('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -', to_tsquery('sea&thousand&years')); - -select headline('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -', to_tsquery('granite&sea')); - -select headline('Erosion It took the sea a thousand years, -A thousand years to trace -The granite features of this cliff -In crag and scarp and base. -It took the sea an hour one night -An hour of storm to place -The sculpture of these granite seams, -Upon a woman s face. E. J. Pratt (1882 1964) -', to_tsquery('sea')); - - -select headline(' -<html> -<!-- some comment --> -<body> -Sea view wow <u>foo bar</u> <i>qq</i> -<a href="http://www.google.com/foo.bar.html" target="_blank">YES </a> -ff-bg -<script> - document.write(15); -</script> -</body> -</html>', -to_tsquery('sea&foo'), 'HighlightAll=true'); ---check debug -select * from public.ts_debug('Tsearch module for PostgreSQL 7.3.3'); - ---check ordering -insert into test_tsvector values (null, null); -select a is null, a from test_tsvector order by a; - -drop index wowidx; -create index wowidx on test_tsvector using gin (a); -set enable_seqscan=off; - -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr|qh'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'wr&qh'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq&yt'; -SELECT count(*) FROM test_tsvector WHERE a @@ 'eq|yt'; -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq&yt)|(wr&qh)'; -SELECT count(*) FROM test_tsvector WHERE a @@ '(eq|yt)&(wr|qh)'; diff --git a/contrib/tsearch2/tsearch2--1.0.sql b/contrib/tsearch2/tsearch2--1.0.sql deleted file mode 100644 index a32c5fe85b..0000000000 --- a/contrib/tsearch2/tsearch2--1.0.sql +++ /dev/null @@ -1,576 +0,0 @@ -/* contrib/tsearch2/tsearch2--1.0.sql */ - --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION tsearch2" to load this file. \quit - --- These domains are just to catch schema-qualified references to the --- old data types. -CREATE DOMAIN tsvector AS pg_catalog.tsvector; -CREATE DOMAIN tsquery AS pg_catalog.tsquery; -CREATE DOMAIN gtsvector AS pg_catalog.gtsvector; -CREATE DOMAIN gtsq AS pg_catalog.text; - ---dict interface -CREATE FUNCTION lexize(oid, text) - RETURNS _text - as 'ts_lexize' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION lexize(text, text) - RETURNS _text - as 'MODULE_PATHNAME', 'tsa_lexize_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION lexize(text) - RETURNS _text - as 'MODULE_PATHNAME', 'tsa_lexize_bycurrent' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curdict(int) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_set_curdict' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curdict(text) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_set_curdict_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---built-in dictionaries -CREATE FUNCTION dex_init(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_dex_init' - LANGUAGE C; - -CREATE FUNCTION dex_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_dex_lexize' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION snb_en_init(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_snb_en_init' - LANGUAGE C; - -CREATE FUNCTION snb_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_snb_lexize' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION snb_ru_init_koi8(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_snb_ru_init_koi8' - LANGUAGE C; - -CREATE FUNCTION snb_ru_init_utf8(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_snb_ru_init_utf8' - LANGUAGE C; - -CREATE FUNCTION snb_ru_init(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_snb_ru_init' - LANGUAGE C; - -CREATE FUNCTION spell_init(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_spell_init' - LANGUAGE C; - -CREATE FUNCTION spell_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_spell_lexize' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION syn_init(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_syn_init' - LANGUAGE C; - -CREATE FUNCTION syn_lexize(internal,internal,int4) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_syn_lexize' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION thesaurus_init(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_thesaurus_init' - LANGUAGE C; - -CREATE FUNCTION thesaurus_lexize(internal,internal,int4,internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_thesaurus_lexize' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---sql-level interface -CREATE TYPE tokentype - as (tokid int4, alias text, descr text); - -CREATE FUNCTION token_type(int4) - RETURNS setof tokentype - as 'ts_token_type_byid' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT - ROWS 16; - -CREATE FUNCTION token_type(text) - RETURNS setof tokentype - as 'ts_token_type_byname' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT - ROWS 16; - -CREATE FUNCTION token_type() - RETURNS setof tokentype - as 'MODULE_PATHNAME', 'tsa_token_type_current' - LANGUAGE C - RETURNS NULL ON NULL INPUT - ROWS 16; - -CREATE FUNCTION set_curprs(int) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_set_curprs' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curprs(text) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_set_curprs_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE TYPE tokenout - as (tokid int4, token text); - -CREATE FUNCTION parse(oid,text) - RETURNS setof tokenout - as 'ts_parse_byid' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION parse(text,text) - RETURNS setof tokenout - as 'ts_parse_byname' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION parse(text) - RETURNS setof tokenout - as 'MODULE_PATHNAME', 'tsa_parse_current' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---default parser -CREATE FUNCTION prsd_start(internal,int4) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_prsd_start' - LANGUAGE C; - -CREATE FUNCTION prsd_getlexeme(internal,internal,internal) - RETURNS int4 - as 'MODULE_PATHNAME', 'tsa_prsd_getlexeme' - LANGUAGE C; - -CREATE FUNCTION prsd_end(internal) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_prsd_end' - LANGUAGE C; - -CREATE FUNCTION prsd_lextype(internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_prsd_lextype' - LANGUAGE C; - -CREATE FUNCTION prsd_headline(internal,internal,internal) - RETURNS internal - as 'MODULE_PATHNAME', 'tsa_prsd_headline' - LANGUAGE C; - ---tsearch config -CREATE FUNCTION set_curcfg(int) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_set_curcfg' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION set_curcfg(text) - RETURNS void - as 'MODULE_PATHNAME', 'tsa_set_curcfg_byname' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION show_curcfg() - RETURNS oid - AS 'get_current_ts_config' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT STABLE; - -CREATE FUNCTION length(tsvector) - RETURNS int4 - AS 'tsvector_length' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsvector(oid, text) - RETURNS tsvector - AS 'to_tsvector_byid' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsvector(text, text) - RETURNS tsvector - AS 'MODULE_PATHNAME', 'tsa_to_tsvector_name' - LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsvector(text) - RETURNS tsvector - AS 'to_tsvector' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION strip(tsvector) - RETURNS tsvector - AS 'tsvector_strip' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION setweight(tsvector,"char") - RETURNS tsvector - AS 'tsvector_setweight' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION concat(tsvector,tsvector) - RETURNS tsvector - AS 'tsvector_concat' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION querytree(tsquery) - RETURNS text - AS 'tsquerytree' - LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION to_tsquery(oid, text) - RETURNS tsquery - AS 'to_tsquery_byid' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsquery(text, text) - RETURNS tsquery - AS 'MODULE_PATHNAME','tsa_to_tsquery_name' - LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION to_tsquery(text) - RETURNS tsquery - AS 'to_tsquery' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION plainto_tsquery(oid, text) - RETURNS tsquery - AS 'plainto_tsquery_byid' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION plainto_tsquery(text, text) - RETURNS tsquery - AS 'MODULE_PATHNAME','tsa_plainto_tsquery_name' - LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION plainto_tsquery(text) - RETURNS tsquery - AS 'plainto_tsquery' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - ---Trigger -CREATE FUNCTION tsearch2() - RETURNS trigger - AS 'MODULE_PATHNAME', 'tsa_tsearch2' - LANGUAGE C; - ---Relevation -CREATE FUNCTION rank(float4[], tsvector, tsquery) - RETURNS float4 - AS 'ts_rank_wtt' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank(float4[], tsvector, tsquery, int4) - RETURNS float4 - AS 'ts_rank_wttf' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank(tsvector, tsquery) - RETURNS float4 - AS 'ts_rank_tt' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank(tsvector, tsquery, int4) - RETURNS float4 - AS 'ts_rank_ttf' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(float4[], tsvector, tsquery) - RETURNS float4 - AS 'ts_rankcd_wtt' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(float4[], tsvector, tsquery, int4) - RETURNS float4 - AS 'ts_rankcd_wttf' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(tsvector, tsquery) - RETURNS float4 - AS 'ts_rankcd_tt' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rank_cd(tsvector, tsquery, int4) - RETURNS float4 - AS 'ts_rankcd_ttf' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(oid, text, tsquery, text) - RETURNS text - AS 'ts_headline_byid_opt' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(oid, text, tsquery) - RETURNS text - AS 'ts_headline_byid' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, text, tsquery, text) - RETURNS text - AS 'MODULE_PATHNAME', 'tsa_headline_byname' - LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, text, tsquery) - RETURNS text - AS 'MODULE_PATHNAME', 'tsa_headline_byname' - LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, tsquery, text) - RETURNS text - AS 'ts_headline_opt' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION headline(text, tsquery) - RETURNS text - AS 'ts_headline' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - --- CREATE the OPERATOR class -CREATE OPERATOR CLASS gist_tsvector_ops -FOR TYPE tsvector USING gist -AS - OPERATOR 1 @@ (tsvector, tsquery), - FUNCTION 1 gtsvector_consistent (internal, tsvector, smallint, oid, internal), - FUNCTION 2 gtsvector_union (internal, internal), - FUNCTION 3 gtsvector_compress (internal), - FUNCTION 4 gtsvector_decompress (internal), - FUNCTION 5 gtsvector_penalty (internal, internal, internal), - FUNCTION 6 gtsvector_picksplit (internal, internal), - FUNCTION 7 gtsvector_same (gtsvector, gtsvector, internal), - STORAGE gtsvector; - ---stat info -CREATE TYPE statinfo - as (word text, ndoc int4, nentry int4); - -CREATE FUNCTION stat(text) - RETURNS setof statinfo - as 'ts_stat1' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION stat(text,text) - RETURNS setof statinfo - as 'ts_stat2' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT; - ---reset - just for debuging -CREATE FUNCTION reset_tsearch() - RETURNS void - as 'MODULE_PATHNAME', 'tsa_reset_tsearch' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---get cover (debug for rank_cd) -CREATE FUNCTION get_covers(tsvector,tsquery) - RETURNS text - as 'MODULE_PATHNAME', 'tsa_get_covers' - LANGUAGE C - RETURNS NULL ON NULL INPUT; - ---debug function -create type tsdebug as ( - ts_name text, - tok_type text, - description text, - token text, - dict_name text[], - "tsvector" tsvector -); - -CREATE FUNCTION _get_parser_from_curcfg() -RETURNS text as -$$select prsname::text from pg_catalog.pg_ts_parser p join pg_ts_config c on cfgparser = p.oid where c.oid = show_curcfg();$$ -LANGUAGE SQL RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION ts_debug(text) -RETURNS setof tsdebug as $$ -select - (select c.cfgname::text from pg_catalog.pg_ts_config as c - where c.oid = show_curcfg()), - t.alias as tok_type, - t.descr as description, - p.token, - ARRAY ( SELECT m.mapdict::pg_catalog.regdictionary::pg_catalog.text - FROM pg_catalog.pg_ts_config_map AS m - WHERE m.mapcfg = show_curcfg() AND m.maptokentype = p.tokid - ORDER BY m.mapseqno ) - AS dict_name, - strip(to_tsvector(p.token)) as tsvector -from - parse( _get_parser_from_curcfg(), $1 ) as p, - token_type() as t -where - t.tokid = p.tokid -$$ LANGUAGE SQL RETURNS NULL ON NULL INPUT; - -CREATE FUNCTION numnode(tsquery) - RETURNS int4 - as 'tsquery_numnode' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsquery_and(tsquery,tsquery) - RETURNS tsquery - as 'tsquery_and' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsquery_or(tsquery,tsquery) - RETURNS tsquery - as 'tsquery_or' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsquery_not(tsquery) - RETURNS tsquery - as 'tsquery_not' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - ---------------rewrite subsystem - -CREATE FUNCTION rewrite(tsquery, text) - RETURNS tsquery - as 'tsquery_rewrite_query' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rewrite(tsquery, tsquery, tsquery) - RETURNS tsquery - as 'tsquery_rewrite' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION rewrite_accum(tsquery,tsquery[]) - RETURNS tsquery - AS 'MODULE_PATHNAME', 'tsa_rewrite_accum' - LANGUAGE C; - -CREATE FUNCTION rewrite_finish(tsquery) - RETURNS tsquery - as 'MODULE_PATHNAME', 'tsa_rewrite_finish' - LANGUAGE C; - -CREATE AGGREGATE rewrite ( - BASETYPE = tsquery[], - SFUNC = rewrite_accum, - STYPE = tsquery, - FINALFUNC = rewrite_finish -); - -CREATE FUNCTION tsq_mcontains(tsquery, tsquery) - RETURNS bool - as 'tsq_mcontains' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE FUNCTION tsq_mcontained(tsquery, tsquery) - RETURNS bool - as 'tsq_mcontained' - LANGUAGE INTERNAL - RETURNS NULL ON NULL INPUT IMMUTABLE; - -CREATE OPERATOR CLASS gist_tp_tsquery_ops -FOR TYPE tsquery USING gist -AS - OPERATOR 7 @> (tsquery, tsquery), - OPERATOR 8 <@ (tsquery, tsquery), - FUNCTION 1 gtsquery_consistent (internal, tsquery, smallint, oid, internal), - FUNCTION 2 gtsquery_union (internal, internal), - FUNCTION 3 gtsquery_compress (internal), - FUNCTION 4 gtsquery_decompress (internal), - FUNCTION 5 gtsquery_penalty (internal, internal, internal), - FUNCTION 6 gtsquery_picksplit (internal, internal), - FUNCTION 7 gtsquery_same (bigint, bigint, internal), - STORAGE bigint; - -CREATE OPERATOR CLASS gin_tsvector_ops -FOR TYPE tsvector USING gin -AS - OPERATOR 1 @@ (tsvector, tsquery), - OPERATOR 2 @@@ (tsvector, tsquery), - FUNCTION 1 bttextcmp(text, text), - FUNCTION 2 gin_extract_tsvector(tsvector,internal,internal), - FUNCTION 3 gin_extract_tsquery(tsvector,internal,smallint,internal,internal,internal,internal), - FUNCTION 4 gin_tsquery_consistent(internal,smallint,tsvector,int,internal,internal,internal,internal), - FUNCTION 5 gin_cmp_prefix(text,text,smallint,internal), - STORAGE text; - -CREATE OPERATOR CLASS tsvector_ops -FOR TYPE tsvector USING btree AS - OPERATOR 1 < , - OPERATOR 2 <= , - OPERATOR 3 = , - OPERATOR 4 >= , - OPERATOR 5 > , - FUNCTION 1 tsvector_cmp(tsvector, tsvector); - -CREATE OPERATOR CLASS tsquery_ops -FOR TYPE tsquery USING btree AS - OPERATOR 1 < , - OPERATOR 2 <= , - OPERATOR 3 = , - OPERATOR 4 >= , - OPERATOR 5 > , - FUNCTION 1 tsquery_cmp(tsquery, tsquery); diff --git a/contrib/tsearch2/tsearch2--unpackaged--1.0.sql b/contrib/tsearch2/tsearch2--unpackaged--1.0.sql deleted file mode 100644 index e123297132..0000000000 --- a/contrib/tsearch2/tsearch2--unpackaged--1.0.sql +++ /dev/null @@ -1,144 +0,0 @@ -/* contrib/tsearch2/tsearch2--unpackaged--1.0.sql */ - --- complain if script is sourced in psql, rather than via CREATE EXTENSION -\echo Use "CREATE EXTENSION tsearch2 FROM unpackaged" to load this file. \quit - -ALTER EXTENSION tsearch2 ADD type @extschema@.tsvector; -ALTER EXTENSION tsearch2 ADD type @extschema@.tsquery; -ALTER EXTENSION tsearch2 ADD type @extschema@.gtsvector; -ALTER EXTENSION tsearch2 ADD type gtsq; -ALTER EXTENSION tsearch2 ADD function lexize(oid,text); -ALTER EXTENSION tsearch2 ADD function lexize(text,text); -ALTER EXTENSION tsearch2 ADD function lexize(text); -ALTER EXTENSION tsearch2 ADD function set_curdict(integer); -ALTER EXTENSION tsearch2 ADD function set_curdict(text); -ALTER EXTENSION tsearch2 ADD function dex_init(internal); -ALTER EXTENSION tsearch2 ADD function dex_lexize(internal,internal,integer); -ALTER EXTENSION tsearch2 ADD function snb_en_init(internal); -ALTER EXTENSION tsearch2 ADD function snb_lexize(internal,internal,integer); -ALTER EXTENSION tsearch2 ADD function snb_ru_init_koi8(internal); -ALTER EXTENSION tsearch2 ADD function snb_ru_init_utf8(internal); -ALTER EXTENSION tsearch2 ADD function snb_ru_init(internal); -ALTER EXTENSION tsearch2 ADD function spell_init(internal); -ALTER EXTENSION tsearch2 ADD function spell_lexize(internal,internal,integer); -ALTER EXTENSION tsearch2 ADD function syn_init(internal); -ALTER EXTENSION tsearch2 ADD function syn_lexize(internal,internal,integer); -ALTER EXTENSION tsearch2 ADD function @extschema@.thesaurus_init(internal); -ALTER EXTENSION tsearch2 ADD function thesaurus_lexize(internal,internal,integer,internal); -ALTER EXTENSION tsearch2 ADD type tokentype; -ALTER EXTENSION tsearch2 ADD function token_type(integer); -ALTER EXTENSION tsearch2 ADD function token_type(text); -ALTER EXTENSION tsearch2 ADD function token_type(); -ALTER EXTENSION tsearch2 ADD function set_curprs(integer); -ALTER EXTENSION tsearch2 ADD function set_curprs(text); -ALTER EXTENSION tsearch2 ADD type tokenout; -ALTER EXTENSION tsearch2 ADD function parse(oid,text); -ALTER EXTENSION tsearch2 ADD function parse(text,text); -ALTER EXTENSION tsearch2 ADD function parse(text); -ALTER EXTENSION tsearch2 ADD function @extschema@.prsd_start(internal,integer); -ALTER EXTENSION tsearch2 ADD function prsd_getlexeme(internal,internal,internal); -ALTER EXTENSION tsearch2 ADD function @extschema@.prsd_end(internal); -ALTER EXTENSION tsearch2 ADD function @extschema@.prsd_lextype(internal); -ALTER EXTENSION tsearch2 ADD function prsd_headline(internal,internal,internal); -ALTER EXTENSION tsearch2 ADD function set_curcfg(integer); -ALTER EXTENSION tsearch2 ADD function set_curcfg(text); -ALTER EXTENSION tsearch2 ADD function show_curcfg(); -ALTER EXTENSION tsearch2 ADD function @extschema@.length(tsvector); -ALTER EXTENSION tsearch2 ADD function to_tsvector(oid,text); -ALTER EXTENSION tsearch2 ADD function to_tsvector(text,text); -ALTER EXTENSION tsearch2 ADD function @extschema@.to_tsvector(text); -ALTER EXTENSION tsearch2 ADD function @extschema@.strip(tsvector); -ALTER EXTENSION tsearch2 ADD function @extschema@.setweight(tsvector,"char"); -ALTER EXTENSION tsearch2 ADD function concat(tsvector,tsvector); -ALTER EXTENSION tsearch2 ADD function @extschema@.querytree(tsquery); -ALTER EXTENSION tsearch2 ADD function to_tsquery(oid,text); -ALTER EXTENSION tsearch2 ADD function to_tsquery(text,text); -ALTER EXTENSION tsearch2 ADD function @extschema@.to_tsquery(text); -ALTER EXTENSION tsearch2 ADD function plainto_tsquery(oid,text); -ALTER EXTENSION tsearch2 ADD function plainto_tsquery(text,text); -ALTER EXTENSION tsearch2 ADD function @extschema@.plainto_tsquery(text); -ALTER EXTENSION tsearch2 ADD function tsearch2(); -ALTER EXTENSION tsearch2 ADD function rank(real[],tsvector,tsquery); -ALTER EXTENSION tsearch2 ADD function rank(real[],tsvector,tsquery,integer); -ALTER EXTENSION tsearch2 ADD function rank(tsvector,tsquery); -ALTER EXTENSION tsearch2 ADD function rank(tsvector,tsquery,integer); -ALTER EXTENSION tsearch2 ADD function rank_cd(real[],tsvector,tsquery); -ALTER EXTENSION tsearch2 ADD function rank_cd(real[],tsvector,tsquery,integer); -ALTER EXTENSION tsearch2 ADD function rank_cd(tsvector,tsquery); -ALTER EXTENSION tsearch2 ADD function rank_cd(tsvector,tsquery,integer); -ALTER EXTENSION tsearch2 ADD function headline(oid,text,tsquery,text); -ALTER EXTENSION tsearch2 ADD function headline(oid,text,tsquery); -ALTER EXTENSION tsearch2 ADD function headline(text,text,tsquery,text); -ALTER EXTENSION tsearch2 ADD function headline(text,text,tsquery); -ALTER EXTENSION tsearch2 ADD function headline(text,tsquery,text); -ALTER EXTENSION tsearch2 ADD function headline(text,tsquery); -ALTER EXTENSION tsearch2 ADD operator family gist_tsvector_ops using gist; -ALTER EXTENSION tsearch2 ADD operator class gist_tsvector_ops using gist; -ALTER EXTENSION tsearch2 ADD type statinfo; -ALTER EXTENSION tsearch2 ADD function stat(text); -ALTER EXTENSION tsearch2 ADD function stat(text,text); -ALTER EXTENSION tsearch2 ADD function reset_tsearch(); -ALTER EXTENSION tsearch2 ADD function get_covers(tsvector,tsquery); -ALTER EXTENSION tsearch2 ADD type tsdebug; -ALTER EXTENSION tsearch2 ADD function _get_parser_from_curcfg(); -ALTER EXTENSION tsearch2 ADD function @extschema@.ts_debug(text); -ALTER EXTENSION tsearch2 ADD function @extschema@.numnode(tsquery); -ALTER EXTENSION tsearch2 ADD function @extschema@.tsquery_and(tsquery,tsquery); -ALTER EXTENSION tsearch2 ADD function @extschema@.tsquery_or(tsquery,tsquery); -ALTER EXTENSION tsearch2 ADD function @extschema@.tsquery_not(tsquery); -ALTER EXTENSION tsearch2 ADD function rewrite(tsquery,text); -ALTER EXTENSION tsearch2 ADD function rewrite(tsquery,tsquery,tsquery); -ALTER EXTENSION tsearch2 ADD function rewrite_accum(tsquery,tsquery[]); -ALTER EXTENSION tsearch2 ADD function rewrite_finish(tsquery); -ALTER EXTENSION tsearch2 ADD function rewrite(tsquery[]); -ALTER EXTENSION tsearch2 ADD function @extschema@.tsq_mcontains(tsquery,tsquery); -ALTER EXTENSION tsearch2 ADD function @extschema@.tsq_mcontained(tsquery,tsquery); -ALTER EXTENSION tsearch2 ADD operator family gist_tp_tsquery_ops using gist; -ALTER EXTENSION tsearch2 ADD operator class gist_tp_tsquery_ops using gist; -ALTER EXTENSION tsearch2 ADD operator family gin_tsvector_ops using gin; -ALTER EXTENSION tsearch2 ADD operator class gin_tsvector_ops using gin; -ALTER EXTENSION tsearch2 ADD operator family @extschema@.tsvector_ops using btree; -ALTER EXTENSION tsearch2 ADD operator class @extschema@.tsvector_ops using btree; -ALTER EXTENSION tsearch2 ADD operator family @extschema@.tsquery_ops using btree; -ALTER EXTENSION tsearch2 ADD operator class @extschema@.tsquery_ops using btree; - --- tsearch2 relies on the core functions gin_extract_tsvector, --- gin_extract_tsquery, and gin_tsquery_consistent, which changed signature in --- 9.1. To support upgrading, pg_catalog contains entries for these functions --- with both the old and new signatures, and the former is what would have --- been added to our opclass during initial restore of a 9.0 dump script. --- Avert your eyes while we hack the pg_amproc entries to make them link to --- the new forms ... - -UPDATE pg_catalog.pg_amproc -SET amproc = 'pg_catalog.gin_extract_tsvector(pg_catalog.tsvector,internal,internal)'::pg_catalog.regprocedure -WHERE amprocfamily = - (SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin_tsvector_ops' AND - opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace - WHERE nspname = '@extschema@')) - AND amproclefttype = 'pg_catalog.tsvector'::pg_catalog.regtype - AND amprocrighttype = 'pg_catalog.tsvector'::pg_catalog.regtype - AND amprocnum = 2 - AND amproc = 'pg_catalog.gin_extract_tsvector(pg_catalog.tsvector,internal)'::pg_catalog.regprocedure; - -UPDATE pg_catalog.pg_amproc -SET amproc = 'pg_catalog.gin_extract_tsquery(pg_catalog.tsquery,internal,smallint,internal,internal,internal,internal)'::pg_catalog.regprocedure -WHERE amprocfamily = - (SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin_tsvector_ops' AND - opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace - WHERE nspname = '@extschema@')) - AND amproclefttype = 'pg_catalog.tsvector'::pg_catalog.regtype - AND amprocrighttype = 'pg_catalog.tsvector'::pg_catalog.regtype - AND amprocnum = 3 - AND amproc = 'pg_catalog.gin_extract_tsquery(pg_catalog.tsquery,internal,smallint,internal,internal)'::pg_catalog.regprocedure; - -UPDATE pg_catalog.pg_amproc -SET amproc = 'pg_catalog.gin_tsquery_consistent(internal,smallint,pg_catalog.tsquery,integer,internal,internal,internal,internal)'::pg_catalog.regprocedure -WHERE amprocfamily = - (SELECT oid FROM pg_catalog.pg_opfamily WHERE opfname = 'gin_tsvector_ops' AND - opfnamespace = (SELECT oid FROM pg_catalog.pg_namespace - WHERE nspname = '@extschema@')) - AND amproclefttype = 'pg_catalog.tsvector'::pg_catalog.regtype - AND amprocrighttype = 'pg_catalog.tsvector'::pg_catalog.regtype - AND amprocnum = 4 - AND amproc = 'pg_catalog.gin_tsquery_consistent(internal,smallint,pg_catalog.tsquery,integer,internal,internal)'::pg_catalog.regprocedure; diff --git a/contrib/tsearch2/tsearch2.c b/contrib/tsearch2/tsearch2.c deleted file mode 100644 index 3fc72cb995..0000000000 --- a/contrib/tsearch2/tsearch2.c +++ /dev/null @@ -1,541 +0,0 @@ -/*------------------------------------------------------------------------- - * - * tsearch2.c - * Backwards-compatibility package for old contrib/tsearch2 API - * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group - * - * - * IDENTIFICATION - * contrib/tsearch2/tsearch2.c - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "catalog/namespace.h" -#include "catalog/pg_type.h" -#include "commands/trigger.h" -#include "tsearch/ts_utils.h" -#include "utils/builtins.h" -#include "utils/guc.h" -#include "utils/syscache.h" - -PG_MODULE_MAGIC; - -static Oid current_dictionary_oid = InvalidOid; -static Oid current_parser_oid = InvalidOid; - -/* insert given value at argument position 0 */ -#define INSERT_ARGUMENT0(argument, isnull) \ - do { \ - int i; \ - for (i = fcinfo->nargs; i > 0; i--) \ - { \ - fcinfo->arg[i] = fcinfo->arg[i-1]; \ - fcinfo->argnull[i] = fcinfo->argnull[i-1]; \ - } \ - fcinfo->arg[0] = (argument); \ - fcinfo->argnull[0] = (isnull); \ - fcinfo->nargs++; \ - } while (0) - -#define TextGetObjectId(infunction, text) \ - DatumGetObjectId(DirectFunctionCall1(infunction, \ - CStringGetDatum(text_to_cstring(text)))) - -#define UNSUPPORTED_FUNCTION(name) \ - PG_FUNCTION_INFO_V1(name); \ - Datum \ - name(PG_FUNCTION_ARGS) \ - { \ - ereport(ERROR, \ - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),\ - errmsg("function %s is no longer supported", \ - format_procedure(fcinfo->flinfo->fn_oid)), \ - errhint("Switch to new tsearch functionality."))); \ - /* keep compiler quiet */ \ - PG_RETURN_NULL(); \ - } \ - extern int no_such_variable - -static Oid GetCurrentDict(void); -static Oid GetCurrentParser(void); - -PG_FUNCTION_INFO_V1(tsa_lexize_byname); -PG_FUNCTION_INFO_V1(tsa_lexize_bycurrent); -PG_FUNCTION_INFO_V1(tsa_set_curdict); -PG_FUNCTION_INFO_V1(tsa_set_curdict_byname); -PG_FUNCTION_INFO_V1(tsa_token_type_current); -PG_FUNCTION_INFO_V1(tsa_set_curprs); -PG_FUNCTION_INFO_V1(tsa_set_curprs_byname); -PG_FUNCTION_INFO_V1(tsa_parse_current); -PG_FUNCTION_INFO_V1(tsa_set_curcfg); -PG_FUNCTION_INFO_V1(tsa_set_curcfg_byname); -PG_FUNCTION_INFO_V1(tsa_to_tsvector_name); -PG_FUNCTION_INFO_V1(tsa_to_tsquery_name); -PG_FUNCTION_INFO_V1(tsa_plainto_tsquery_name); -PG_FUNCTION_INFO_V1(tsa_headline_byname); -PG_FUNCTION_INFO_V1(tsa_ts_stat); -PG_FUNCTION_INFO_V1(tsa_tsearch2); -PG_FUNCTION_INFO_V1(tsa_rewrite_accum); -PG_FUNCTION_INFO_V1(tsa_rewrite_finish); - - -/* - * List of unsupported functions - * - * The parser and dictionary functions are defined only so that the former - * contents of pg_ts_parser and pg_ts_dict can be loaded into the system, - * for ease of reference while creating the new tsearch configuration. - */ - -UNSUPPORTED_FUNCTION(tsa_dex_init); -UNSUPPORTED_FUNCTION(tsa_dex_lexize); - -UNSUPPORTED_FUNCTION(tsa_snb_en_init); -UNSUPPORTED_FUNCTION(tsa_snb_lexize); -UNSUPPORTED_FUNCTION(tsa_snb_ru_init_koi8); -UNSUPPORTED_FUNCTION(tsa_snb_ru_init_utf8); -UNSUPPORTED_FUNCTION(tsa_snb_ru_init); - -UNSUPPORTED_FUNCTION(tsa_spell_init); -UNSUPPORTED_FUNCTION(tsa_spell_lexize); - -UNSUPPORTED_FUNCTION(tsa_syn_init); -UNSUPPORTED_FUNCTION(tsa_syn_lexize); - -UNSUPPORTED_FUNCTION(tsa_thesaurus_init); -UNSUPPORTED_FUNCTION(tsa_thesaurus_lexize); - -UNSUPPORTED_FUNCTION(tsa_prsd_start); -UNSUPPORTED_FUNCTION(tsa_prsd_getlexeme); -UNSUPPORTED_FUNCTION(tsa_prsd_end); -UNSUPPORTED_FUNCTION(tsa_prsd_lextype); -UNSUPPORTED_FUNCTION(tsa_prsd_headline); - -UNSUPPORTED_FUNCTION(tsa_reset_tsearch); -UNSUPPORTED_FUNCTION(tsa_get_covers); - - -/* - * list of redefined functions - */ - -/* lexize(text, text) */ -Datum -tsa_lexize_byname(PG_FUNCTION_ARGS) -{ - text *dictname = PG_GETARG_TEXT_PP(0); - Datum arg1 = PG_GETARG_DATUM(1); - - return DirectFunctionCall2(ts_lexize, - ObjectIdGetDatum(TextGetObjectId(regdictionaryin, dictname)), - arg1); -} - -/* lexize(text) */ -Datum -tsa_lexize_bycurrent(PG_FUNCTION_ARGS) -{ - Datum arg0 = PG_GETARG_DATUM(0); - Oid id = GetCurrentDict(); - - return DirectFunctionCall2(ts_lexize, - ObjectIdGetDatum(id), - arg0); -} - -/* set_curdict(int) */ -Datum -tsa_set_curdict(PG_FUNCTION_ARGS) -{ - Oid dict_oid = PG_GETARG_OID(0); - - if (!SearchSysCacheExists(TSDICTOID, - ObjectIdGetDatum(dict_oid), - 0, 0, 0)) - elog(ERROR, "cache lookup failed for text search dictionary %u", - dict_oid); - - current_dictionary_oid = dict_oid; - - PG_RETURN_VOID(); -} - -/* set_curdict(text) */ -Datum -tsa_set_curdict_byname(PG_FUNCTION_ARGS) -{ - text *name = PG_GETARG_TEXT_PP(0); - Oid dict_oid; - - dict_oid = get_ts_dict_oid(stringToQualifiedNameList(text_to_cstring(name)), false); - - current_dictionary_oid = dict_oid; - - PG_RETURN_VOID(); -} - -/* token_type() */ -Datum -tsa_token_type_current(PG_FUNCTION_ARGS) -{ - INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false); - return ts_token_type_byid(fcinfo); -} - -/* set_curprs(int) */ -Datum -tsa_set_curprs(PG_FUNCTION_ARGS) -{ - Oid parser_oid = PG_GETARG_OID(0); - - if (!SearchSysCacheExists(TSPARSEROID, - ObjectIdGetDatum(parser_oid), - 0, 0, 0)) - elog(ERROR, "cache lookup failed for text search parser %u", - parser_oid); - - current_parser_oid = parser_oid; - - PG_RETURN_VOID(); -} - -/* set_curprs(text) */ -Datum -tsa_set_curprs_byname(PG_FUNCTION_ARGS) -{ - text *name = PG_GETARG_TEXT_PP(0); - Oid parser_oid; - - parser_oid = get_ts_parser_oid(stringToQualifiedNameList(text_to_cstring(name)), false); - - current_parser_oid = parser_oid; - - PG_RETURN_VOID(); -} - -/* parse(text) */ -Datum -tsa_parse_current(PG_FUNCTION_ARGS) -{ - INSERT_ARGUMENT0(ObjectIdGetDatum(GetCurrentParser()), false); - return ts_parse_byid(fcinfo); -} - -/* set_curcfg(int) */ -Datum -tsa_set_curcfg(PG_FUNCTION_ARGS) -{ - Oid arg0 = PG_GETARG_OID(0); - char *name; - - name = DatumGetCString(DirectFunctionCall1(regconfigout, - ObjectIdGetDatum(arg0))); - - SetConfigOption("default_text_search_config", name, - PGC_USERSET, PGC_S_SESSION); - - PG_RETURN_VOID(); -} - -/* set_curcfg(text) */ -Datum -tsa_set_curcfg_byname(PG_FUNCTION_ARGS) -{ - text *arg0 = PG_GETARG_TEXT_PP(0); - char *name; - - name = text_to_cstring(arg0); - - SetConfigOption("default_text_search_config", name, - PGC_USERSET, PGC_S_SESSION); - - PG_RETURN_VOID(); -} - -/* to_tsvector(text, text) */ -Datum -tsa_to_tsvector_name(PG_FUNCTION_ARGS) -{ - text *cfgname = PG_GETARG_TEXT_PP(0); - Datum arg1 = PG_GETARG_DATUM(1); - Oid config_oid; - - config_oid = TextGetObjectId(regconfigin, cfgname); - - return DirectFunctionCall2(to_tsvector_byid, - ObjectIdGetDatum(config_oid), arg1); -} - -/* to_tsquery(text, text) */ -Datum -tsa_to_tsquery_name(PG_FUNCTION_ARGS) -{ - text *cfgname = PG_GETARG_TEXT_PP(0); - Datum arg1 = PG_GETARG_DATUM(1); - Oid config_oid; - - config_oid = TextGetObjectId(regconfigin, cfgname); - - return DirectFunctionCall2(to_tsquery_byid, - ObjectIdGetDatum(config_oid), arg1); -} - - -/* plainto_tsquery(text, text) */ -Datum -tsa_plainto_tsquery_name(PG_FUNCTION_ARGS) -{ - text *cfgname = PG_GETARG_TEXT_PP(0); - Datum arg1 = PG_GETARG_DATUM(1); - Oid config_oid; - - config_oid = TextGetObjectId(regconfigin, cfgname); - - return DirectFunctionCall2(plainto_tsquery_byid, - ObjectIdGetDatum(config_oid), arg1); -} - -/* headline(text, text, tsquery [,text]) */ -Datum -tsa_headline_byname(PG_FUNCTION_ARGS) -{ - Datum arg0 = PG_GETARG_DATUM(0); - Datum arg1 = PG_GETARG_DATUM(1); - Datum arg2 = PG_GETARG_DATUM(2); - Datum result; - Oid config_oid; - - /* first parameter has to be converted to oid */ - config_oid = DatumGetObjectId(DirectFunctionCall1(regconfigin, - CStringGetDatum(TextDatumGetCString(arg0)))); - - if (PG_NARGS() == 3) - result = DirectFunctionCall3(ts_headline_byid, - ObjectIdGetDatum(config_oid), arg1, arg2); - else - { - Datum arg3 = PG_GETARG_DATUM(3); - - result = DirectFunctionCall4(ts_headline_byid_opt, - ObjectIdGetDatum(config_oid), - arg1, arg2, arg3); - } - - return result; -} - -/* - * tsearch2 version of update trigger - * - * We pass this on to the core trigger after inserting the default text - * search configuration name as the second argument. Note that this isn't - * a complete implementation of the original functionality; tsearch2 allowed - * transformation function names to be included in the list. However, that - * is deliberately removed as being a security risk. - */ -Datum -tsa_tsearch2(PG_FUNCTION_ARGS) -{ - TriggerData *trigdata; - Trigger *trigger; - char **tgargs, - **tgargs_old; - int i; - Datum res; - - /* Check call context */ - if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */ - elog(ERROR, "tsvector_update_trigger: not fired by trigger manager"); - - trigdata = (TriggerData *) fcinfo->context; - trigger = trigdata->tg_trigger; - - if (trigger->tgnargs < 2) - elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)"); - - /* create space for configuration name */ - tgargs = (char **) palloc((trigger->tgnargs + 1) * sizeof(char *)); - tgargs[0] = trigger->tgargs[0]; - for (i = 1; i < trigger->tgnargs; i++) - tgargs[i + 1] = trigger->tgargs[i]; - - tgargs[1] = pstrdup(GetConfigOptionByName("default_text_search_config", - NULL, false)); - tgargs_old = trigger->tgargs; - trigger->tgargs = tgargs; - trigger->tgnargs++; - - res = tsvector_update_trigger_byid(fcinfo); - - /* restore old trigger data */ - trigger->tgargs = tgargs_old; - trigger->tgnargs--; - - pfree(tgargs[1]); - pfree(tgargs); - - return res; -} - - -Datum -tsa_rewrite_accum(PG_FUNCTION_ARGS) -{ - TSQuery acc; - ArrayType *qa; - TSQuery q; - QTNode *qex = NULL, - *subs = NULL, - *acctree = NULL; - bool isfind = false; - Datum *elemsp; - int nelemsp; - MemoryContext aggcontext; - MemoryContext oldcontext; - - if (!AggCheckCallContext(fcinfo, &aggcontext)) - elog(ERROR, "tsa_rewrite_accum called in non-aggregate context"); - - if (PG_ARGISNULL(0) || PG_GETARG_POINTER(0) == NULL) - { - acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); - SET_VARSIZE(acc, HDRSIZETQ); - acc->size = 0; - } - else - acc = PG_GETARG_TSQUERY(0); - - if (PG_ARGISNULL(1) || PG_GETARG_POINTER(1) == NULL) - PG_RETURN_TSQUERY(acc); - else - qa = PG_GETARG_ARRAYTYPE_P_COPY(1); - - if (ARR_NDIM(qa) != 1) - elog(ERROR, "array must be one-dimensional, not %d dimensions", - ARR_NDIM(qa)); - if (ArrayGetNItems(ARR_NDIM(qa), ARR_DIMS(qa)) != 3) - elog(ERROR, "array must have three elements"); - if (ARR_ELEMTYPE(qa) != TSQUERYOID) - elog(ERROR, "array must contain tsquery elements"); - - deconstruct_array(qa, TSQUERYOID, -1, false, 'i', &elemsp, NULL, &nelemsp); - - q = DatumGetTSQuery(elemsp[0]); - if (q->size == 0) - { - pfree(elemsp); - PG_RETURN_POINTER(acc); - } - - if (!acc->size) - { - if (VARSIZE(acc) > HDRSIZETQ) - { - pfree(elemsp); - PG_RETURN_POINTER(acc); - } - else - acctree = QT2QTN(GETQUERY(q), GETOPERAND(q)); - } - else - acctree = QT2QTN(GETQUERY(acc), GETOPERAND(acc)); - - QTNTernary(acctree); - QTNSort(acctree); - - q = DatumGetTSQuery(elemsp[1]); - if (q->size == 0) - { - pfree(elemsp); - PG_RETURN_POINTER(acc); - } - qex = QT2QTN(GETQUERY(q), GETOPERAND(q)); - QTNTernary(qex); - QTNSort(qex); - - q = DatumGetTSQuery(elemsp[2]); - if (q->size) - subs = QT2QTN(GETQUERY(q), GETOPERAND(q)); - - acctree = findsubquery(acctree, qex, subs, &isfind); - - if (isfind || !acc->size) - { - /* pfree( acc ); do not pfree(p), because nodeAgg.c will */ - if (acctree) - { - QTNBinary(acctree); - oldcontext = MemoryContextSwitchTo(aggcontext); - acc = QTN2QT(acctree); - MemoryContextSwitchTo(oldcontext); - } - else - { - acc = (TSQuery) MemoryContextAlloc(aggcontext, HDRSIZETQ); - SET_VARSIZE(acc, HDRSIZETQ); - acc->size = 0; - } - } - - pfree(elemsp); - QTNFree(qex); - QTNFree(subs); - QTNFree(acctree); - - PG_RETURN_TSQUERY(acc); -} - -Datum -tsa_rewrite_finish(PG_FUNCTION_ARGS) -{ - TSQuery acc = PG_GETARG_TSQUERY(0); - TSQuery rewrited; - - if (acc == NULL || PG_ARGISNULL(0) || acc->size == 0) - { - rewrited = (TSQuery) palloc(HDRSIZETQ); - SET_VARSIZE(rewrited, HDRSIZETQ); - rewrited->size = 0; - } - else - { - rewrited = (TSQuery) palloc(VARSIZE(acc)); - memcpy(rewrited, acc, VARSIZE(acc)); - pfree(acc); - } - - PG_RETURN_POINTER(rewrited); -} - - -/* - * Get Oid of current dictionary - */ -static Oid -GetCurrentDict(void) -{ - if (current_dictionary_oid == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("no current dictionary"), - errhint("Execute SELECT set_curdict(...)."))); - - return current_dictionary_oid; -} - -/* - * Get Oid of current parser - * - * Here, it seems reasonable to select the "default" parser if none has been - * set. - */ -static Oid -GetCurrentParser(void) -{ - if (current_parser_oid == InvalidOid) - current_parser_oid = get_ts_parser_oid(stringToQualifiedNameList("pg_catalog.default"), false); - return current_parser_oid; -} diff --git a/contrib/tsearch2/tsearch2.control b/contrib/tsearch2/tsearch2.control deleted file mode 100644 index 3e11bcfbe8..0000000000 --- a/contrib/tsearch2/tsearch2.control +++ /dev/null @@ -1,7 +0,0 @@ -# tsearch2 extension -comment = 'compatibility package for pre-8.3 text search functions' -default_version = '1.0' -module_pathname = '$libdir/tsearch2' -# this is not relocatable because the tsearch2--unpackaged--1.0.sql script -# has to use @extschema@ to avoid conflict with items in pg_catalog -relocatable = false diff --git a/contrib/tsm_system_rows/tsm_system_rows.c b/contrib/tsm_system_rows/tsm_system_rows.c index c5251ed652..544458ec91 100644 --- a/contrib/tsm_system_rows/tsm_system_rows.c +++ b/contrib/tsm_system_rows/tsm_system_rows.c @@ -17,7 +17,7 @@ * won't visit blocks added after the first scan, but that is fine since * such blocks shouldn't contain any visible tuples anyway. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION diff --git a/contrib/tsm_system_time/tsm_system_time.c b/contrib/tsm_system_time/tsm_system_time.c index cd681c042a..af8d025414 100644 --- a/contrib/tsm_system_time/tsm_system_time.c +++ b/contrib/tsm_system_time/tsm_system_time.c @@ -13,7 +13,7 @@ * However, we do what we can to reduce surprising behavior by selecting * the sampling pattern just once per query, much as in tsm_system_rows. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION diff --git a/contrib/unaccent/unaccent.c b/contrib/unaccent/unaccent.c index e243bc2607..6a34cfd3ed 100644 --- a/contrib/unaccent/unaccent.c +++ b/contrib/unaccent/unaccent.c @@ -3,7 +3,7 @@ * unaccent.c * Text search unaccent dictionary * - * Copyright (c) 2009-2016, PostgreSQL Global Development Group + * Copyright (c) 2009-2017, PostgreSQL Global Development Group * * IDENTIFICATION * contrib/unaccent/unaccent.c @@ -20,6 +20,7 @@ #include "tsearch/ts_locale.h" #include "tsearch/ts_public.h" #include "utils/builtins.h" +#include "utils/regproc.h" PG_MODULE_MAGIC; @@ -383,14 +384,14 @@ unaccent_dict(PG_FUNCTION_ARGS) dictOid = PG_GETARG_OID(0); strArg = 1; } - str = PG_GETARG_TEXT_P(strArg); + str = PG_GETARG_TEXT_PP(strArg); dict = lookup_ts_dictionary_cache(dictOid); res = (TSLexeme *) DatumGetPointer(FunctionCall4(&(dict->lexize), PointerGetDatum(dict->dictData), - PointerGetDatum(VARDATA(str)), - Int32GetDatum(VARSIZE(str) - VARHDRSZ), + PointerGetDatum(VARDATA_ANY(str)), + Int32GetDatum(VARSIZE_ANY_EXHDR(str)), PointerGetDatum(NULL))); PG_FREE_IF_COPY(str, strArg); diff --git a/contrib/uuid-ossp/uuid-ossp.c b/contrib/uuid-ossp/uuid-ossp.c index 3e12bc4e96..1ce08555cf 100644 --- a/contrib/uuid-ossp/uuid-ossp.c +++ b/contrib/uuid-ossp/uuid-ossp.c @@ -2,7 +2,7 @@ * * UUID generation functions using the BSD, E2FS or OSSP UUID library * - * Copyright (c) 2007-2016, PostgreSQL Global Development Group + * Copyright (c) 2007-2017, PostgreSQL Global Development Group * * Portions Copyright (c) 2009 Andrew Gierth * @@ -17,6 +17,10 @@ #include "utils/builtins.h" #include "utils/uuid.h" +/* for ntohl/htonl */ +#include <netinet/in.h> +#include <arpa/inet.h> + /* * It's possible that there's more than one uuid.h header file present. * We expect configure to set the HAVE_ symbol for only the one we want. @@ -26,14 +30,14 @@ */ #define uuid_hash bsd_uuid_hash -#ifdef HAVE_UUID_H +#if defined(HAVE_UUID_H) #include <uuid.h> -#endif -#ifdef HAVE_OSSP_UUID_H +#elif defined(HAVE_OSSP_UUID_H) #include <ossp/uuid.h> -#endif -#ifdef HAVE_UUID_UUID_H +#elif defined(HAVE_UUID_UUID_H) #include <uuid/uuid.h> +#else +#error "please use configure's --with-uuid switch to select a UUID library" #endif #undef uuid_hash @@ -499,13 +503,13 @@ Datum uuid_generate_v3(PG_FUNCTION_ARGS) { pg_uuid_t *ns = PG_GETARG_UUID_P(0); - text *name = PG_GETARG_TEXT_P(1); + text *name = PG_GETARG_TEXT_PP(1); #ifdef HAVE_UUID_OSSP return uuid_generate_v35_internal(UUID_MAKE_V3, ns, name); #else return uuid_generate_internal(UUID_MAKE_V3, (unsigned char *) ns, - VARDATA(name), VARSIZE(name) - VARHDRSZ); + VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name)); #endif } @@ -521,12 +525,12 @@ Datum uuid_generate_v5(PG_FUNCTION_ARGS) { pg_uuid_t *ns = PG_GETARG_UUID_P(0); - text *name = PG_GETARG_TEXT_P(1); + text *name = PG_GETARG_TEXT_PP(1); #ifdef HAVE_UUID_OSSP return uuid_generate_v35_internal(UUID_MAKE_V5, ns, name); #else return uuid_generate_internal(UUID_MAKE_V5, (unsigned char *) ns, - VARDATA(name), VARSIZE(name) - VARHDRSZ); + VARDATA_ANY(name), VARSIZE_ANY_EXHDR(name)); #endif } diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c index 769c805a84..887483cf0f 100644 --- a/contrib/vacuumlo/vacuumlo.c +++ b/contrib/vacuumlo/vacuumlo.c @@ -3,7 +3,7 @@ * vacuumlo.c * This removes orphaned large objects from a database. * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -21,11 +21,11 @@ #include <termios.h> #endif +#include "catalog/pg_class.h" + #include "libpq-fe.h" #include "pg_getopt.h" -#define atooid(x) ((Oid) strtoul((x), NULL, 10)) - #define BUFSIZE 1024 enum trivalue @@ -65,13 +65,17 @@ vacuumlo(const char *database, const struct _param * param) long matched; long deleted; int i; - static char *password = NULL; bool new_pass; bool success = true; + static bool have_password = false; + static char password[100]; /* Note: password can be carried over from a previous call */ - if (param->pg_prompt == TRI_YES && password == NULL) - password = simple_prompt("Password: ", 100, false); + if (param->pg_prompt == TRI_YES && !have_password) + { + simple_prompt("Password: ", password, sizeof(password), false); + have_password = true; + } /* * Start the connection. Loop until we have a password if requested by @@ -91,7 +95,7 @@ vacuumlo(const char *database, const struct _param * param) keywords[2] = "user"; values[2] = param->pg_user; keywords[3] = "password"; - values[3] = password; + values[3] = have_password ? password : NULL; keywords[4] = "dbname"; values[4] = database; keywords[5] = "fallback_application_name"; @@ -110,11 +114,12 @@ vacuumlo(const char *database, const struct _param * param) if (PQstatus(conn) == CONNECTION_BAD && PQconnectionNeedsPassword(conn) && - password == NULL && + !have_password && param->pg_prompt != TRI_NO) { PQfinish(conn); - password = simple_prompt("Password: ", 100, false); + simple_prompt("Password: ", password, sizeof(password), false); + have_password = true; new_pass = true; } } while (new_pass); @@ -206,7 +211,7 @@ vacuumlo(const char *database, const struct _param * param) strcat(buf, " AND a.atttypid = t.oid "); strcat(buf, " AND c.relnamespace = s.oid "); strcat(buf, " AND t.typname in ('oid', 'lo') "); - strcat(buf, " AND c.relkind in ('r', 'm')"); + strcat(buf, " AND c.relkind in (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) ")"); strcat(buf, " AND s.nspname !~ '^pg_'"); res = PQexec(conn, buf); if (PQresultStatus(res) != PGRES_TUPLES_OK) @@ -237,7 +242,7 @@ vacuumlo(const char *database, const struct _param * param) if (!schema || !table || !field) { - fprintf(stderr, "Out of memory\n"); + fprintf(stderr, "%s", PQerrorMessage(conn)); PQclear(res); PQfinish(conn); if (schema != NULL) @@ -514,7 +519,7 @@ main(int argc, char **argv) } break; case 'U': - param.pg_user = strdup(optarg); + param.pg_user = pg_strdup(optarg); break; case 'w': param.pg_prompt = TRI_NO; @@ -529,10 +534,10 @@ main(int argc, char **argv) fprintf(stderr, "%s: invalid port number: %s\n", progname, optarg); exit(1); } - param.pg_port = strdup(optarg); + param.pg_port = pg_strdup(optarg); break; case 'h': - param.pg_host = strdup(optarg); + param.pg_host = pg_strdup(optarg); break; } } diff --git a/contrib/xml2/xpath.c b/contrib/xml2/xpath.c index ac28996867..034545caa8 100644 --- a/contrib/xml2/xpath.c +++ b/contrib/xml2/xpath.c @@ -95,9 +95,9 @@ PG_FUNCTION_INFO_V1(xml_is_well_formed); Datum xml_is_well_formed(PG_FUNCTION_ARGS) { - text *t = PG_GETARG_TEXT_P(0); /* document buffer */ + text *t = PG_GETARG_TEXT_PP(0); /* document buffer */ bool result = false; - int32 docsize = VARSIZE(t) - VARHDRSZ; + int32 docsize = VARSIZE_ANY_EXHDR(t); xmlDocPtr doctree; PgXmlErrorContext *xmlerrcxt; @@ -105,7 +105,7 @@ xml_is_well_formed(PG_FUNCTION_ARGS) PG_TRY(); { - doctree = xmlParseMemory((char *) VARDATA(t), docsize); + doctree = xmlParseMemory((char *) VARDATA_ANY(t), docsize); result = (doctree != NULL); @@ -133,7 +133,7 @@ PG_FUNCTION_INFO_V1(xml_encode_special_chars); Datum xml_encode_special_chars(PG_FUNCTION_ARGS) { - text *tin = PG_GETARG_TEXT_P(0); + text *tin = PG_GETARG_TEXT_PP(0); text *tout; xmlChar *ts, *tt; @@ -248,10 +248,10 @@ PG_FUNCTION_INFO_V1(xpath_nodeset); Datum xpath_nodeset(PG_FUNCTION_ARGS) { - text *document = PG_GETARG_TEXT_P(0); - text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ - xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); - xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(3)); + text *document = PG_GETARG_TEXT_PP(0); + text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ + xmlChar *toptag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2)); + xmlChar *septag = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(3)); xmlChar *xpath; text *xpres; xmlXPathObjectPtr res; @@ -281,9 +281,9 @@ PG_FUNCTION_INFO_V1(xpath_list); Datum xpath_list(PG_FUNCTION_ARGS) { - text *document = PG_GETARG_TEXT_P(0); - text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ - xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_P(2)); + text *document = PG_GETARG_TEXT_PP(0); + text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ + xmlChar *plainsep = pgxml_texttoxmlchar(PG_GETARG_TEXT_PP(2)); xmlChar *xpath; text *xpres; xmlXPathObjectPtr res; @@ -310,15 +310,15 @@ PG_FUNCTION_INFO_V1(xpath_string); Datum xpath_string(PG_FUNCTION_ARGS) { - text *document = PG_GETARG_TEXT_P(0); - text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + text *document = PG_GETARG_TEXT_PP(0); + text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *xpath; int32 pathsize; text *xpres; xmlXPathObjectPtr res; xpath_workspace workspace; - pathsize = VARSIZE(xpathsupp) - VARHDRSZ; + pathsize = VARSIZE_ANY_EXHDR(xpathsupp); /* * We encapsulate the supplied path with "string()" = 8 chars + 1 for NUL @@ -328,7 +328,7 @@ xpath_string(PG_FUNCTION_ARGS) xpath = (xmlChar *) palloc(pathsize + 9); memcpy((char *) xpath, "string(", 7); - memcpy((char *) (xpath + 7), VARDATA(xpathsupp), pathsize); + memcpy((char *) (xpath + 7), VARDATA_ANY(xpathsupp), pathsize); xpath[pathsize + 7] = ')'; xpath[pathsize + 8] = '\0'; @@ -351,8 +351,8 @@ PG_FUNCTION_INFO_V1(xpath_number); Datum xpath_number(PG_FUNCTION_ARGS) { - text *document = PG_GETARG_TEXT_P(0); - text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + text *document = PG_GETARG_TEXT_PP(0); + text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *xpath; float4 fRes; xmlXPathObjectPtr res; @@ -383,8 +383,8 @@ PG_FUNCTION_INFO_V1(xpath_bool); Datum xpath_bool(PG_FUNCTION_ARGS) { - text *document = PG_GETARG_TEXT_P(0); - text *xpathsupp = PG_GETARG_TEXT_P(1); /* XPath expression */ + text *document = PG_GETARG_TEXT_PP(0); + text *xpathsupp = PG_GETARG_TEXT_PP(1); /* XPath expression */ xmlChar *xpath; int bRes; xmlXPathObjectPtr res; @@ -413,7 +413,7 @@ xpath_bool(PG_FUNCTION_ARGS) static xmlXPathObjectPtr pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace) { - int32 docsize = VARSIZE(document) - VARHDRSZ; + int32 docsize = VARSIZE_ANY_EXHDR(document); PgXmlErrorContext *xmlerrcxt; xmlXPathCompExprPtr comppath; @@ -425,7 +425,7 @@ pgxml_xpath(text *document, xmlChar *xpath, xpath_workspace *workspace) PG_TRY(); { - workspace->doctree = xmlParseMemory((char *) VARDATA(document), + workspace->doctree = xmlParseMemory((char *) VARDATA_ANY(document), docsize); if (workspace->doctree != NULL) { @@ -610,7 +610,7 @@ xpath_table(PG_FUNCTION_ARGS) /* * At the moment we assume that the returned attributes make sense for the - * XPath specififed (i.e. we trust the caller). It's not fatal if they get + * XPath specified (i.e. we trust the caller). It's not fatal if they get * it wrong - the input function for the column type will raise an error * if the path result can't be converted into the correct binary * representation. diff --git a/contrib/xml2/xslt_proc.c b/contrib/xml2/xslt_proc.c index 343924e991..391e6b593b 100644 --- a/contrib/xml2/xslt_proc.c +++ b/contrib/xml2/xslt_proc.c @@ -49,8 +49,8 @@ xslt_process(PG_FUNCTION_ARGS) { #ifdef USE_LIBXSLT - text *doct = PG_GETARG_TEXT_P(0); - text *ssheet = PG_GETARG_TEXT_P(1); + text *doct = PG_GETARG_TEXT_PP(0); + text *ssheet = PG_GETARG_TEXT_PP(1); text *result; text *paramstr; const char **params; @@ -66,7 +66,7 @@ xslt_process(PG_FUNCTION_ARGS) if (fcinfo->nargs == 3) { - paramstr = PG_GETARG_TEXT_P(2); + paramstr = PG_GETARG_TEXT_PP(2); params = parse_params(paramstr); } else @@ -85,16 +85,16 @@ xslt_process(PG_FUNCTION_ARGS) bool xslt_sec_prefs_error; /* Parse document */ - doctree = xmlParseMemory((char *) VARDATA(doct), - VARSIZE(doct) - VARHDRSZ); + doctree = xmlParseMemory((char *) VARDATA_ANY(doct), + VARSIZE_ANY_EXHDR(doct)); if (doctree == NULL) xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, "error parsing XML document"); /* Same for stylesheet */ - ssdoc = xmlParseMemory((char *) VARDATA(ssheet), - VARSIZE(ssheet) - VARHDRSZ); + ssdoc = xmlParseMemory((char *) VARDATA_ANY(ssheet), + VARSIZE_ANY_EXHDR(ssheet)); if (ssdoc == NULL) xml_ereport(xmlerrcxt, ERROR, ERRCODE_EXTERNAL_ROUTINE_EXCEPTION, |