/* * Does the supplied GpPolicy support unique indexing on the specified * attributes? * * If the table is distributed randomly, no unique indexing is supported. * Otherwise, the set of columns being indexed should be a superset of the * policy. * * If the proposed index does not match the distribution policy but the relation * is empty and does not have a primary key or unique index, update the * distribution policy to match the index definition (MPP-101), as long as it * doesn't contain expressions. */ void checkPolicyForUniqueIndex(Relation rel, AttrNumber *indattr, int nidxatts, bool isprimary, bool has_exprs, bool has_pkey, bool has_ukey) { Bitmapset *polbm = NULL; Bitmapset *indbm = NULL; int i; GpPolicy *pol = rel->rd_cdbpolicy; /* * Firstly, unique/primary key indexes aren't supported if we're * distributing randomly. */ if (GpPolicyIsRandomly(pol)) { ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("%s and DISTRIBUTED RANDOMLY are incompatible", isprimary ? "PRIMARY KEY" : "UNIQUE"))); } /* * We use bitmaps to make intersection tests easier. As noted, order is * not relevant so looping is just painful. */ for (i = 0; i < pol->nattrs; i++) polbm = bms_add_member(polbm, pol->attrs[i]); for (i = 0; i < nidxatts; i++) { if (indattr[i] < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("cannot create %s on system column", isprimary ? "primary key" : "unique index"))); indbm = bms_add_member(indbm, indattr[i]); } Assert(bms_membership(polbm) != BMS_EMPTY_SET); Assert(bms_membership(indbm) != BMS_EMPTY_SET); /* * If the existing policy is not a subset, we must either error out or * update the distribution policy. It might be tempting to say that even * when the policy is a subset, we should update it to match the index * definition. The problem then is that if the user actually wants to * distribution on (a, b) but then creates an index on (a, b, c) we'll * change the policy underneath them. * * What is really needed is a new field in gp_distribution_policy telling us * if the policy has been explicitly set. */ if (!bms_is_subset(polbm, indbm)) { if (cdbRelSize(rel) != 0 || has_pkey || has_ukey || has_exprs) { ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("%s must contain all columns in the " "distribution key of relation \"%s\"", isprimary ? "PRIMARY KEY" : "UNIQUE index", RelationGetRelationName(rel)))); } else { /* update policy since table is not populated yet. See MPP-101 */ GpPolicy *policy = palloc(sizeof(GpPolicy) + (sizeof(AttrNumber) * nidxatts)); policy->ptype = POLICYTYPE_PARTITIONED; policy->nattrs = 0; for (i = 0; i < nidxatts; i++) policy->attrs[policy->nattrs++] = indattr[i]; GpPolicyReplace(rel->rd_id, policy); if (isprimary) elog(NOTICE, "updating distribution policy to match new primary key"); else elog(NOTICE, "updating distribution policy to match new unique index"); } } }
/* * ValidateErrorTableMetaData * * This function gets called if a user wants an already existing table to be * used as an error table for some COPY or external table operation with SREH. * In here we verify that the metadata of the user selected table matches the * predefined requirement for an error table. */ void ValidateErrorTableMetaData(Relation rel) { TupleDesc tupDesc = RelationGetDescr(rel); Form_pg_attribute *attr = tupDesc->attrs; TupleConstr *constr = tupDesc->constr; char *relname = RelationGetRelationName(rel); int attr_count = tupDesc->natts; /* * Verify it is an ordinary table. */ if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("error table \"%s\" is not a table", relname))); /* * Verify it is a heap table. */ if (!RelationIsHeap(rel)) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("error table \"%s\" is not a heap table", relname), errhint("Use a relation with heap storage type for error table"))); /* * Verify number of attributes match */ if (attr_count != NUM_ERRORTABLE_ATTR) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("Relation \"%s\" already exists and is not of a valid " "error table format (expected %d attributes, found %d)", relname, NUM_ERRORTABLE_ATTR, attr_count))); /* * Verify this table is randomly-distributed. */ if (!GpPolicyIsRandomly(rel->rd_cdbpolicy)) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("error table \"%s\" is not randomly distributed", relname), errhint("Use a randomly-distributed realtion for error table"))); /* * Verify this table is not a partitioned or child partition table. */ if (rel_is_partitioned(RelationGetRelid(rel)) || rel_is_child_partition(RelationGetRelid(rel))) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("error table \"%s\" is a partitioned table", relname), errdetail("Either root or child partition are not allowed as an error table"))); /* * Verify this table has no constraints. * * This errors out with DEFAULTs, CHECKs or NOT NULLs. * * Unique constraint is not allowed in randomly-distributed table * so we don't check it here. * * We never insert NULL values in some attributes, but allowing * NOT NULL for error table columns doesn't give much convenience * to the user anyway. Likewise, DEFAULT values are not of interest * mostly, but it does not give much value to user, either. */ if (constr) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("Relation \"%s\" already exists and is not of a valid " "error table format. If appears to have constraints " "defined.", relname))); /* * run through each attribute at a time and verify it's what we expect */ VerifyErrorTableAttr(attr, errtable_cmdtime, "cmdtime", TIMESTAMPTZOID, relname); VerifyErrorTableAttr(attr, errtable_relname, "relname", TEXTOID, relname); VerifyErrorTableAttr(attr, errtable_filename, "filename", TEXTOID, relname); VerifyErrorTableAttr(attr, errtable_linenum, "linenum", INT4OID, relname); VerifyErrorTableAttr(attr, errtable_bytenum, "bytenum", INT4OID, relname); VerifyErrorTableAttr(attr, errtable_errmsg, "errmsg", TEXTOID, relname); VerifyErrorTableAttr(attr, errtable_rawdata, "rawdata", TEXTOID, relname); VerifyErrorTableAttr(attr, errtable_rawbytes, "rawbytes", BYTEAOID, relname); }