mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 10:30:33 +03:00 
			
		
		
		
	Indexes with INCLUDE columns and their support in B-tree
This patch introduces INCLUDE clause to index definition. This clause specifies a list of columns which will be included as a non-key part in the index. The INCLUDE columns exist solely to allow more queries to benefit from index-only scans. Also, such columns don't need to have appropriate operator classes. Expressions are not supported as INCLUDE columns since they cannot be used in index-only scans. Index access methods supporting INCLUDE are indicated by amcaninclude flag in IndexAmRoutine. For now, only B-tree indexes support INCLUDE clause. In B-tree indexes INCLUDE columns are truncated from pivot index tuples (tuples located in non-leaf pages and high keys). Therefore, B-tree indexes now might have variable number of attributes. This patch also provides generic facility to support that: pivot tuples contain number of their attributes in t_tid.ip_posid. Free 13th bit of t_info is used for indicating that. This facility will simplify further support of index suffix truncation. The changes of above are backward-compatible, pg_upgrade doesn't need special handling of B-tree indexes for that. Bump catalog version Author: Anastasia Lubennikova with contribition by Alexander Korotkov and me Reviewed by: Peter Geoghegan, Tomas Vondra, Antonin Houska, Jeff Janes, David Rowley, Alexander Korotkov Discussion: https://www.postgresql.org/message-id/flat/56168952.4010101@postgrespro.ru
This commit is contained in:
		| @@ -1051,7 +1051,7 @@ transformOnConflictClause(ParseState *pstate, | ||||
| 		 * relation.  Have to be careful to use resnos that correspond to | ||||
| 		 * attnos of the underlying relation. | ||||
| 		 */ | ||||
| 		for (attno = 0; attno < targetrel->rd_rel->relnatts; attno++) | ||||
| 		for (attno = 0; attno < RelationGetNumberOfAttributes(targetrel); attno++) | ||||
| 		{ | ||||
| 			Form_pg_attribute attr = TupleDescAttr(targetrel->rd_att, attno); | ||||
| 			char	   *name; | ||||
| @@ -2276,8 +2276,8 @@ transformUpdateTargetList(ParseState *pstate, List *origTlist) | ||||
| 								EXPR_KIND_UPDATE_SOURCE); | ||||
|  | ||||
| 	/* Prepare to assign non-conflicting resnos to resjunk attributes */ | ||||
| 	if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) | ||||
| 		pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; | ||||
| 	if (pstate->p_next_resno <= RelationGetNumberOfAttributes(pstate->p_target_relation)) | ||||
| 		pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; | ||||
|  | ||||
| 	/* Prepare non-junk columns for assignment to target table */ | ||||
| 	target_rte = pstate->p_target_rangetblentry; | ||||
|   | ||||
| @@ -382,6 +382,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
| 				oper_argtypes RuleActionList RuleActionMulti | ||||
| 				opt_column_list columnList opt_name_list | ||||
| 				sort_clause opt_sort_clause sortby_list index_params | ||||
| 				opt_include opt_c_include index_including_params | ||||
| 				name_list role_list from_clause from_list opt_array_bounds | ||||
| 				qualified_name_list any_name any_name_list type_name_list | ||||
| 				any_operator expr_list attrs | ||||
| @@ -645,7 +646,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); | ||||
|  | ||||
| 	HANDLER HAVING HEADER_P HOLD HOUR_P | ||||
|  | ||||
| 	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P | ||||
| 	IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P INCLUDE | ||||
| 	INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P | ||||
| 	INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER | ||||
| 	INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION | ||||
| @@ -3686,17 +3687,18 @@ ConstraintElem: | ||||
| 					n->initially_valid = !n->skip_validation; | ||||
| 					$$ = (Node *)n; | ||||
| 				} | ||||
| 			| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace | ||||
| 			| UNIQUE '(' columnList ')' opt_c_include opt_definition OptConsTableSpace | ||||
| 				ConstraintAttributeSpec | ||||
| 				{ | ||||
| 					Constraint *n = makeNode(Constraint); | ||||
| 					n->contype = CONSTR_UNIQUE; | ||||
| 					n->location = @1; | ||||
| 					n->keys = $3; | ||||
| 					n->options = $5; | ||||
| 					n->including = $5; | ||||
| 					n->options = $6; | ||||
| 					n->indexname = NULL; | ||||
| 					n->indexspace = $6; | ||||
| 					processCASbits($7, @7, "UNIQUE", | ||||
| 					n->indexspace = $7; | ||||
| 					processCASbits($8, @8, "UNIQUE", | ||||
| 								   &n->deferrable, &n->initdeferred, NULL, | ||||
| 								   NULL, yyscanner); | ||||
| 					$$ = (Node *)n; | ||||
| @@ -3707,6 +3709,7 @@ ConstraintElem: | ||||
| 					n->contype = CONSTR_UNIQUE; | ||||
| 					n->location = @1; | ||||
| 					n->keys = NIL; | ||||
| 					n->including = NIL; | ||||
| 					n->options = NIL; | ||||
| 					n->indexname = $2; | ||||
| 					n->indexspace = NULL; | ||||
| @@ -3715,17 +3718,18 @@ ConstraintElem: | ||||
| 								   NULL, yyscanner); | ||||
| 					$$ = (Node *)n; | ||||
| 				} | ||||
| 			| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace | ||||
| 			| PRIMARY KEY '(' columnList ')' opt_c_include opt_definition OptConsTableSpace | ||||
| 				ConstraintAttributeSpec | ||||
| 				{ | ||||
| 					Constraint *n = makeNode(Constraint); | ||||
| 					n->contype = CONSTR_PRIMARY; | ||||
| 					n->location = @1; | ||||
| 					n->keys = $4; | ||||
| 					n->options = $6; | ||||
| 					n->including = $6; | ||||
| 					n->options = $7; | ||||
| 					n->indexname = NULL; | ||||
| 					n->indexspace = $7; | ||||
| 					processCASbits($8, @8, "PRIMARY KEY", | ||||
| 					n->indexspace = $8; | ||||
| 					processCASbits($9, @9, "PRIMARY KEY", | ||||
| 								   &n->deferrable, &n->initdeferred, NULL, | ||||
| 								   NULL, yyscanner); | ||||
| 					$$ = (Node *)n; | ||||
| @@ -3736,6 +3740,7 @@ ConstraintElem: | ||||
| 					n->contype = CONSTR_PRIMARY; | ||||
| 					n->location = @1; | ||||
| 					n->keys = NIL; | ||||
| 					n->including = NIL; | ||||
| 					n->options = NIL; | ||||
| 					n->indexname = $3; | ||||
| 					n->indexspace = NULL; | ||||
| @@ -3745,7 +3750,7 @@ ConstraintElem: | ||||
| 					$$ = (Node *)n; | ||||
| 				} | ||||
| 			| EXCLUDE access_method_clause '(' ExclusionConstraintList ')' | ||||
| 				opt_definition OptConsTableSpace ExclusionWhereClause | ||||
| 				opt_c_include opt_definition OptConsTableSpace  ExclusionWhereClause | ||||
| 				ConstraintAttributeSpec | ||||
| 				{ | ||||
| 					Constraint *n = makeNode(Constraint); | ||||
| @@ -3753,11 +3758,12 @@ ConstraintElem: | ||||
| 					n->location = @1; | ||||
| 					n->access_method	= $2; | ||||
| 					n->exclusions		= $4; | ||||
| 					n->options			= $6; | ||||
| 					n->including		= $6; | ||||
| 					n->options			= $7; | ||||
| 					n->indexname		= NULL; | ||||
| 					n->indexspace		= $7; | ||||
| 					n->where_clause		= $8; | ||||
| 					processCASbits($9, @9, "EXCLUDE", | ||||
| 					n->indexspace		= $8; | ||||
| 					n->where_clause		= $9; | ||||
| 					processCASbits($10, @10, "EXCLUDE", | ||||
| 								   &n->deferrable, &n->initdeferred, NULL, | ||||
| 								   NULL, yyscanner); | ||||
| 					$$ = (Node *)n; | ||||
| @@ -3803,6 +3809,10 @@ columnElem: ColId | ||||
| 				} | ||||
| 		; | ||||
|  | ||||
| opt_c_include:	INCLUDE '(' columnList ')'			{ $$ = $3; } | ||||
| 			 |		/* EMPTY */						{ $$ = NIL; } | ||||
| 		; | ||||
|  | ||||
| key_match:  MATCH FULL | ||||
| 			{ | ||||
| 				$$ = FKCONSTR_MATCH_FULL; | ||||
| @@ -7373,7 +7383,7 @@ defacl_privilege_target: | ||||
|  | ||||
| IndexStmt:	CREATE opt_unique INDEX opt_concurrently opt_index_name | ||||
| 			ON relation_expr access_method_clause '(' index_params ')' | ||||
| 			opt_reloptions OptTableSpace where_clause | ||||
| 			opt_include opt_reloptions OptTableSpace where_clause | ||||
| 				{ | ||||
| 					IndexStmt *n = makeNode(IndexStmt); | ||||
| 					n->unique = $2; | ||||
| @@ -7383,9 +7393,10 @@ IndexStmt:	CREATE opt_unique INDEX opt_concurrently opt_index_name | ||||
| 					n->relationId = InvalidOid; | ||||
| 					n->accessMethod = $8; | ||||
| 					n->indexParams = $10; | ||||
| 					n->options = $12; | ||||
| 					n->tableSpace = $13; | ||||
| 					n->whereClause = $14; | ||||
| 					n->indexIncludingParams = $12; | ||||
| 					n->options = $13; | ||||
| 					n->tableSpace = $14; | ||||
| 					n->whereClause = $15; | ||||
| 					n->excludeOpNames = NIL; | ||||
| 					n->idxcomment = NULL; | ||||
| 					n->indexOid = InvalidOid; | ||||
| @@ -7400,7 +7411,7 @@ IndexStmt:	CREATE opt_unique INDEX opt_concurrently opt_index_name | ||||
| 				} | ||||
| 			| CREATE opt_unique INDEX opt_concurrently IF_P NOT EXISTS index_name | ||||
| 			ON relation_expr access_method_clause '(' index_params ')' | ||||
| 			opt_reloptions OptTableSpace where_clause | ||||
| 			opt_include opt_reloptions OptTableSpace where_clause | ||||
| 				{ | ||||
| 					IndexStmt *n = makeNode(IndexStmt); | ||||
| 					n->unique = $2; | ||||
| @@ -7410,9 +7421,10 @@ IndexStmt:	CREATE opt_unique INDEX opt_concurrently opt_index_name | ||||
| 					n->relationId = InvalidOid; | ||||
| 					n->accessMethod = $11; | ||||
| 					n->indexParams = $13; | ||||
| 					n->options = $15; | ||||
| 					n->tableSpace = $16; | ||||
| 					n->whereClause = $17; | ||||
| 					n->indexIncludingParams = $15; | ||||
| 					n->options = $16; | ||||
| 					n->tableSpace = $17; | ||||
| 					n->whereClause = $18; | ||||
| 					n->excludeOpNames = NIL; | ||||
| 					n->idxcomment = NULL; | ||||
| 					n->indexOid = InvalidOid; | ||||
| @@ -7491,6 +7503,14 @@ index_elem:	ColId opt_collate opt_class opt_asc_desc opt_nulls_order | ||||
| 				} | ||||
| 		; | ||||
|  | ||||
| opt_include:		INCLUDE '(' index_including_params ')'			{ $$ = $3; } | ||||
| 			 |		/* EMPTY */						{ $$ = NIL; } | ||||
| 		; | ||||
|  | ||||
| index_including_params:	index_elem						{ $$ = list_make1($1); } | ||||
| 			| index_including_params ',' index_elem		{ $$ = lappend($1, $3); } | ||||
| 		; | ||||
|  | ||||
| opt_collate: COLLATE any_name						{ $$ = $2; } | ||||
| 			| /*EMPTY*/								{ $$ = NIL; } | ||||
| 		; | ||||
| @@ -15206,6 +15226,7 @@ unreserved_keyword: | ||||
| 			| IMMUTABLE | ||||
| 			| IMPLICIT_P | ||||
| 			| IMPORT_P | ||||
| 			| INCLUDE | ||||
| 			| INCLUDING | ||||
| 			| INCREMENT | ||||
| 			| INDEX | ||||
|   | ||||
| @@ -3089,7 +3089,7 @@ attnameAttNum(Relation rd, const char *attname, bool sysColOK) | ||||
| { | ||||
| 	int			i; | ||||
|  | ||||
| 	for (i = 0; i < rd->rd_rel->relnatts; i++) | ||||
| 	for (i = 0; i < RelationGetNumberOfAttributes(rd); i++) | ||||
| 	{ | ||||
| 		Form_pg_attribute att = TupleDescAttr(rd->rd_att, i); | ||||
|  | ||||
|   | ||||
| @@ -978,7 +978,8 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) | ||||
| 		/* | ||||
| 		 * Generate default column list for INSERT. | ||||
| 		 */ | ||||
| 		int			numcol = pstate->p_target_relation->rd_rel->relnatts; | ||||
| 		int			numcol = RelationGetNumberOfAttributes(pstate->p_target_relation); | ||||
|  | ||||
| 		int			i; | ||||
|  | ||||
| 		for (i = 0; i < numcol; i++) | ||||
|   | ||||
| @@ -1468,9 +1468,10 @@ generateClonedIndexStmt(RangeVar *heapRel, Oid heapRelid, Relation source_idx, | ||||
|  | ||||
| 	/* Build the list of IndexElem */ | ||||
| 	index->indexParams = NIL; | ||||
| 	index->indexIncludingParams = NIL; | ||||
|  | ||||
| 	indexpr_item = list_head(indexprs); | ||||
| 	for (keyno = 0; keyno < idxrec->indnatts; keyno++) | ||||
| 	for (keyno = 0; keyno < idxrec->indnkeyatts; keyno++) | ||||
| 	{ | ||||
| 		IndexElem  *iparam; | ||||
| 		AttrNumber	attnum = idxrec->indkey.values[keyno]; | ||||
| @@ -1559,6 +1560,40 @@ generateClonedIndexStmt(RangeVar *heapRel, Oid heapRelid, Relation source_idx, | ||||
| 		index->indexParams = lappend(index->indexParams, iparam); | ||||
| 	} | ||||
|  | ||||
| 	/* Handle included columns separately */ | ||||
| 	for (keyno = idxrec->indnkeyatts; keyno < idxrec->indnatts; keyno++) | ||||
| 	{ | ||||
| 		IndexElem  *iparam; | ||||
| 		AttrNumber	attnum = idxrec->indkey.values[keyno]; | ||||
| 		Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(source_idx), | ||||
| 											   keyno); | ||||
|  | ||||
| 		iparam = makeNode(IndexElem); | ||||
|  | ||||
| 		if (AttributeNumberIsValid(attnum)) | ||||
| 		{ | ||||
| 			/* Simple index column */ | ||||
| 			char	   *attname; | ||||
|  | ||||
| 			attname = get_attname(indrelid, attnum, false); | ||||
| 			keycoltype = get_atttype(indrelid, attnum); | ||||
|  | ||||
| 			iparam->name = attname; | ||||
| 			iparam->expr = NULL; | ||||
| 		} | ||||
| 		else | ||||
| 			ereport(ERROR, | ||||
| 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 					 errmsg("expressions are not supported in included columns"))); | ||||
|  | ||||
| 		/* Copy the original index column name */ | ||||
| 		iparam->indexcolname = pstrdup(NameStr(attr->attname)); | ||||
|  | ||||
| 		/* Add the collation name, if non-default */ | ||||
| 		iparam->collation = get_collation(indcollation->values[keyno], keycoltype); | ||||
|  | ||||
| 		index->indexIncludingParams = lappend(index->indexIncludingParams, iparam); | ||||
| 	} | ||||
| 	/* Copy reloptions if any */ | ||||
| 	datum = SysCacheGetAttr(RELOID, ht_idxrel, | ||||
| 							Anum_pg_class_reloptions, &isnull); | ||||
| @@ -1829,6 +1864,7 @@ transformIndexConstraints(CreateStmtContext *cxt) | ||||
| 			IndexStmt  *priorindex = lfirst(k); | ||||
|  | ||||
| 			if (equal(index->indexParams, priorindex->indexParams) && | ||||
| 				equal(index->indexIncludingParams, priorindex->indexIncludingParams) && | ||||
| 				equal(index->whereClause, priorindex->whereClause) && | ||||
| 				equal(index->excludeOpNames, priorindex->excludeOpNames) && | ||||
| 				strcmp(index->accessMethod, priorindex->accessMethod) == 0 && | ||||
| @@ -1900,6 +1936,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 	index->tableSpace = constraint->indexspace; | ||||
| 	index->whereClause = constraint->where_clause; | ||||
| 	index->indexParams = NIL; | ||||
| 	index->indexIncludingParams = NIL; | ||||
| 	index->excludeOpNames = NIL; | ||||
| 	index->idxcomment = NULL; | ||||
| 	index->indexOid = InvalidOid; | ||||
| @@ -2049,24 +2086,29 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 													heap_rel->rd_rel->relhasoids); | ||||
| 			attname = pstrdup(NameStr(attform->attname)); | ||||
|  | ||||
| 			/* | ||||
| 			 * Insist on default opclass and sort options.  While the index | ||||
| 			 * would still work as a constraint with non-default settings, it | ||||
| 			 * might not provide exactly the same uniqueness semantics as | ||||
| 			 * you'd get from a normally-created constraint; and there's also | ||||
| 			 * the dump/reload problem mentioned above. | ||||
| 			 */ | ||||
| 			defopclass = GetDefaultOpClass(attform->atttypid, | ||||
| 										   index_rel->rd_rel->relam); | ||||
| 			if (indclass->values[i] != defopclass || | ||||
| 				index_rel->rd_indoption[i] != 0) | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_WRONG_OBJECT_TYPE), | ||||
| 						 errmsg("index \"%s\" does not have default sorting behavior", index_name), | ||||
| 						 errdetail("Cannot create a primary key or unique constraint using such an index."), | ||||
| 						 parser_errposition(cxt->pstate, constraint->location))); | ||||
| 			if (i < index_form->indnkeyatts) | ||||
| 			{ | ||||
| 				/* | ||||
| 				 * Insist on default opclass and sort options.  While the | ||||
| 				 * index would still work as a constraint with non-default | ||||
| 				 * settings, it might not provide exactly the same uniqueness | ||||
| 				 * semantics as you'd get from a normally-created constraint; | ||||
| 				 * and there's also the dump/reload problem mentioned above. | ||||
| 				 */ | ||||
| 				defopclass = GetDefaultOpClass(attform->atttypid, | ||||
| 											   index_rel->rd_rel->relam); | ||||
| 				if (indclass->values[i] != defopclass || | ||||
| 					index_rel->rd_indoption[i] != 0) | ||||
| 					ereport(ERROR, | ||||
| 							(errcode(ERRCODE_WRONG_OBJECT_TYPE), | ||||
| 							 errmsg("index \"%s\" does not have default sorting behavior", index_name), | ||||
| 							 errdetail("Cannot create a primary key or unique constraint using such an index."), | ||||
| 							 parser_errposition(cxt->pstate, constraint->location))); | ||||
|  | ||||
| 			constraint->keys = lappend(constraint->keys, makeString(attname)); | ||||
| 				constraint->keys = lappend(constraint->keys, makeString(attname)); | ||||
| 			} | ||||
| 			else | ||||
| 				constraint->including = lappend(constraint->including, makeString(attname)); | ||||
| 		} | ||||
|  | ||||
| 		/* Close the index relation but keep the lock */ | ||||
| @@ -2095,8 +2137,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 			index->indexParams = lappend(index->indexParams, elem); | ||||
| 			index->excludeOpNames = lappend(index->excludeOpNames, opname); | ||||
| 		} | ||||
|  | ||||
| 		return index; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| @@ -2107,7 +2147,136 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 	 * it to DefineIndex to mark the columns NOT NULL, it's more efficient to | ||||
| 	 * get it right the first time.) | ||||
| 	 */ | ||||
| 	foreach(lc, constraint->keys) | ||||
| 	else | ||||
| 	{ | ||||
| 		foreach(lc, constraint->keys) | ||||
| 		{ | ||||
| 			char	   *key = strVal(lfirst(lc)); | ||||
| 			bool		found = false; | ||||
| 			ColumnDef  *column = NULL; | ||||
| 			ListCell   *columns; | ||||
| 			IndexElem  *iparam; | ||||
|  | ||||
| 			/* Make sure referenced column exist. */ | ||||
| 			foreach(columns, cxt->columns) | ||||
| 			{ | ||||
| 				column = castNode(ColumnDef, lfirst(columns)); | ||||
| 				if (strcmp(column->colname, key) == 0) | ||||
| 				{ | ||||
| 					found = true; | ||||
| 					break; | ||||
| 				} | ||||
| 			} | ||||
| 			if (found) | ||||
| 			{ | ||||
| 				/* found column in the new table; force it to be NOT NULL */ | ||||
| 				if (constraint->contype == CONSTR_PRIMARY) | ||||
| 					column->is_not_null = true; | ||||
| 			} | ||||
| 			else if (SystemAttributeByName(key, cxt->hasoids) != NULL) | ||||
| 			{ | ||||
| 				/* | ||||
| 				 * column will be a system column in the new table, so accept | ||||
| 				 * it. System columns can't ever be null, so no need to worry | ||||
| 				 * about PRIMARY/NOT NULL constraint. | ||||
| 				 */ | ||||
| 				found = true; | ||||
| 			} | ||||
| 			else if (cxt->inhRelations) | ||||
| 			{ | ||||
| 				/* try inherited tables */ | ||||
| 				ListCell   *inher; | ||||
|  | ||||
| 				foreach(inher, cxt->inhRelations) | ||||
| 				{ | ||||
| 					RangeVar   *inh = castNode(RangeVar, lfirst(inher)); | ||||
| 					Relation	rel; | ||||
| 					int			count; | ||||
|  | ||||
| 					rel = heap_openrv(inh, AccessShareLock); | ||||
| 					/* check user requested inheritance from valid relkind */ | ||||
| 					if (rel->rd_rel->relkind != RELKIND_RELATION && | ||||
| 						rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && | ||||
| 						rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_WRONG_OBJECT_TYPE), | ||||
| 								 errmsg("inherited relation \"%s\" is not a table or foreign table", | ||||
| 										inh->relname))); | ||||
| 					for (count = 0; count < rel->rd_att->natts; count++) | ||||
| 					{ | ||||
| 						Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, | ||||
| 																  count); | ||||
| 						char	   *inhname = NameStr(inhattr->attname); | ||||
|  | ||||
| 						if (inhattr->attisdropped) | ||||
| 							continue; | ||||
| 						if (strcmp(key, inhname) == 0) | ||||
| 						{ | ||||
| 							found = true; | ||||
|  | ||||
| 							/* | ||||
| 							 * We currently have no easy way to force an | ||||
| 							 * inherited column to be NOT NULL at creation, if | ||||
| 							 * its parent wasn't so already. We leave it to | ||||
| 							 * DefineIndex to fix things up in this case. | ||||
| 							 */ | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					heap_close(rel, NoLock); | ||||
| 					if (found) | ||||
| 						break; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			/* | ||||
| 			 * In the ALTER TABLE case, don't complain about index keys not | ||||
| 			 * created in the command; they may well exist already. | ||||
| 			 * DefineIndex will complain about them if not, and will also take | ||||
| 			 * care of marking them NOT NULL. | ||||
| 			 */ | ||||
| 			if (!found && !cxt->isalter) | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_UNDEFINED_COLUMN), | ||||
| 						 errmsg("column \"%s\" named in key does not exist", key), | ||||
| 						 parser_errposition(cxt->pstate, constraint->location))); | ||||
|  | ||||
| 			/* Check for PRIMARY KEY(foo, foo) */ | ||||
| 			foreach(columns, index->indexParams) | ||||
| 			{ | ||||
| 				iparam = (IndexElem *) lfirst(columns); | ||||
| 				if (iparam->name && strcmp(key, iparam->name) == 0) | ||||
| 				{ | ||||
| 					if (index->primary) | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_DUPLICATE_COLUMN), | ||||
| 								 errmsg("column \"%s\" appears twice in primary key constraint", | ||||
| 										key), | ||||
| 								 parser_errposition(cxt->pstate, constraint->location))); | ||||
| 					else | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_DUPLICATE_COLUMN), | ||||
| 								 errmsg("column \"%s\" appears twice in unique constraint", | ||||
| 										key), | ||||
| 								 parser_errposition(cxt->pstate, constraint->location))); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			/* OK, add it to the index definition */ | ||||
| 			iparam = makeNode(IndexElem); | ||||
| 			iparam->name = pstrdup(key); | ||||
| 			iparam->expr = NULL; | ||||
| 			iparam->indexcolname = NULL; | ||||
| 			iparam->collation = NIL; | ||||
| 			iparam->opclass = NIL; | ||||
| 			iparam->ordering = SORTBY_DEFAULT; | ||||
| 			iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; | ||||
| 			index->indexParams = lappend(index->indexParams, iparam); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Add included columns to index definition */ | ||||
| 	foreach(lc, constraint->including) | ||||
| 	{ | ||||
| 		char	   *key = strVal(lfirst(lc)); | ||||
| 		bool		found = false; | ||||
| @@ -2124,65 +2293,63 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if (found) | ||||
| 		{ | ||||
| 			/* found column in the new table; force it to be NOT NULL */ | ||||
| 			if (constraint->contype == CONSTR_PRIMARY) | ||||
| 				column->is_not_null = true; | ||||
| 		} | ||||
| 		else if (SystemAttributeByName(key, cxt->hasoids) != NULL) | ||||
| 		{ | ||||
| 			/* | ||||
| 			 * column will be a system column in the new table, so accept it. | ||||
| 			 * System columns can't ever be null, so no need to worry about | ||||
| 			 * PRIMARY/NOT NULL constraint. | ||||
| 			 */ | ||||
| 			found = true; | ||||
| 		} | ||||
| 		else if (cxt->inhRelations) | ||||
| 		{ | ||||
| 			/* try inherited tables */ | ||||
| 			ListCell   *inher; | ||||
|  | ||||
| 			foreach(inher, cxt->inhRelations) | ||||
| 		if (!found) | ||||
| 		{ | ||||
| 			if (SystemAttributeByName(key, cxt->hasoids) != NULL) | ||||
| 			{ | ||||
| 				RangeVar   *inh = lfirst_node(RangeVar, inher); | ||||
| 				Relation	rel; | ||||
| 				int			count; | ||||
| 				/* | ||||
| 				 * column will be a system column in the new table, so accept | ||||
| 				 * it. System columns can't ever be null, so no need to worry | ||||
| 				 * about PRIMARY/NOT NULL constraint. | ||||
| 				 */ | ||||
| 				found = true; | ||||
| 			} | ||||
| 			else if (cxt->inhRelations) | ||||
| 			{ | ||||
| 				/* try inherited tables */ | ||||
| 				ListCell   *inher; | ||||
|  | ||||
| 				rel = heap_openrv(inh, AccessShareLock); | ||||
| 				/* check user requested inheritance from valid relkind */ | ||||
| 				if (rel->rd_rel->relkind != RELKIND_RELATION && | ||||
| 					rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && | ||||
| 					rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) | ||||
| 					ereport(ERROR, | ||||
| 							(errcode(ERRCODE_WRONG_OBJECT_TYPE), | ||||
| 							 errmsg("inherited relation \"%s\" is not a table or foreign table", | ||||
| 									inh->relname))); | ||||
| 				for (count = 0; count < rel->rd_att->natts; count++) | ||||
| 				foreach(inher, cxt->inhRelations) | ||||
| 				{ | ||||
| 					Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, | ||||
| 															  count); | ||||
| 					char	   *inhname = NameStr(inhattr->attname); | ||||
| 					RangeVar   *inh = lfirst_node(RangeVar, inher); | ||||
| 					Relation	rel; | ||||
| 					int			count; | ||||
|  | ||||
| 					if (inhattr->attisdropped) | ||||
| 						continue; | ||||
| 					if (strcmp(key, inhname) == 0) | ||||
| 					rel = heap_openrv(inh, AccessShareLock); | ||||
| 					/* check user requested inheritance from valid relkind */ | ||||
| 					if (rel->rd_rel->relkind != RELKIND_RELATION && | ||||
| 						rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && | ||||
| 						rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) | ||||
| 						ereport(ERROR, | ||||
| 								(errcode(ERRCODE_WRONG_OBJECT_TYPE), | ||||
| 								 errmsg("inherited relation \"%s\" is not a table or foreign table", | ||||
| 										inh->relname))); | ||||
| 					for (count = 0; count < rel->rd_att->natts; count++) | ||||
| 					{ | ||||
| 						found = true; | ||||
| 						Form_pg_attribute inhattr = TupleDescAttr(rel->rd_att, | ||||
| 																  count); | ||||
| 						char	   *inhname = NameStr(inhattr->attname); | ||||
|  | ||||
| 						/* | ||||
| 						 * We currently have no easy way to force an inherited | ||||
| 						 * column to be NOT NULL at creation, if its parent | ||||
| 						 * wasn't so already. We leave it to DefineIndex to | ||||
| 						 * fix things up in this case. | ||||
| 						 */ | ||||
| 						break; | ||||
| 						if (inhattr->attisdropped) | ||||
| 							continue; | ||||
| 						if (strcmp(key, inhname) == 0) | ||||
| 						{ | ||||
| 							found = true; | ||||
|  | ||||
| 							/* | ||||
| 							 * We currently have no easy way to force an | ||||
| 							 * inherited column to be NOT NULL at creation, if | ||||
| 							 * its parent wasn't so already. We leave it to | ||||
| 							 * DefineIndex to fix things up in this case. | ||||
| 							 */ | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					heap_close(rel, NoLock); | ||||
| 					if (found) | ||||
| 						break; | ||||
| 				} | ||||
| 				heap_close(rel, NoLock); | ||||
| 				if (found) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -2198,27 +2365,6 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 					 errmsg("column \"%s\" named in key does not exist", key), | ||||
| 					 parser_errposition(cxt->pstate, constraint->location))); | ||||
|  | ||||
| 		/* Check for PRIMARY KEY(foo, foo) */ | ||||
| 		foreach(columns, index->indexParams) | ||||
| 		{ | ||||
| 			iparam = (IndexElem *) lfirst(columns); | ||||
| 			if (iparam->name && strcmp(key, iparam->name) == 0) | ||||
| 			{ | ||||
| 				if (index->primary) | ||||
| 					ereport(ERROR, | ||||
| 							(errcode(ERRCODE_DUPLICATE_COLUMN), | ||||
| 							 errmsg("column \"%s\" appears twice in primary key constraint", | ||||
| 									key), | ||||
| 							 parser_errposition(cxt->pstate, constraint->location))); | ||||
| 				else | ||||
| 					ereport(ERROR, | ||||
| 							(errcode(ERRCODE_DUPLICATE_COLUMN), | ||||
| 							 errmsg("column \"%s\" appears twice in unique constraint", | ||||
| 									key), | ||||
| 							 parser_errposition(cxt->pstate, constraint->location))); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		/* OK, add it to the index definition */ | ||||
| 		iparam = makeNode(IndexElem); | ||||
| 		iparam->name = pstrdup(key); | ||||
| @@ -2226,9 +2372,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) | ||||
| 		iparam->indexcolname = NULL; | ||||
| 		iparam->collation = NIL; | ||||
| 		iparam->opclass = NIL; | ||||
| 		iparam->ordering = SORTBY_DEFAULT; | ||||
| 		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; | ||||
| 		index->indexParams = lappend(index->indexParams, iparam); | ||||
| 		index->indexIncludingParams = lappend(index->indexIncludingParams, iparam); | ||||
| 	} | ||||
|  | ||||
| 	return index; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user