expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
{
	List	   *result = NIL;
	List	   *atsubcmds = NIL;
	AttrNumber	parent_attno;
	Relation	relation;
	Relation	childrel;
	TupleDesc	tupleDesc;
	TupleConstr *constr;
	AttrNumber *attmap;
	char	   *comment;

	/*
	 * Open the relation referenced by the LIKE clause.  We should still have
	 * the table lock obtained by transformTableLikeClause (and this'll throw
	 * an assertion failure if not).  Hence, no need to recheck privileges
	 * etc.  We must open the rel by OID not name, to be sure we get the same
	 * table.
	 */
	if (!OidIsValid(table_like_clause->relationOid))
		elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");

	relation = relation_open(table_like_clause->relationOid, NoLock);

	tupleDesc = RelationGetDescr(relation);
	constr = tupleDesc->constr;

	/*
	 * Open the newly-created child relation; we have lock on that too.
	 */
	childrel = relation_openrv(heapRel, NoLock);

	/*
	 * Construct a map from the LIKE relation's attnos to the child rel's.
	 * This re-checks type match etc, although it shouldn't be possible to
	 * have a failure since both tables are locked.
	 */
	attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
										tupleDesc,
										gettext_noop("could not convert row type"));

	/*
	 * Process defaults, if required.
	 */
	if ((table_like_clause->options &
		 (CREATE_TABLE_LIKE_DEFAULTS | CREATE_TABLE_LIKE_GENERATED)) &&
		constr != NULL)
	{
		AttrDefault *attrdef = constr->defval;

		for (parent_attno = 1; parent_attno <= tupleDesc->natts;
			 parent_attno++)
		{
			Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
														parent_attno - 1);

			/*
			 * Ignore dropped columns in the parent.
			 */
			if (attribute->attisdropped)
				continue;

			/*
			 * Copy default, if present and it should be copied.  We have
			 * separate options for plain default expressions and GENERATED
			 * defaults.
			 */
			if (attribute->atthasdef &&
				(attribute->attgenerated ?
				 (table_like_clause->options & CREATE_TABLE_LIKE_GENERATED) :
				 (table_like_clause->options & CREATE_TABLE_LIKE_DEFAULTS)))
			{
				Node	   *this_default = NULL;
				AlterTableCmd *atsubcmd;
				bool		found_whole_row;

				/* Find default in constraint structure */
				for (int i = 0; i < constr->num_defval; i++)
				{
					if (attrdef[i].adnum == parent_attno)
					{
						this_default = stringToNode(attrdef[i].adbin);
						break;
					}
				}
				Assert(this_default != NULL);

				atsubcmd = makeNode(AlterTableCmd);
				atsubcmd->subtype = AT_CookedColumnDefault;
				atsubcmd->num = attmap[parent_attno - 1];
				atsubcmd->def = map_variable_attnos(this_default,
													1, 0,
													attmap, tupleDesc->natts,
													InvalidOid,
													&found_whole_row);

				/*
				 * Prevent this for the same reason as for constraints below.
				 * Note that defaults cannot contain any vars, so it's OK that
				 * the error message refers to generated columns.
				 */
				if (found_whole_row)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot convert whole-row table reference"),
							 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
									   NameStr(attribute->attname),
									   RelationGetRelationName(relation))));

				atsubcmds = lappend(atsubcmds, atsubcmd);
			}
		}
	}

	/*
	 * Copy CHECK constraints if requested, being careful to adjust attribute
	 * numbers so they match the child.
	 */
	if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
		constr != NULL)
	{
		int			ccnum;

		for (ccnum = 0; ccnum < constr->num_check; ccnum++)
		{
			char	   *ccname = constr->check[ccnum].ccname;
			char	   *ccbin = constr->check[ccnum].ccbin;
			Node	   *ccbin_node;
			bool		found_whole_row;
			Constraint *n;
			AlterTableCmd *atsubcmd;

			ccbin_node = map_variable_attnos(stringToNode(ccbin),
											 1, 0,
											 attmap, tupleDesc->natts,
											 InvalidOid, &found_whole_row);

			/*
			 * We reject whole-row variables because the whole point of LIKE
			 * is that the new table's rowtype might later diverge from the
			 * parent's.  So, while translation might be possible right now,
			 * it wouldn't be possible to guarantee it would work in future.
			 */
			if (found_whole_row)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot convert whole-row table reference"),
						 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
								   ccname,
								   RelationGetRelationName(relation))));

			n = makeNode(Constraint);
			n->contype = CONSTR_CHECK;
			n->location = -1;
			n->conname = pstrdup(ccname);
			n->raw_expr = NULL;
			n->cooked_expr = nodeToString(ccbin_node);

			/* We can skip validation, since the new table should be empty. */
			n->skip_validation = true;
			n->initially_valid = true;

			atsubcmd = makeNode(AlterTableCmd);
			atsubcmd->subtype = AT_AddConstraint;
			atsubcmd->def = (Node *) n;
			atsubcmds = lappend(atsubcmds, atsubcmd);

			/* Copy comment on constraint */
			if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
				(comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation),
																  n->conname, false),
									  ConstraintRelationId,
									  0)) != NULL)
			{
				CommentStmt *stmt = makeNode(CommentStmt);

				stmt->objtype = OBJECT_TABCONSTRAINT;
				stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
												   makeString(heapRel->relname),
												   makeString(n->conname));
				stmt->comment = comment;

				result = lappend(result, stmt);
			}
		}
	}

	/*
	 * If we generated any ALTER TABLE actions above, wrap them into a single
	 * ALTER TABLE command.  Stick it at the front of the result, so it runs
	 * before any CommentStmts we made above.
	 */
	if (atsubcmds)
	{
		AlterTableStmt *atcmd = makeNode(AlterTableStmt);

		atcmd->relation = copyObject(heapRel);
		atcmd->cmds = atsubcmds;
		atcmd->relkind = OBJECT_TABLE;
		atcmd->missing_ok = false;
		result = lcons(atcmd, result);
	}

	/*
	 * Process indexes if required.
	 */
	if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
		relation->rd_rel->relhasindex)
	{
		List	   *parent_indexes;
		ListCell   *l;

		parent_indexes = RelationGetIndexList(relation);

		foreach(l, parent_indexes)
		{
			Oid			parent_index_oid = lfirst_oid(l);
			Relation	parent_index;
			IndexStmt  *index_stmt;

			parent_index = index_open(parent_index_oid, AccessShareLock);

			/* Build CREATE INDEX statement to recreate the parent_index */
			index_stmt = generateClonedIndexStmt(heapRel,
												 parent_index,
												 attmap, tupleDesc->natts,
												 NULL);

			/* Copy comment on index, if requested */
			if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
			{
				comment = GetComment(parent_index_oid, RelationRelationId, 0);

				/*
				 * We make use of IndexStmt's idxcomment option, so as not to
				 * need to know now what name the index will have.
				 */
				index_stmt->idxcomment = comment;
			}

			result = lappend(result, index_stmt);

			index_close(parent_index, AccessShareLock);
		}
	}

	/* Done with child rel */
	table_close(childrel, NoLock);

	/*
	 * Close the parent rel, but keep our AccessShareLock on it until xact
	 * commit.  That will prevent someone else from deleting or ALTERing the
	 * parent before the child is committed.
	 */
	table_close(relation, NoLock);

	return result;
}