@@ -85,7 +85,6 @@ typedef struct
85
85
List * ckconstraints ; /* CHECK constraints */
86
86
List * fkconstraints ; /* FOREIGN KEY constraints */
87
87
List * ixconstraints ; /* index-creating constraints */
88
- List * inh_indexes ; /* cloned indexes from INCLUDING INDEXES */
89
88
List * extstats ; /* cloned extended statistics */
90
89
List * blist ; /* "before list" of things to do before
91
90
* creating the table */
@@ -150,6 +149,9 @@ static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con,
150
149
* Returns a List of utility commands to be done in sequence. One of these
151
150
* will be the transformed CreateStmt, but there may be additional actions
152
151
* to be done before and after the actual DefineRelation() call.
152
+ * In addition to normal utility commands such as AlterTableStmt and
153
+ * IndexStmt, the result list may contain TableLikeClause(s), representing
154
+ * the need to perform additional parse analysis after DefineRelation().
153
155
*
154
156
* SQL allows constraints to be scattered all over, so thumb through
155
157
* the columns and collect all constraints into one place.
@@ -238,7 +240,6 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
238
240
cxt .ckconstraints = NIL ;
239
241
cxt .fkconstraints = NIL ;
240
242
cxt .ixconstraints = NIL ;
241
- cxt .inh_indexes = NIL ;
242
243
cxt .extstats = NIL ;
243
244
cxt .blist = NIL ;
244
245
cxt .alist = NIL ;
@@ -888,8 +889,11 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
888
889
* transformTableLikeClause
889
890
*
890
891
* Change the LIKE <srctable> portion of a CREATE TABLE statement into
891
- * column definitions which recreate the user defined column portions of
892
- * <srctable>.
892
+ * column definitions that recreate the user defined column portions of
893
+ * <srctable>. Also, if there are any LIKE options that we can't fully
894
+ * process at this point, add the TableLikeClause to cxt->alist, which
895
+ * will cause utility.c to call expandTableLikeClause() after the new
896
+ * table has been created.
893
897
*/
894
898
static void
895
899
transformTableLikeClause (CreateStmtContext * cxt , TableLikeClause * table_like_clause )
@@ -898,7 +902,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
898
902
Relation relation ;
899
903
TupleDesc tupleDesc ;
900
904
TupleConstr * constr ;
901
- AttrNumber * attmap ;
902
905
AclResult aclresult ;
903
906
char * comment ;
904
907
ParseCallbackState pcbstate ;
@@ -912,6 +915,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
912
915
(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
913
916
errmsg ("LIKE is not supported for creating foreign tables" )));
914
917
918
+ /* Open the relation referenced by the LIKE clause */
915
919
relation = relation_openrv (table_like_clause -> relation , AccessShareLock );
916
920
917
921
if (relation -> rd_rel -> relkind != RELKIND_RELATION &&
@@ -950,15 +954,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
950
954
tupleDesc = RelationGetDescr (relation );
951
955
constr = tupleDesc -> constr ;
952
956
953
- /*
954
- * Initialize column number map for map_variable_attnos(). We need this
955
- * since dropped columns in the source table aren't copied, so the new
956
- * table can have different column numbers.
957
- */
958
- attmap = (AttrNumber * ) palloc0 (sizeof (AttrNumber ) * tupleDesc -> natts );
959
-
960
957
/*
961
958
* Insert the copied attributes into the cxt for the new table definition.
959
+ * We must do this now so that they appear in the table in the relative
960
+ * position where the LIKE clause is, as required by SQL99.
962
961
*/
963
962
for (parent_attno = 1 ; parent_attno <= tupleDesc -> natts ;
964
963
parent_attno ++ )
@@ -969,7 +968,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
969
968
ColumnDef * def ;
970
969
971
970
/*
972
- * Ignore dropped columns in the parent. attmap entry is left zero.
971
+ * Ignore dropped columns in the parent.
973
972
*/
974
973
if (attribute -> attisdropped )
975
974
continue ;
@@ -1001,8 +1000,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
1001
1000
*/
1002
1001
cxt -> columns = lappend (cxt -> columns , def );
1003
1002
1004
- attmap [parent_attno - 1 ] = list_length (cxt -> columns );
1005
-
1006
1003
/*
1007
1004
* Copy default, if present and the default has been requested
1008
1005
*/
@@ -1082,22 +1079,126 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
1082
1079
/* We use oids if at least one LIKE'ed table has oids. */
1083
1080
cxt -> hasoids |= relation -> rd_rel -> relhasoids ;
1084
1081
1082
+ /*
1083
+ * We cannot yet deal with CHECK constraints or indexes, since we don't
1084
+ * yet know what column numbers the copied columns will have in the
1085
+ * finished table. If any of those options are specified, add the LIKE
1086
+ * clause to cxt->alist so that expandTableLikeClause will be called after
1087
+ * we do know that.
1088
+ */
1089
+ if (table_like_clause -> options &
1090
+ (CREATE_TABLE_LIKE_CONSTRAINTS |
1091
+ CREATE_TABLE_LIKE_INDEXES ))
1092
+ cxt -> alist = lappend (cxt -> alist , table_like_clause );
1093
+
1094
+ /*
1095
+ * We may copy extended statistics if requested, since the representation
1096
+ * of CreateStatsStmt doesn't depend on column numbers.
1097
+ */
1098
+ if (table_like_clause -> options & CREATE_TABLE_LIKE_STATISTICS )
1099
+ {
1100
+ List * parent_extstats ;
1101
+ ListCell * l ;
1102
+
1103
+ parent_extstats = RelationGetStatExtList (relation );
1104
+
1105
+ foreach (l , parent_extstats )
1106
+ {
1107
+ Oid parent_stat_oid = lfirst_oid (l );
1108
+ CreateStatsStmt * stats_stmt ;
1109
+
1110
+ stats_stmt = generateClonedExtStatsStmt (cxt -> relation ,
1111
+ RelationGetRelid (relation ),
1112
+ parent_stat_oid );
1113
+
1114
+ /* Copy comment on statistics object, if requested */
1115
+ if (table_like_clause -> options & CREATE_TABLE_LIKE_COMMENTS )
1116
+ {
1117
+ comment = GetComment (parent_stat_oid , StatisticExtRelationId , 0 );
1118
+
1119
+ /*
1120
+ * We make use of CreateStatsStmt's stxcomment option, so as
1121
+ * not to need to know now what name the statistics will have.
1122
+ */
1123
+ stats_stmt -> stxcomment = comment ;
1124
+ }
1125
+
1126
+ cxt -> extstats = lappend (cxt -> extstats , stats_stmt );
1127
+ }
1128
+
1129
+ list_free (parent_extstats );
1130
+ }
1131
+
1132
+ /*
1133
+ * Close the parent rel, but keep our AccessShareLock on it until xact
1134
+ * commit. That will prevent someone else from deleting or ALTERing the
1135
+ * parent before we can run expandTableLikeClause.
1136
+ */
1137
+ heap_close (relation , NoLock );
1138
+ }
1139
+
1140
+ /*
1141
+ * expandTableLikeClause
1142
+ *
1143
+ * Process LIKE options that require knowing the final column numbers
1144
+ * assigned to the new table's columns. This executes after we have
1145
+ * run DefineRelation for the new table. It returns a list of utility
1146
+ * commands that should be run to generate indexes etc.
1147
+ */
1148
+ List *
1149
+ expandTableLikeClause (RangeVar * heapRel , TableLikeClause * table_like_clause )
1150
+ {
1151
+ List * result = NIL ;
1152
+ List * atsubcmds = NIL ;
1153
+ Relation relation ;
1154
+ Relation childrel ;
1155
+ TupleDesc tupleDesc ;
1156
+ TupleConstr * constr ;
1157
+ AttrNumber * attmap ;
1158
+ char * comment ;
1159
+
1160
+ /*
1161
+ * Open the relation referenced by the LIKE clause. We should still have
1162
+ * the table lock obtained by transformTableLikeClause (and this'll throw
1163
+ * an assertion failure if not). Hence, no need to recheck privileges
1164
+ * etc.
1165
+ */
1166
+ relation = relation_openrv (table_like_clause -> relation , NoLock );
1167
+
1168
+ tupleDesc = RelationGetDescr (relation );
1169
+ constr = tupleDesc -> constr ;
1170
+
1171
+ /*
1172
+ * Open the newly-created child relation; we have lock on that too.
1173
+ */
1174
+ childrel = relation_openrv (heapRel , NoLock );
1175
+
1176
+ /*
1177
+ * Construct a map from the LIKE relation's attnos to the child rel's.
1178
+ * This re-checks type match etc, although it shouldn't be possible to
1179
+ * have a failure since both tables are locked.
1180
+ */
1181
+ attmap = convert_tuples_by_name_map (RelationGetDescr (childrel ),
1182
+ tupleDesc ,
1183
+ gettext_noop ("could not convert row type" ));
1184
+
1085
1185
/*
1086
1186
* Copy CHECK constraints if requested, being careful to adjust attribute
1087
1187
* numbers so they match the child.
1088
1188
*/
1089
1189
if ((table_like_clause -> options & CREATE_TABLE_LIKE_CONSTRAINTS ) &&
1090
- tupleDesc -> constr )
1190
+ constr != NULL )
1091
1191
{
1092
1192
int ccnum ;
1093
1193
1094
- for (ccnum = 0 ; ccnum < tupleDesc -> constr -> num_check ; ccnum ++ )
1194
+ for (ccnum = 0 ; ccnum < constr -> num_check ; ccnum ++ )
1095
1195
{
1096
- char * ccname = tupleDesc -> constr -> check [ccnum ].ccname ;
1097
- char * ccbin = tupleDesc -> constr -> check [ccnum ].ccbin ;
1098
- Constraint * n = makeNode (Constraint );
1196
+ char * ccname = constr -> check [ccnum ].ccname ;
1197
+ char * ccbin = constr -> check [ccnum ].ccbin ;
1099
1198
Node * ccbin_node ;
1100
1199
bool found_whole_row ;
1200
+ Constraint * n ;
1201
+ AlterTableCmd * atsubcmd ;
1101
1202
1102
1203
ccbin_node = map_variable_attnos (stringToNode (ccbin ),
1103
1204
1 , 0 ,
@@ -1118,12 +1219,21 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
1118
1219
ccname ,
1119
1220
RelationGetRelationName (relation ))));
1120
1221
1222
+ n = makeNode (Constraint );
1121
1223
n -> contype = CONSTR_CHECK ;
1122
1224
n -> location = -1 ;
1123
1225
n -> conname = pstrdup (ccname );
1124
1226
n -> raw_expr = NULL ;
1125
1227
n -> cooked_expr = nodeToString (ccbin_node );
1126
- cxt -> ckconstraints = lappend (cxt -> ckconstraints , n );
1228
+
1229
+ /* We can skip validation, since the new table should be empty. */
1230
+ n -> skip_validation = true;
1231
+ n -> initially_valid = true;
1232
+
1233
+ atsubcmd = makeNode (AlterTableCmd );
1234
+ atsubcmd -> subtype = AT_AddConstraint ;
1235
+ atsubcmd -> def = (Node * ) n ;
1236
+ atsubcmds = lappend (atsubcmds , atsubcmd );
1127
1237
1128
1238
/* Copy comment on constraint */
1129
1239
if ((table_like_clause -> options & CREATE_TABLE_LIKE_COMMENTS ) &&
@@ -1135,18 +1245,34 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
1135
1245
CommentStmt * stmt = makeNode (CommentStmt );
1136
1246
1137
1247
stmt -> objtype = OBJECT_TABCONSTRAINT ;
1138
- stmt -> object = (Node * ) list_make3 (makeString (cxt -> relation -> schemaname ),
1139
- makeString (cxt -> relation -> relname ),
1248
+ stmt -> object = (Node * ) list_make3 (makeString (heapRel -> schemaname ),
1249
+ makeString (heapRel -> relname ),
1140
1250
makeString (n -> conname ));
1141
1251
stmt -> comment = comment ;
1142
1252
1143
- cxt -> alist = lappend (cxt -> alist , stmt );
1253
+ result = lappend (result , stmt );
1144
1254
}
1145
1255
}
1146
1256
}
1147
1257
1148
1258
/*
1149
- * Likewise, copy indexes if requested
1259
+ * If we generated any ALTER TABLE actions above, wrap them into a single
1260
+ * ALTER TABLE command. Stick it at the front of the result, so it runs
1261
+ * before any CommentStmts we made above.
1262
+ */
1263
+ if (atsubcmds )
1264
+ {
1265
+ AlterTableStmt * atcmd = makeNode (AlterTableStmt );
1266
+
1267
+ atcmd -> relation = copyObject (heapRel );
1268
+ atcmd -> cmds = atsubcmds ;
1269
+ atcmd -> relkind = OBJECT_TABLE ;
1270
+ atcmd -> missing_ok = false;
1271
+ result = lcons (atcmd , result );
1272
+ }
1273
+
1274
+ /*
1275
+ * Process indexes if required.
1150
1276
*/
1151
1277
if ((table_like_clause -> options & CREATE_TABLE_LIKE_INDEXES ) &&
1152
1278
relation -> rd_rel -> relhasindex )
@@ -1165,7 +1291,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
1165
1291
parent_index = index_open (parent_index_oid , AccessShareLock );
1166
1292
1167
1293
/* Build CREATE INDEX statement to recreate the parent_index */
1168
- index_stmt = generateClonedIndexStmt (cxt -> relation , InvalidOid ,
1294
+ index_stmt = generateClonedIndexStmt (heapRel , InvalidOid ,
1169
1295
parent_index ,
1170
1296
attmap , tupleDesc -> natts , NULL );
1171
1297
@@ -1181,56 +1307,23 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
1181
1307
index_stmt -> idxcomment = comment ;
1182
1308
}
1183
1309
1184
- /* Save it in the inh_indexes list for the time being */
1185
- cxt -> inh_indexes = lappend (cxt -> inh_indexes , index_stmt );
1310
+ result = lappend (result , index_stmt );
1186
1311
1187
1312
index_close (parent_index , AccessShareLock );
1188
1313
}
1189
1314
}
1190
1315
1191
- /*
1192
- * Likewise, copy extended statistics if requested
1193
- */
1194
- if (table_like_clause -> options & CREATE_TABLE_LIKE_STATISTICS )
1195
- {
1196
- List * parent_extstats ;
1197
- ListCell * l ;
1198
-
1199
- parent_extstats = RelationGetStatExtList (relation );
1200
-
1201
- foreach (l , parent_extstats )
1202
- {
1203
- Oid parent_stat_oid = lfirst_oid (l );
1204
- CreateStatsStmt * stats_stmt ;
1205
-
1206
- stats_stmt = generateClonedExtStatsStmt (cxt -> relation ,
1207
- RelationGetRelid (relation ),
1208
- parent_stat_oid );
1209
-
1210
- /* Copy comment on statistics object, if requested */
1211
- if (table_like_clause -> options & CREATE_TABLE_LIKE_COMMENTS )
1212
- {
1213
- comment = GetComment (parent_stat_oid , StatisticExtRelationId , 0 );
1214
-
1215
- /*
1216
- * We make use of CreateStatsStmt's stxcomment option, so as
1217
- * not to need to know now what name the statistics will have.
1218
- */
1219
- stats_stmt -> stxcomment = comment ;
1220
- }
1221
-
1222
- cxt -> extstats = lappend (cxt -> extstats , stats_stmt );
1223
- }
1224
-
1225
- list_free (parent_extstats );
1226
- }
1316
+ /* Done with child rel */
1317
+ heap_close (childrel , NoLock );
1227
1318
1228
1319
/*
1229
1320
* Close the parent rel, but keep our AccessShareLock on it until xact
1230
1321
* commit. That will prevent someone else from deleting or ALTERing the
1231
1322
* parent before the child is committed.
1232
1323
*/
1233
1324
heap_close (relation , NoLock );
1325
+
1326
+ return result ;
1234
1327
}
1235
1328
1236
1329
static void
@@ -1810,24 +1903,6 @@ transformIndexConstraints(CreateStmtContext *cxt)
1810
1903
indexlist = lappend (indexlist , index );
1811
1904
}
1812
1905
1813
- /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
1814
- foreach (lc , cxt -> inh_indexes )
1815
- {
1816
- index = (IndexStmt * ) lfirst (lc );
1817
-
1818
- if (index -> primary )
1819
- {
1820
- if (cxt -> pkey != NULL )
1821
- ereport (ERROR ,
1822
- (errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
1823
- errmsg ("multiple primary keys for table \"%s\" are not allowed" ,
1824
- cxt -> relation -> relname )));
1825
- cxt -> pkey = index ;
1826
- }
1827
-
1828
- indexlist = lappend (indexlist , index );
1829
- }
1830
-
1831
1906
/*
1832
1907
* Scan the index list and remove any redundant index specifications. This
1833
1908
* can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
@@ -2962,7 +3037,6 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
2962
3037
cxt .ckconstraints = NIL ;
2963
3038
cxt .fkconstraints = NIL ;
2964
3039
cxt .ixconstraints = NIL ;
2965
- cxt .inh_indexes = NIL ;
2966
3040
cxt .extstats = NIL ;
2967
3041
cxt .blist = NIL ;
2968
3042
cxt .alist = NIL ;
0 commit comments