diff options
author | Michael Paquier | 2012-10-12 00:45:27 +0000 |
---|---|---|
committer | Michael Paquier | 2012-10-12 00:45:27 +0000 |
commit | c41d90b8af6eb100784e3fdaf89375683c092849 (patch) | |
tree | 6fd36df103263e3ab42ed12e094e72f95919d828 | |
parent | 6c6ba3c4583bd55edf5d3498d57635dec36419e0 (diff) |
Refactor code controlling constraint creation evaluation
The code refactored in this commit is present in XC since the beginning of
the project and was outdated. The former code was put inside the utility
deparser to control UNIQUE, PRIMARY KEY, EXCLUDE and FOREIGN KEY creation.
This was a fundamental mistake as this did not allow control of FOREIGN KEY
creation with generic functions that could also be used to evaluate the
shippability of constraints when issuing a DML on a given relation or when
modifying a relation distribution with ALTER TABLE (online data redistribution
feature).
Now all this control is put directly inside Postgres-XC shippability evaluation
code and can be used in a wider way than the previous implementation. All the
error messages are now made generic and refer to the impossibility to create
constraints whose evaluation cannot be enforced to remote nodes.
Postgres-XC does not support yet global constraints, but once it does, it will
be necessary to remove the error messages referring to constraint evaluation
and to use the APIs implemented in this commit not to control the creation of
constraints but to check the shippability of a Query for a given relation
constraint.
The old code was also deeply lacking evaluation of index expressions, so some
control is added about that. A regression test called xc_constraints check the
shippability of constraints created on tables depending on their distribution.
Documentation is also updated, but might need more completion.
The code in this commit checks if redistribution with ALTER TABLE can be done
according to the given constraints already on it for UNIQUE, EXCLUDE and PRIMARY
KEY. For example, you should not be able to change a replicated table with a
primary key to roundrobin you cannot ensure the uniqueness of a key on such
tables so now this is protected properly. However, such checks are still missing
for FOREIGN KEY and REFERENCES.
29 files changed, 626 insertions, 274 deletions
diff --git a/doc-xc/src/sgml/ddl.sgmlin b/doc-xc/src/sgml/ddl.sgmlin index 800d75f939..ba725a69f6 100644 --- a/doc-xc/src/sgml/ddl.sgmlin +++ b/doc-xc/src/sgml/ddl.sgmlin @@ -161,14 +161,14 @@ CREATE TABLE products ( <para> When you distribute a table, you can choose almost any column of a fundamental data type as distribution - column. + column. For details, please refer to <xref linkend="SQL-CREATETABLE">. Datanode for specific row is determined based upon the value of the distribution - column. + column. By default, distribution column is the first column you specified in <type>CREATE TABLE</type> statement and the column value is used to generate hash value as an index for Datanode which accommodate the - row. + row. You can choose another distribution method such as <type>MODULO</> and <type>ROUNDROBIN</>. To specify what column to choose as the distribution column and what value test to @@ -666,7 +666,9 @@ CREATE TABLE products ( by <type>ROUNDROBIN</type>, you cannot enforce <type>UNIQUE</> constraint because it does not have a distribution column. There's no restriction in <type>UNIQUE</> constraint in replicated -tables. + tables. When an expression is used on a <type>UNIQUE</> constraint, + this expression must contain the distribution column of its parent + table. It cannot use other columns as well. </para> <!## end> </sect2> @@ -749,7 +751,10 @@ CREATE TABLE example ( <para> As mentioned in <type>UNIQUE</> constraint, distribution column must be included in <type>PRIMARY KEY</type>. Other restrictions - apply to <type>PRIMARY KEY</> too. + apply to <type>PRIMARY KEY</> too. When an expression is used on + a <type>PRIMARY KEY</> constraint, this expression must contain + the distribution column of its parent table. It cannot use other + columns as well. </para> <!## end> </sect2> diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index c560771081..0628e20422 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -38,6 +38,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #ifdef PGXC +#include "optimizer/pgxcship.h" #include "parser/parse_utilcmd.h" #include "pgxc/pgxc.h" #endif @@ -544,30 +545,6 @@ DefineIndex(RangeVar *heapRelation, (void) index_reloptions(amoptions, reloptions, true); -#ifdef PGXC - /* Make sure we can locally enforce the index */ - if (IS_PGXC_COORDINATOR && (primary || unique)) - { - ListCell *elem; - bool isSafe = false; - - foreach(elem, attributeList) - { - IndexElem *key = (IndexElem *) lfirst(elem); - - if (CheckLocalIndexColumn(rel->rd_locator_info->locatorType, - rel->rd_locator_info->partAttrName, key->name)) - { - isSafe = true; - break; - } - } - if (!isSafe) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("Unique index of partitioned table must contain the hash/modulo distribution column."))); - } -#endif /* * Prepare arguments for index_create, primarily an IndexInfo structure. * Note that ii_Predicate must be in implicit-AND format. @@ -598,6 +575,37 @@ DefineIndex(RangeVar *heapRelation, accessMethodName, accessMethodId, amcanorder, isconstraint); +#ifdef PGXC + /* Check if index is safely shippable */ + if (IS_PGXC_COORDINATOR) + { + List *indexAttrs = NIL; + + /* Prepare call for shippability evaluation */ + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + /* + * Expression attributes are set at 0, and do not make sense + * when comparing them to distribution columns, so bypass. + */ + if (indexInfo->ii_KeyAttrNumbers[i] > 0) + indexAttrs = lappend_int(indexAttrs, indexInfo->ii_KeyAttrNumbers[i]); + } + + /* Finalize check */ + if (!pgxc_check_index_shippability(GetRelationLocInfo(relationId), + primary, + unique, + exclusionOpNames != NULL, + indexAttrs, + indexInfo->ii_Expressions)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Cannot create index whose evaluation cannot be " + "enforced to remote nodes"))); +} +#endif + /* * Extra checks when creating a PRIMARY KEY index. */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index e00f8d0684..d8659ea694 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -92,6 +92,7 @@ #include "catalog/pgxc_class.h" #include "catalog/pgxc_node.h" #include "commands/sequence.h" +#include "optimizer/pgxcship.h" #include "pgxc/execRemote.h" #include "pgxc/redistrib.h" #endif @@ -6362,6 +6363,29 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, ffeqoperators[i] = ffeqop; } +#ifdef PGXC + /* Check the shippability of this foreign key */ + if (IS_PGXC_COORDINATOR) + { + List *childRefs = NIL, *parentRefs = NIL; + + /* Prepare call for shippability check */ + for (i = 0; i < numfks; i++) + childRefs = lappend_int(childRefs, fkattnum[i]); + for (i = 0; i < numpks; i++) + parentRefs = lappend_int(parentRefs, pkattnum[i]); + + /* Now check shippability for this foreign key */ + if (!pgxc_check_fk_shippability(GetRelationLocInfo(RelationGetRelid(pkrel)), + GetRelationLocInfo(RelationGetRelid(rel)), + parentRefs, + childRefs)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Cannot create foreign key whose evaluation cannot be enforced to remote nodes"))); + } +#endif + /* * Record the FK constraint in pg_constraint. */ @@ -10352,6 +10376,42 @@ BuildRedistribCommands(Oid relid, List *subCmds) /* Build the command tree for table redistribution */ PGXCRedistribCreateCommandList(redistribState, newLocInfo); + /* + * Using the new locator info already available, check if constraints on + * relation are compatible with the new distribution. + */ + foreach(item, RelationGetIndexList(rel)) + { + Oid indid = lfirst_oid(item); + Relation indexRel = index_open(indid, AccessShareLock); + List *indexColNums = NIL; + int2vector colIds = indexRel->rd_index->indkey; + + /* + * Prepare call to shippability check. Attributes set to 0 correspond + * to index expressions and are evaluated internally, so they are not + * appended in given list. + */ + for (i = 0; i < colIds.dim1; i++) + { + if (colIds.values[i] > 0) + indexColNums = lappend_int(indexColNums, colIds.values[i]); + } + + if (!pgxc_check_index_shippability(newLocInfo, + indexRel->rd_index->indisprimary, + indexRel->rd_index->indisunique, + indexRel->rd_index->indisexclusion, + indexColNums, + indexRel->rd_indexprs)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Cannot alter table to distribution incompatible " + "with existing constraints"))); + + index_close(indexRel, AccessShareLock); + } + /* Clean up */ FreeRelationLocInfo(newLocInfo); pfree(new_oid_array); diff --git a/src/backend/optimizer/util/pgxcship.c b/src/backend/optimizer/util/pgxcship.c index 44937bafaf..3ca5ec1c55 100644 --- a/src/backend/optimizer/util/pgxcship.c +++ b/src/backend/optimizer/util/pgxcship.c @@ -32,6 +32,7 @@ #include "parser/parse_type.h" #include "pgxc/pgxcnode.h" #include "utils/lsyscache.h" +#include "utils/rel.h" /* @@ -895,6 +896,13 @@ pgxc_shippability_walker(Node *node, Shippability_context *sc_context) query->commandType)) pgxc_set_shippability_reason(sc_context, SS_UNSHIPPABLE_TRIGGER); + + /* + * PGXCTODO: For the time being Postgres-XC does not support + * global constraints, but once it does it will be necessary + * to add here evaluation of the shippability of indexes and + * constraints of the relation used for INSERT/UPDATE/DELETE. + */ } /* @@ -1509,3 +1517,301 @@ pgxc_merge_exec_nodes(ExecNodes *en1, ExecNodes *en2, bool merge_dist_equijoin, /* Keep compiler happy */ return NULL; } + + +/* + * pgxc_check_index_shippability + * Check shippability of index described by given conditions. This generic + * function can be called even if the index is not yet defined. + */ +bool +pgxc_check_index_shippability(RelationLocInfo *relLocInfo, + bool is_primary, + bool is_unique, + bool is_exclusion, + List *indexAttrs, + List *indexExprs) +{ + bool result = true; + ListCell *lc; + + /* + * Leave if no locator information, in this case shippability has no + * meaning. + */ + if (!relLocInfo) + return result; + + /* + * Scan the expressions used in index and check the shippability of each + * of them. If only one is not-shippable, the index is considered as non + * shippable. It is important to check the shippability of the expressions + * before refining scan on the index columns and distribution type of + * parent relation. + */ + foreach(lc, indexExprs) + { + if (!pgxc_is_expr_shippable((Expr *) lfirst(lc), NULL)) + { + /* One of the expressions is not shippable, so leave */ + result = false; + goto finish; + } + } + + /* + * Check the case of EXCLUSION index. + * EXCLUSION constraints are shippable only for replicated relations as + * such constraints need that one tuple is checked on all the others, and + * if this tuple is correctly excluded of the others, the constraint is + * verified. + */ + if (is_exclusion) + { + if (!IsLocatorReplicated(relLocInfo->locatorType)) + { + result = false; + goto finish; + } + } + + /* + * Check the case of PRIMARY KEY INDEX and UNIQUE index. + * Those constraints are shippable if the parent relation is replicated + * or if the column + */ + if (is_unique || + is_primary) + { + /* + * Perform different checks depending on distribution type of parent + * relation. + */ + switch(relLocInfo->locatorType) + { + case LOCATOR_TYPE_REPLICATED: + /* In the replicated case this index is shippable */ + result = true; + break; + + case LOCATOR_TYPE_RROBIN: + /* + * Index on roundrobin parent table cannot be safely shipped + * because of the random behavior of data balancing. + */ + result = false; + break; + + case LOCATOR_TYPE_HASH: + case LOCATOR_TYPE_MODULO: + /* + * Index on Hash and Modulo tables are shippable if the index + * columns include the list of all column keys of distribution. + * We need also to check if this index uses expressions. Such + * expressions are not shippable if they do not satisfy the + * following conditions: + * - expression uses non-distribution columns + * - expression uses more than 1 distribution column + * + * Here is a short example with concatenate that cannot be + * shipped: + * CREATE TABLE aa (a text, b text) DISTRIBUTE BY HASH(a); + * CREATE UNIQUE INDEX aap ON aa((a || b)); + * INSERT INTO aa VALUES ('a', 'abb'); + * INSERT INTO aa VALUES ('aab', b); -- no error ??! + * The output uniqueness is not guaranteed as both INSERT will + * go to different nodes. For such simple reasons unique + * indexes on distributed tables are not shippable. + * Shippability is not even ensured if all the expressions + * use as Var only distributed colums as the hash output of + * their value combination does not ensure that query will + * be directed to the correct remote node. + * + * PGXCTODO: for the time being distribution key can only be + * defined on a single column, so this will need to be changed + * once such a feature is implemented. + */ + + /* Evaluate expression shippability based on index uniqueness */ + if (indexExprs != NIL) + { + /* + * Check if the expressions use only distribution columns. + * In order to evaluate that get all the variable expressions. + */ + List *indexVars = pull_var_clause((Node *) indexExprs, + PVC_RECURSE_AGGREGATES, + PVC_RECURSE_PLACEHOLDERS); + foreach(lc, indexVars) + { + Var *var = (Var *) lfirst(lc); + + /* + * Check if attribute is the distribution column. + * PGXCTODO: this will need modifications once + * multi-column distribution is implemented. + */ + if (var->varattno != relLocInfo->partAttrNum) + { + result = false; + break; + } + } + } + + /* Nothing to do if no attributes */ + if (indexAttrs == NIL) + break; + + /* + * Check that distribution column is included in the list of + * index columns. + */ + if (!list_member_int(indexAttrs, relLocInfo->partAttrNum)) + { + /* + * Distribution column is not in index column list + * So index can be enforced remotely. + */ + result = false; + break; + } + + /* + * by being here we are now sure that the index can be enforced + * remotely as the distribution column is included in index. + */ + break; + + /* Those types are not supported yet */ + case LOCATOR_TYPE_RANGE: + case LOCATOR_TYPE_SINGLE: + case LOCATOR_TYPE_NONE: + case LOCATOR_TYPE_DISTRIBUTED: + case LOCATOR_TYPE_CUSTOM: + default: + /* Should not come here */ + Assert(0); + } + } + +finish: + return result; +} + + +/* + * pgxc_check_fk_shippabilily + * Check the shippability of a parent and a child relation based on the + * distribution of each and the columns that are used to reference to + * parent and child relation. This can be used for inheritance or foreign + * key shippability evaluation. + */ +bool +pgxc_check_fk_shippability(RelationLocInfo *parentLocInfo, + RelationLocInfo *childLocInfo, + List *parentRefs, + List *childRefs) +{ + bool result = true; + + Assert(list_length(parentRefs) == list_length(childRefs)); + + /* + * If either child or parent have no relation data, shippability makes + * no sense. + */ + if (!parentLocInfo || !childLocInfo) + return result; + + /* In the case of a child referencing to itself, constraint is shippable */ + if (IsLocatorInfoEqual(parentLocInfo, childLocInfo)) + return result; + + /* Now begin the evaluation */ + switch (parentLocInfo->locatorType) + { + case LOCATOR_TYPE_REPLICATED: + /* + * If the parent relation is replicated, the child relation can + * always refer to it on all the nodes. + */ + result = true; + break; + + case LOCATOR_TYPE_RROBIN: + /* + * If the parent relation is based on roundrobin, the child + * relation cannot be enforced on remote nodes before of the + * random behavior of data balancing. + */ + result = false; + break; + + case LOCATOR_TYPE_HASH: + case LOCATOR_TYPE_MODULO: + /* + * If parent table is distributed, the child table can reference + * to its parent safely if the following conditions are satisfied: + * - parent and child are both hash-based, or both modulo-based + * - parent reference columns contain the distribution column + * of the parent relation + * - child reference columns contain the distribution column + * of the child relation + * - both child and parent map the same nodes for data location + */ + + /* A replicated child cannot refer to a distributed parent */ + if (IsLocatorReplicated(childLocInfo->locatorType)) + { + result = false; + break; + } + + /* + * Parent and child need to have the same distribution type: + * hash or modulo. + */ + if (parentLocInfo->locatorType != childLocInfo->locatorType) + { + result = false; + break; + } + + /* + * Parent and child need to have their data located exactly + * on the same list of nodes. + */ + if (list_difference_int(childLocInfo->nodeList, parentLocInfo->nodeList) || + list_difference_int(parentLocInfo->nodeList, childLocInfo->nodeList)) + { + result = false; + break; + } + + /* + * Check that child and parents are referenced using their + * distribution column. + */ + if (!list_member_int(childRefs, childLocInfo->partAttrNum) || + !list_member_int(parentRefs, parentLocInfo->partAttrNum)) + { + result = false; + break; + } + + /* By being here, parent-child constraint can be shipped correctly */ + break; + + case LOCATOR_TYPE_RANGE: + case LOCATOR_TYPE_SINGLE: + case LOCATOR_TYPE_NONE: + case LOCATOR_TYPE_DISTRIBUTED: + case LOCATOR_TYPE_CUSTOM: + default: + /* Should not come here */ + Assert(0); + } + + return result; +} diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 27e0a86e4b..9fd4a09fe4 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -53,6 +53,7 @@ #include "parser/parse_type.h" #include "parser/parse_utilcmd.h" #ifdef PGXC +#include "optimizer/pgxcship.h" #include "pgxc/locator.h" #include "pgxc/pgxc.h" #include "optimizer/pgxcplan.h" @@ -133,9 +134,6 @@ static void transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList); static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); static void setSchemaName(char *context_schema, char **stmt_schema_name); -#ifdef PGXC -static void checkLocalFKConstraints(CreateStmtContext *cxt); -#endif /* * transformCreateStmt - @@ -1475,9 +1473,6 @@ static IndexStmt * transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) { IndexStmt *index; -#ifdef PGXC - bool isLocalSafe = false; -#endif ListCell *lc; index = makeNode(IndexStmt); @@ -1740,22 +1735,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) if (strcmp(column->colname, key) == 0) { found = true; - -#ifdef PGXC - /* - * Only allow locally enforceable constraints. - * See if it is a distribution column - * If not set, set it to first column in index. - * If primary key, we prefer that over a unique constraint. - */ - if (IS_PGXC_COORDINATOR && !isLocalSafe) - { - if (cxt->distributeby) - isLocalSafe = CheckLocalIndexColumn ( - ConvertToLocatorType(cxt->distributeby->disttype), - cxt->distributeby->colname, key); - } -#endif break; } } @@ -1859,16 +1838,9 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) * If not set, set it to first column in index. * If primary key, we prefer that over a unique constraint. */ - if (index->indexParams == NIL - && (index->primary || !cxt->fallback_dist_col)) - { + if (index->indexParams == NIL && + (index->primary || !cxt->fallback_dist_col)) cxt->fallback_dist_col = pstrdup(key); - } - - /* Existing table, check if it is safe */ - if (cxt->isalter && !cxt->distributeby && !isLocalSafe) - isLocalSafe = CheckLocalIndexColumn ( - cxt->rel->rd_locator_info->locatorType, cxt->rel->rd_locator_info->partAttrName, key); } #endif @@ -1883,15 +1855,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; index->indexParams = lappend(index->indexParams, iparam); } -#ifdef PGXC - if (IS_PGXC_COORDINATOR && cxt->distributeby - && (cxt->distributeby->disttype == DISTTYPE_HASH || - cxt->distributeby->disttype == DISTTYPE_MODULO) - && !isLocalSafe) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("Unique index of partitioned table must contain the hash distribution column."))); -#endif return index; } @@ -1945,12 +1908,6 @@ transformFKConstraints(CreateStmtContext *cxt, } } -#ifdef PGXC - /* Only allow constraints that are locally enforceable - no distributed ones */ - if (IS_PGXC_COORDINATOR) - checkLocalFKConstraints(cxt); -#endif - /* * For CREATE TABLE or ALTER TABLE ADD COLUMN, gin up an ALTER TABLE ADD * CONSTRAINT command to execute after the basic command is complete. (If @@ -2906,171 +2863,3 @@ setSchemaName(char *context_schema, char **stmt_schema_name) "different from the one being created (%s)", *stmt_schema_name, context_schema))); } - -#ifdef PGXC -/* - * CheckLocalIndexColumn - * - * Checks whether or not the index can be safely enforced locally - */ -bool -CheckLocalIndexColumn (char loctype, char *partcolname, char *indexcolname) -{ - - if (loctype == LOCATOR_TYPE_REPLICATED) - /* always safe */ - return true; - if (loctype == LOCATOR_TYPE_RROBIN) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("Cannot locally enforce a unique index on round robin distributed table."))); - else if (loctype == LOCATOR_TYPE_HASH || loctype == LOCATOR_TYPE_MODULO) - { - if (partcolname && indexcolname && strcmp(partcolname, indexcolname) == 0) - return true; - } - return false; -} - - -/* - * check to see if the constraint can be enforced locally - * if not, an error will be thrown - */ -static void -checkLocalFKConstraints(CreateStmtContext *cxt) -{ - ListCell *fkclist; - - foreach(fkclist, cxt->fkconstraints) - { - Constraint *constraint; - Oid pk_rel_id; - char refloctype; - char *checkcolname = NULL; - - constraint = (Constraint *) lfirst(fkclist); - - /* - * If constraint references to the table itself, it is safe - * Check if relation name is the same - */ - if (constraint->pktable && - strcmp(constraint->pktable->relname,cxt->relation->relname) == 0) - { - /* Is namespace also the same ? */ - char *fkcon_schemaname = NULL; - - if (!cxt->relation->schemaname && - !constraint->pktable->schemaname) - continue; - - if (!constraint->pktable->schemaname) - { - /* Schema name is not defined, look for current one */ - List *search_path = fetch_search_path(false); - fkcon_schemaname = get_namespace_name(linitial_oid(search_path)); - list_free(search_path); - } - else - fkcon_schemaname = constraint->pktable->schemaname; - - /* - * If schema name and relation name are the same, table - * references to itself, so constraint is safe - */ - if (fkcon_schemaname && - strcmp(fkcon_schemaname, - cxt->relation->schemaname) == 0) - continue; - } - - pk_rel_id = RangeVarGetRelid(constraint->pktable, NoLock, false); - - refloctype = GetLocatorType(pk_rel_id); - - /* If referenced table is replicated, the constraint is safe */ - if (refloctype == LOCATOR_TYPE_REPLICATED) - continue; - else if (refloctype == LOCATOR_TYPE_RROBIN) - { - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Cannot reference a round robin table in a foreign key constraint"))); - } - - /* - * See if we are hash or modulo partitioned and the column appears in the - * constraint, and it corresponds to the position in the referenced table. - */ - if (cxt->isalter) - { - if (cxt->rel->rd_locator_info->locatorType == LOCATOR_TYPE_HASH || - cxt->rel->rd_locator_info->locatorType == LOCATOR_TYPE_MODULO) - { - checkcolname = cxt->rel->rd_locator_info->partAttrName; - } - } - else - { - if (cxt->distributeby) - { - if (cxt->distributeby->disttype == DISTTYPE_HASH || - cxt->distributeby->disttype == DISTTYPE_MODULO) - checkcolname = cxt->distributeby->colname; - } - else - { - if (cxt->fallback_dist_col) - checkcolname = cxt->fallback_dist_col; - } - } - - if (checkcolname) - { - int pos = 0; - - ListCell *attritem; - - foreach(attritem, constraint->fk_attrs) - { - char *attrname = (char *) strVal(lfirst(attritem)); - - if (strcmp(checkcolname, attrname) == 0) - { - /* Found the ordinal position in constraint */ - break; - } - pos++; - } - - if (pos >= list_length(constraint->fk_attrs)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Hash/Modulo distributed table must include distribution column in index"))); - - /* - * The check to make sure that the referenced column in pk table is the same - * as the one used to distribute it makes sense only when the user - * supplies the name of the referenced colum while adding the constraint - * because if the user did not specify it the system will choose the pk column - * which will obviously be the one used to distribute it knowing the - * existing constraints in XC - * This is required to make sure that both - * alter table dtab add foreign key (b) references rtab(a); - * and - * alter table dtab add foreign key (b) references rtab; - * behave similarly - */ - if (constraint->pk_attrs != NULL) - { - /* Verify that the referenced table is partitioned at the same position in the index */ - if (!IsDistColumnForRelId(pk_rel_id, strVal(list_nth(constraint->pk_attrs,pos)))) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("Hash/Modulo distribution column does not refer to hash/modulo distribution column in referenced table."))); - } - } - } -} -#endif diff --git a/src/include/optimizer/pgxcship.h b/src/include/optimizer/pgxcship.h index 0f960e1a5a..e9e0ccbd42 100644 --- a/src/include/optimizer/pgxcship.h +++ b/src/include/optimizer/pgxcship.h @@ -33,5 +33,17 @@ extern bool pgxc_qual_has_dist_equijoin(Relids varnos_1, /* Merge given execution nodes based on join shippability conditions */ extern ExecNodes *pgxc_merge_exec_nodes(ExecNodes *en1, ExecNodes *en2, bool merge_dist_equijoin, bool merge_replicated_only); +/* Check the shippability of an index */ +extern bool pgxc_check_index_shippability(RelationLocInfo *relLocInfo, + bool is_primary, + bool is_unique, + bool is_exclusion, + List *indexAttrs, + List *indexExprs); +/* Check the shippability of a parent-child constraint */ +extern bool pgxc_check_fk_shippability(RelationLocInfo *parentLocInfo, + RelationLocInfo *childLocInfo, + List *parentRefs, + List *childRefs); #endif diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 4ad0800f6a..fc33889b02 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -680,7 +680,7 @@ drop table atacc1; create table atacc1 ( test int ) distribute by roundrobin; -- add a unique constraint (fails) alter table atacc1 add constraint atacc_test1 unique (test1); -ERROR: Cannot locally enforce a unique index on round robin distributed table. +ERROR: column "test1" named in key does not exist drop table atacc1; -- something a little more complicated create table atacc1 ( test int, test2 int); @@ -2283,7 +2283,7 @@ CREATE TABLE tt8(a int); CREATE SCHEMA alter2; ALTER TABLE IF EXISTS tt8 ADD COLUMN f int; ALTER TABLE IF EXISTS tt8 ADD CONSTRAINT xxx PRIMARY KEY(f); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes ALTER TABLE IF EXISTS tt8 ADD CHECK (f BETWEEN 0 AND 10); ALTER TABLE IF EXISTS tt8 ALTER COLUMN f SET DEFAULT 0; ALTER TABLE IF EXISTS tt8 RENAME COLUMN f TO f1; diff --git a/src/test/regress/expected/cluster_1.out b/src/test/regress/expected/cluster_1.out index f6fbf98036..ca403bb98e 100644 --- a/src/test/regress/expected/cluster_1.out +++ b/src/test/regress/expected/cluster_1.out @@ -11,7 +11,8 @@ CREATE TABLE clstr_tst (a SERIAL PRIMARY KEY, d TEXT, CONSTRAINT clstr_tst_con FOREIGN KEY (b) REFERENCES clstr_tst_s); NOTICE: CREATE TABLE will create implicit sequence "clstr_tst_a_seq" for serial column "clstr_tst.a" -ERROR: Hash/Modulo distributed table must include distribution column in index +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "clstr_tst_pkey" for table "clstr_tst" +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes CREATE INDEX clstr_tst_b ON clstr_tst (b); ERROR: relation "clstr_tst" does not exist CREATE INDEX clstr_tst_c ON clstr_tst (c); diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 5d8421853f..2449c2ebbf 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -2063,28 +2063,36 @@ CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops); -- -- Test functional index -- -CREATE TABLE func_index_heap (f1 text, f2 text); +-- PGXC: Here replication is used to ensure correct index creation +-- when a non-shippable expression is used. +-- PGXCTODO: this should be removed once global constraints are supported +CREATE TABLE func_index_heap (f1 text, f2 text) DISTRIBUTE BY REPLICATION; CREATE UNIQUE INDEX func_index_index on func_index_heap (textcat(f1,f2)); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. INSERT INTO func_index_heap VALUES('ABC','DEF'); INSERT INTO func_index_heap VALUES('AB','CDEFG'); INSERT INTO func_index_heap VALUES('QWE','RTY'); -- this should fail because of unique index: INSERT INTO func_index_heap VALUES('ABCD', 'EF'); +ERROR: duplicate key value violates unique constraint "func_index_index" +DETAIL: Key (textcat(f1, f2))=(ABCDEF) already exists. -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); -- -- Same test, expressional index -- DROP TABLE func_index_heap; -CREATE TABLE func_index_heap (f1 text, f2 text); +-- PGXC: Here replication is used to ensure correct index creation +-- when a non-shippable expression is used. +-- PGXCTODO: this should be removed once global constraints are supported +CREATE TABLE func_index_heap (f1 text, f2 text) DISTRIBUTE BY REPLICATION; CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. INSERT INTO func_index_heap VALUES('ABC','DEF'); INSERT INTO func_index_heap VALUES('AB','CDEFG'); INSERT INTO func_index_heap VALUES('QWE','RTY'); -- this should fail because of unique index: INSERT INTO func_index_heap VALUES('ABCD', 'EF'); +ERROR: duplicate key value violates unique constraint "func_index_index" +DETAIL: Key ((f1 || f2))=(ABCDEF) already exists. -- but this shouldn't: INSERT INTO func_index_heap VALUES('QWERTY'); -- @@ -2092,11 +2100,8 @@ INSERT INTO func_index_heap VALUES('QWERTY'); -- tables that already contain data. -- create unique index hash_f8_index_1 on hash_f8_heap(abs(random)); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. create unique index hash_f8_index_2 on hash_f8_heap((seqno + 1), random); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. create unique index hash_f8_index_3 on hash_f8_heap(random) where seqno > 1000; -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. -- -- Try some concurrent index builds -- @@ -2386,7 +2391,7 @@ SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500; DROP INDEX onek_nulltest; -- Check initial-positioning logic too CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out index 6718121e46..8514f2d3df 100644 --- a/src/test/regress/expected/create_table.out +++ b/src/test/regress/expected/create_table.out @@ -159,10 +159,13 @@ CREATE TABLE hash_txt_heap ( seqno int4, random text ); +-- PGXC: Here replication is used to ensure correct index creation +-- when a non-shippable expression is used. +-- PGXCTODO: this should be removed once global constraints are supported CREATE TABLE hash_f8_heap ( seqno int4, random float8 -); +) DISTRIBUTE BY REPLICATION; -- don't include the hash_ovfl_heap stuff in the distribution -- the data set is too large for what it's worth -- diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out index 23c5ade6a7..7d29b72622 100644 --- a/src/test/regress/expected/enum.out +++ b/src/test/regress/expected/enum.out @@ -288,7 +288,7 @@ SET enable_bitmapscan = off; -- Btree index / opclass with the various operators -- CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); -ERROR: Cannot locally enforce a unique index on round robin distributed table. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes SELECT * FROM enumtest WHERE col = 'orange'; col -------- diff --git a/src/test/regress/expected/functional_deps_1.out b/src/test/regress/expected/functional_deps_1.out index 2230bcb28b..899ad83255 100644 --- a/src/test/regress/expected/functional_deps_1.out +++ b/src/test/regress/expected/functional_deps_1.out @@ -9,7 +9,7 @@ CREATE TEMP TABLE articles ( created date ); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "articles_pkey" for table "articles" -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes CREATE TEMP TABLE articles_in_category ( article_id int, category_id int, @@ -148,7 +148,7 @@ CREATE TEMP TABLE users ( UNIQUE (name) ); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users" -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes -- OK SELECT u.uid, u.name FROM node n INNER JOIN users u ON u.uid = n.uid diff --git a/src/test/regress/expected/hash_index_1.out b/src/test/regress/expected/hash_index_1.out index 26f7053a89..de2b052d8e 100644 --- a/src/test/regress/expected/hash_index_1.out +++ b/src/test/regress/expected/hash_index_1.out @@ -182,13 +182,12 @@ SELECT h.seqno AS i8096, h.random AS f1234_1234 UPDATE hash_f8_heap SET seqno = 20000 WHERE hash_f8_heap.random = '488912369'::float8; -ERROR: Partition column can't be updated in current version SELECT h.seqno AS f20000 FROM hash_f8_heap h WHERE h.random = '488912369'::float8; f20000 -------- - 8932 + 20000 (1 row) -- UPDATE hash_ovfl_heap diff --git a/src/test/regress/expected/namespace_1.out b/src/test/regress/expected/namespace_1.out index 533028cb01..56762a2534 100644 --- a/src/test/regress/expected/namespace_1.out +++ b/src/test/regress/expected/namespace_1.out @@ -11,7 +11,7 @@ CREATE SCHEMA test_schema_1 ); NOTICE: CREATE TABLE will create implicit sequence "abc_a_seq" for serial column "abc.a" NOTICE: CREATE TABLE / UNIQUE will create implicit index "abc_b_key" for table "abc" -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes -- verify that the objects were created SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1'); diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out index 677a311f81..9a338dfd8f 100644 --- a/src/test/regress/expected/rangetypes.out +++ b/src/test/regress/expected/rangetypes.out @@ -835,7 +835,7 @@ create table test_range_excl( during tsrange, exclude using gist (room with =, during with &&), exclude using gist (speaker with =, during with &&) -); +) distribute by replication; NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_room_during_excl" for table "test_range_excl" NOTICE: CREATE TABLE / EXCLUDE will create implicit index "test_range_excl_speaker_during_excl" for table "test_range_excl" insert into test_range_excl diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 080a501dd8..09b452a25e 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -47,7 +47,7 @@ SELECT relname, relhasindex fast_emp4000 | t float4_tbl | f float8_tbl | f - func_index_heap | f + func_index_heap | t hash_f8_heap | t hash_i4_heap | t hash_name_heap | t diff --git a/src/test/regress/expected/triggers_1.out b/src/test/regress/expected/triggers_1.out index 8c9ae67deb..b54717d387 100644 --- a/src/test/regress/expected/triggers_1.out +++ b/src/test/regress/expected/triggers_1.out @@ -983,7 +983,7 @@ CREATE TABLE country_table ( ); NOTICE: CREATE TABLE will create implicit sequence "country_table_country_id_seq" for serial column "country_table.country_id" NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "country_table_pkey" for table "country_table" -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes INSERT INTO country_table (country_name, continent) VALUES ('Japan', 'Asia'), ('UK', 'Europe'), @@ -999,6 +999,7 @@ CREATE TABLE city_table ( country_id int references country_table ); NOTICE: CREATE TABLE will create implicit sequence "city_table_city_id_seq" for serial column "city_table.city_id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "city_table_pkey" for table "city_table" ERROR: relation "country_table" does not exist CREATE VIEW city_view AS SELECT city_id, city_name, population, country_name, continent diff --git a/src/test/regress/expected/tsearch_2.out b/src/test/regress/expected/tsearch_2.out index bd81fff125..9c53cf75fc 100644 --- a/src/test/regress/expected/tsearch_2.out +++ b/src/test/regress/expected/tsearch_2.out @@ -813,7 +813,7 @@ SELECT COUNT(*) FROM test_tsquery WHERE keyword > 'new & york'; (1 row) CREATE UNIQUE INDEX bt_tsq ON test_tsquery (keyword); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes SET enable_seqscan=OFF; SELECT COUNT(*) FROM test_tsquery WHERE keyword < 'new & york'; count diff --git a/src/test/regress/expected/typed_table_1.out b/src/test/regress/expected/typed_table_1.out index 5c7fc0c9e6..885959794b 100644 --- a/src/test/regress/expected/typed_table_1.out +++ b/src/test/regress/expected/typed_table_1.out @@ -46,7 +46,7 @@ CREATE TABLE persons2 OF person_type ( UNIQUE (name) ); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "persons2_pkey" for table "persons2" -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes \d persons2 CREATE TABLE persons3 OF person_type ( PRIMARY KEY (id), diff --git a/src/test/regress/expected/uuid_1.out b/src/test/regress/expected/uuid_1.out index 982f1dd7b2..9cb6fe2481 100644 --- a/src/test/regress/expected/uuid_1.out +++ b/src/test/regress/expected/uuid_1.out @@ -116,7 +116,7 @@ CREATE INDEX guid1_btree ON guid1 USING BTREE (guid_field); CREATE INDEX guid1_hash ON guid1 USING HASH (guid_field); -- unique index test CREATE UNIQUE INDEX guid1_unique_BTREE ON guid1 USING BTREE (guid_field); -ERROR: Unique index of partitioned table must contain the hash/modulo distribution column. +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes -- should fail INSERT INTO guid1(guid_field) VALUES('11111111-1111-1111-1111-111111111111'); -- check to see whether the new indexes are actually there diff --git a/src/test/regress/expected/xc_constraints.out b/src/test/regress/expected/xc_constraints.out new file mode 100644 index 0000000000..7351de5381 --- /dev/null +++ b/src/test/regress/expected/xc_constraints.out @@ -0,0 +1,88 @@ +-- +-- XC_CONSTRAINTS +-- +-- Checks for constraint shippability in Postgres-XC for tables with different +-- distribution strategies +-- Create some tables for all the tests +CREATE TABLE xc_cons_rep (c1 int, c2 text, c3 text) DISTRIBUTE BY REPLICATION; +CREATE TABLE xc_cons_rr (c1 int, c2 text, c3 text) DISTRIBUTE BY ROUNDROBIN; +CREATE TABLE xc_cons_hash (c1 text, c2 text, c3 text) DISTRIBUTE BY HASH(c1); +-- UNIQUE/PRIMARY KEY constraint +-- Replicated table +CREATE UNIQUE INDEX xc_cons_rep_unique1 ON xc_cons_rep(c1); -- OK +CREATE UNIQUE INDEX xc_cons_rep_unique2 ON xc_cons_rep(c1,c2); -- OK +CREATE UNIQUE INDEX xc_cons_rep_unique3 ON xc_cons_rep((c2 || c3)); -- OK +-- Roundrobin table +CREATE UNIQUE INDEX xc_cons_rr_unique1 ON xc_cons_rr(c1); -- error, not shippable +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +CREATE UNIQUE INDEX xc_cons_rr_unique2 ON xc_cons_rr(c1,c2); -- error, not shippable +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +CREATE UNIQUE INDEX xc_cons_rr_unique3 ON xc_cons_rr((c2 || c3)); -- error, not shippable +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +-- Distributed table +-- OK, is distribution column +CREATE UNIQUE INDEX xc_cons_hash_unique1 ON xc_cons_hash(c1); +-- OK, contains distribution column +CREATE UNIQUE INDEX xc_cons_hash_unique2 ON xc_cons_hash(c1,c2); +-- OK, expression contains only distribution column +CREATE UNIQUE INDEX xc_cons_hash_unique3 ON xc_cons_hash((c1 || c1)); +-- error, expression contains other columns than distribution one +CREATE UNIQUE INDEX xc_cons_hash_unique3 ON xc_cons_hash((c1 || c2)); +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +-- Clean up +DROP TABLE xc_cons_rep,xc_cons_rr,xc_cons_hash; +-- EXCLUDE +CREATE TABLE xc_cons_hash (a int, c circle, EXCLUDE USING gist (c WITH &&)) DISTRIBUTE BY HASH(a); -- error +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_cons_rr (c circle, EXCLUDE USING gist (c WITH &&)) DISTRIBUTE BY ROUNDROBIN; -- error +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_cons_rep (c circle, EXCLUDE USING gist (c WITH &&)) DISTRIBUTE BY REPLICATION; -- OK +NOTICE: CREATE TABLE / EXCLUDE will create implicit index "xc_cons_rep_c_excl" for table "xc_cons_rep" +DROP TABLE xc_cons_rep; +-- FOREIGN KEY creation +CREATE SCHEMA xc_constraints_tests; +SET search_path = xc_constraints_tests; +-- Some parent tables +CREATE TABLE xc_parent_rep (a int PRIMARY KEY) DISTRIBUTE BY REPLICATION; -- OK +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "xc_parent_rep_pkey" for table "xc_parent_rep" +CREATE TABLE xc_parent_hash (a int PRIMARY KEY) DISTRIBUTE BY HASH(a); -- OK +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "xc_parent_hash_pkey" for table "xc_parent_hash" +CREATE TABLE xc_parent_modulo (a int PRIMARY KEY) DISTRIBUTE BY MODULO(a); -- OK +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "xc_parent_modulo_pkey" for table "xc_parent_modulo" +-- Test on roundrobin does not make sense as it cannot have a primary key +CREATE TABLE xc_parent_rr (a int PRIMARY KEY) DISTRIBUTE BY ROUNDROBIN; -- error +ERROR: Cannot create index whose evaluation cannot be enforced to remote nodes +-- Test creation of child tables referencing to parents +-- To replicated parent +CREATE TABLE xc_child_rep_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY REPLICATION; -- OK +CREATE TABLE xc_child_rr_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY ROUNDROBIN; -- OK +CREATE TABLE xc_child_modulo_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY MODULO(b); -- OK +CREATE TABLE xc_child_hash_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY HASH(b); -- OK +-- To hash parent +CREATE TABLE xc_child_rep_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY REPLICATION; -- error +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_child_rr_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY ROUNDROBIN; -- error +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_child_modulo_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY MODULO(b); -- error +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_child_hash_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY HASH(b); -- OK +-- To modulo parent +CREATE TABLE xc_child_rep_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY REPLICATION; -- error +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_child_rr_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY ROUNDROBIN; -- error +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes +CREATE TABLE xc_child_modulo_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY MODULO(b); -- OK +CREATE TABLE xc_child_hash_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY HASH(b); -- error +ERROR: Cannot create foreign key whose evaluation cannot be enforced to remote nodes +-- Clean up +DROP SCHEMA xc_constraints_tests CASCADE; +NOTICE: drop cascades to 9 other objects +DETAIL: drop cascades to table xc_parent_rep +drop cascades to table xc_parent_hash +drop cascades to table xc_parent_modulo +drop cascades to table xc_child_rep_to_rep +drop cascades to table xc_child_rr_to_rep +drop cascades to table xc_child_modulo_to_rep +drop cascades to table xc_child_hash_to_rep +drop cascades to table xc_child_hash_to_hash +drop cascades to table xc_child_modulo_to_modulo diff --git a/src/test/regress/input/constraints.source b/src/test/regress/input/constraints.source index 17d93caaef..c09a7bce1b 100644 --- a/src/test/regress/input/constraints.source +++ b/src/test/regress/input/constraints.source @@ -406,7 +406,7 @@ CREATE TABLE circles ( EXCLUDE USING gist (c1 WITH &&, (c2::circle) WITH &&) WHERE (circle_center(c1) <> '(0,0)') -); +) DISTRIBUTE BY REPLICATION; -- these should succeed because they don't match the index predicate INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>'); diff --git a/src/test/regress/output/constraints_1.source b/src/test/regress/output/constraints_1.source index a267534a87..f2c296c87d 100644 --- a/src/test/regress/output/constraints_1.source +++ b/src/test/regress/output/constraints_1.source @@ -577,7 +577,7 @@ CREATE TABLE circles ( EXCLUDE USING gist (c1 WITH &&, (c2::circle) WITH &&) WHERE (circle_center(c1) <> '(0,0)') -); +) DISTRIBUTE BY REPLICATION; NOTICE: CREATE TABLE / EXCLUDE will create implicit index "circles_c1_c2_excl" for table "circles" -- these should succeed because they don't match the index predicate INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>'); diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 35c4e7d444..93347af701 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -113,7 +113,7 @@ test: stats # This creates functions used by tests xc_misc, xc_FQS and xc_FQS_join test: xc_create_function # Those ones can be run in parallel -test: xc_groupby xc_distkey xc_having xc_temp xc_remote xc_FQS xc_FQS_join xc_copy xc_for_update xc_alter_table xc_sequence xc_misc xc_triggers +test: xc_groupby xc_distkey xc_having xc_temp xc_remote xc_FQS xc_FQS_join xc_copy xc_for_update xc_alter_table xc_sequence xc_misc xc_triggers xc_constraints # Cluster setting related test is independant test: xc_node diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index a0b47dedca..d119cf6c66 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -143,6 +143,7 @@ test: xc_FQS test: xc_FQS_join test: xc_misc test: xc_triggers +test: xc_constraints test: xc_copy test: xc_for_update test: xc_alter_table diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 0550736f30..eaeeeffcee 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -655,7 +655,10 @@ CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops); -- -- Test functional index -- -CREATE TABLE func_index_heap (f1 text, f2 text); +-- PGXC: Here replication is used to ensure correct index creation +-- when a non-shippable expression is used. +-- PGXCTODO: this should be removed once global constraints are supported +CREATE TABLE func_index_heap (f1 text, f2 text) DISTRIBUTE BY REPLICATION; CREATE UNIQUE INDEX func_index_index on func_index_heap (textcat(f1,f2)); INSERT INTO func_index_heap VALUES('ABC','DEF'); @@ -671,7 +674,10 @@ INSERT INTO func_index_heap VALUES('QWERTY'); -- Same test, expressional index -- DROP TABLE func_index_heap; -CREATE TABLE func_index_heap (f1 text, f2 text); +-- PGXC: Here replication is used to ensure correct index creation +-- when a non-shippable expression is used. +-- PGXCTODO: this should be removed once global constraints are supported +CREATE TABLE func_index_heap (f1 text, f2 text) DISTRIBUTE BY REPLICATION; CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops); INSERT INTO func_index_heap VALUES('ABC','DEF'); diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql index f67431f699..aadadcb8d3 100644 --- a/src/test/regress/sql/create_table.sql +++ b/src/test/regress/sql/create_table.sql @@ -186,10 +186,13 @@ CREATE TABLE hash_txt_heap ( random text ); +-- PGXC: Here replication is used to ensure correct index creation +-- when a non-shippable expression is used. +-- PGXCTODO: this should be removed once global constraints are supported CREATE TABLE hash_f8_heap ( seqno int4, random float8 -); +) DISTRIBUTE BY REPLICATION; -- don't include the hash_ovfl_heap stuff in the distribution -- the data set is too large for what it's worth diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql index 5f95f05325..36ba8c46f4 100644 --- a/src/test/regress/sql/rangetypes.sql +++ b/src/test/regress/sql/rangetypes.sql @@ -236,7 +236,7 @@ create table test_range_excl( during tsrange, exclude using gist (room with =, during with &&), exclude using gist (speaker with =, during with &&) -); +) distribute by replication; insert into test_range_excl values(int4range(123, 123, '[]'), int4range(1, 1, '[]'), '[2010-01-02 10:00, 2010-01-02 11:00)'); diff --git a/src/test/regress/sql/xc_constraints.sql b/src/test/regress/sql/xc_constraints.sql new file mode 100644 index 0000000000..8c98b2e13b --- /dev/null +++ b/src/test/regress/sql/xc_constraints.sql @@ -0,0 +1,65 @@ +-- +-- XC_CONSTRAINTS +-- + +-- Checks for constraint shippability in Postgres-XC for tables with different +-- distribution strategies + +-- Create some tables for all the tests +CREATE TABLE xc_cons_rep (c1 int, c2 text, c3 text) DISTRIBUTE BY REPLICATION; +CREATE TABLE xc_cons_rr (c1 int, c2 text, c3 text) DISTRIBUTE BY ROUNDROBIN; +CREATE TABLE xc_cons_hash (c1 text, c2 text, c3 text) DISTRIBUTE BY HASH(c1); + +-- UNIQUE/PRIMARY KEY constraint +-- Replicated table +CREATE UNIQUE INDEX xc_cons_rep_unique1 ON xc_cons_rep(c1); -- OK +CREATE UNIQUE INDEX xc_cons_rep_unique2 ON xc_cons_rep(c1,c2); -- OK +CREATE UNIQUE INDEX xc_cons_rep_unique3 ON xc_cons_rep((c2 || c3)); -- OK +-- Roundrobin table +CREATE UNIQUE INDEX xc_cons_rr_unique1 ON xc_cons_rr(c1); -- error, not shippable +CREATE UNIQUE INDEX xc_cons_rr_unique2 ON xc_cons_rr(c1,c2); -- error, not shippable +CREATE UNIQUE INDEX xc_cons_rr_unique3 ON xc_cons_rr((c2 || c3)); -- error, not shippable +-- Distributed table +-- OK, is distribution column +CREATE UNIQUE INDEX xc_cons_hash_unique1 ON xc_cons_hash(c1); +-- OK, contains distribution column +CREATE UNIQUE INDEX xc_cons_hash_unique2 ON xc_cons_hash(c1,c2); +-- OK, expression contains only distribution column +CREATE UNIQUE INDEX xc_cons_hash_unique3 ON xc_cons_hash((c1 || c1)); +-- error, expression contains other columns than distribution one +CREATE UNIQUE INDEX xc_cons_hash_unique3 ON xc_cons_hash((c1 || c2)); +-- Clean up +DROP TABLE xc_cons_rep,xc_cons_rr,xc_cons_hash; +-- EXCLUDE +CREATE TABLE xc_cons_hash (a int, c circle, EXCLUDE USING gist (c WITH &&)) DISTRIBUTE BY HASH(a); -- error +CREATE TABLE xc_cons_rr (c circle, EXCLUDE USING gist (c WITH &&)) DISTRIBUTE BY ROUNDROBIN; -- error +CREATE TABLE xc_cons_rep (c circle, EXCLUDE USING gist (c WITH &&)) DISTRIBUTE BY REPLICATION; -- OK +DROP TABLE xc_cons_rep; + +-- FOREIGN KEY creation +CREATE SCHEMA xc_constraints_tests; +SET search_path = xc_constraints_tests; +-- Some parent tables +CREATE TABLE xc_parent_rep (a int PRIMARY KEY) DISTRIBUTE BY REPLICATION; -- OK +CREATE TABLE xc_parent_hash (a int PRIMARY KEY) DISTRIBUTE BY HASH(a); -- OK +CREATE TABLE xc_parent_modulo (a int PRIMARY KEY) DISTRIBUTE BY MODULO(a); -- OK +-- Test on roundrobin does not make sense as it cannot have a primary key +CREATE TABLE xc_parent_rr (a int PRIMARY KEY) DISTRIBUTE BY ROUNDROBIN; -- error +-- Test creation of child tables referencing to parents +-- To replicated parent +CREATE TABLE xc_child_rep_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY REPLICATION; -- OK +CREATE TABLE xc_child_rr_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY ROUNDROBIN; -- OK +CREATE TABLE xc_child_modulo_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY MODULO(b); -- OK +CREATE TABLE xc_child_hash_to_rep (b int, FOREIGN KEY (b) REFERENCES xc_parent_rep(a)) DISTRIBUTE BY HASH(b); -- OK +-- To hash parent +CREATE TABLE xc_child_rep_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY REPLICATION; -- error +CREATE TABLE xc_child_rr_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY ROUNDROBIN; -- error +CREATE TABLE xc_child_modulo_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY MODULO(b); -- error +CREATE TABLE xc_child_hash_to_hash (b int, FOREIGN KEY (b) REFERENCES xc_parent_hash(a)) DISTRIBUTE BY HASH(b); -- OK +-- To modulo parent +CREATE TABLE xc_child_rep_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY REPLICATION; -- error +CREATE TABLE xc_child_rr_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY ROUNDROBIN; -- error +CREATE TABLE xc_child_modulo_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY MODULO(b); -- OK +CREATE TABLE xc_child_hash_to_modulo (b int, FOREIGN KEY (b) REFERENCES xc_parent_modulo(a)) DISTRIBUTE BY HASH(b); -- error +-- Clean up +DROP SCHEMA xc_constraints_tests CASCADE; |