28
28
29
29
#include "access/htup_details.h"
30
30
#include "access/reloptions.h"
31
+ #include "access/tupconvert.h"
31
32
#include "catalog/dependency.h"
32
33
#include "catalog/heap.h"
33
34
#include "catalog/index.h"
@@ -77,7 +78,6 @@ typedef struct
77
78
List * ckconstraints ; /* CHECK constraints */
78
79
List * fkconstraints ; /* FOREIGN KEY constraints */
79
80
List * ixconstraints ; /* index-creating constraints */
80
- List * inh_indexes ; /* cloned indexes from INCLUDING INDEXES */
81
81
List * blist ; /* "before list" of things to do before
82
82
* creating the table */
83
83
List * alist ; /* "after list" of things to do after creating
@@ -108,7 +108,7 @@ static void transformTableLikeClause(CreateStmtContext *cxt,
108
108
TableLikeClause * table_like_clause );
109
109
static void transformOfType (CreateStmtContext * cxt ,
110
110
TypeName * ofTypename );
111
- static IndexStmt * generateClonedIndexStmt (CreateStmtContext * cxt ,
111
+ static IndexStmt * generateClonedIndexStmt (RangeVar * heapRel ,
112
112
Relation source_idx ,
113
113
const AttrNumber * attmap , int attmap_length );
114
114
static List * get_collation (Oid collation , Oid actual_datatype );
@@ -132,6 +132,9 @@ static void setSchemaName(char *context_schema, char **stmt_schema_name);
132
132
* Returns a List of utility commands to be done in sequence. One of these
133
133
* will be the transformed CreateStmt, but there may be additional actions
134
134
* to be done before and after the actual DefineRelation() call.
135
+ * In addition to normal utility commands such as AlterTableStmt and
136
+ * IndexStmt, the result list may contain TableLikeClause(s), representing
137
+ * the need to perform additional parse analysis after DefineRelation().
135
138
*
136
139
* SQL allows constraints to be scattered all over, so thumb through
137
140
* the columns and collect all constraints into one place.
@@ -218,7 +221,6 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
218
221
cxt .ckconstraints = NIL ;
219
222
cxt .fkconstraints = NIL ;
220
223
cxt .ixconstraints = NIL ;
221
- cxt .inh_indexes = NIL ;
222
224
cxt .blist = NIL ;
223
225
cxt .alist = NIL ;
224
226
cxt .pkey = NULL ;
@@ -691,8 +693,11 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
691
693
* transformTableLikeClause
692
694
*
693
695
* Change the LIKE <srctable> portion of a CREATE TABLE statement into
694
- * column definitions which recreate the user defined column portions of
695
- * <srctable>.
696
+ * column definitions that recreate the user defined column portions of
697
+ * <srctable>. Also, if there are any LIKE options that we can't fully
698
+ * process at this point, add the TableLikeClause to cxt->alist, which
699
+ * will cause utility.c to call expandTableLikeClause() after the new
700
+ * table has been created.
696
701
*/
697
702
static void
698
703
transformTableLikeClause (CreateStmtContext * cxt , TableLikeClause * table_like_clause )
@@ -701,7 +706,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
701
706
Relation relation ;
702
707
TupleDesc tupleDesc ;
703
708
TupleConstr * constr ;
704
- AttrNumber * attmap ;
705
709
AclResult aclresult ;
706
710
char * comment ;
707
711
ParseCallbackState pcbstate ;
@@ -715,6 +719,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
715
719
(errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
716
720
errmsg ("LIKE is not supported for creating foreign tables" )));
717
721
722
+ /* Open the relation referenced by the LIKE clause */
718
723
relation = relation_openrv (table_like_clause -> relation , AccessShareLock );
719
724
720
725
if (relation -> rd_rel -> relkind != RELKIND_RELATION &&
@@ -752,15 +757,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
752
757
tupleDesc = RelationGetDescr (relation );
753
758
constr = tupleDesc -> constr ;
754
759
755
- /*
756
- * Initialize column number map for map_variable_attnos(). We need this
757
- * since dropped columns in the source table aren't copied, so the new
758
- * table can have different column numbers.
759
- */
760
- attmap = (AttrNumber * ) palloc0 (sizeof (AttrNumber ) * tupleDesc -> natts );
761
-
762
760
/*
763
761
* Insert the copied attributes into the cxt for the new table definition.
762
+ * We must do this now so that they appear in the table in the relative
763
+ * position where the LIKE clause is, as required by SQL99.
764
764
*/
765
765
for (parent_attno = 1 ; parent_attno <= tupleDesc -> natts ;
766
766
parent_attno ++ )
@@ -770,7 +770,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
770
770
ColumnDef * def ;
771
771
772
772
/*
773
- * Ignore dropped columns in the parent. attmap entry is left zero.
773
+ * Ignore dropped columns in the parent.
774
774
*/
775
775
if (attribute -> attisdropped )
776
776
continue ;
@@ -802,8 +802,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
802
802
*/
803
803
cxt -> columns = lappend (cxt -> columns , def );
804
804
805
- attmap [parent_attno - 1 ] = list_length (cxt -> columns );
806
-
807
805
/*
808
806
* Copy default, if present and the default has been requested
809
807
*/
@@ -860,22 +858,88 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
860
858
}
861
859
}
862
860
861
+ /*
862
+ * We cannot yet deal with CHECK constraints or indexes, since we don't
863
+ * yet know what column numbers the copied columns will have in the
864
+ * finished table. If any of those options are specified, add the LIKE
865
+ * clause to cxt->alist so that expandTableLikeClause will be called after
866
+ * we do know that.
867
+ */
868
+ if (table_like_clause -> options &
869
+ (CREATE_TABLE_LIKE_CONSTRAINTS |
870
+ CREATE_TABLE_LIKE_INDEXES ))
871
+ cxt -> alist = lappend (cxt -> alist , table_like_clause );
872
+
873
+ /*
874
+ * Close the parent rel, but keep our AccessShareLock on it until xact
875
+ * commit. That will prevent someone else from deleting or ALTERing the
876
+ * parent before we can run expandTableLikeClause.
877
+ */
878
+ heap_close (relation , NoLock );
879
+ }
880
+
881
+ /*
882
+ * expandTableLikeClause
883
+ *
884
+ * Process LIKE options that require knowing the final column numbers
885
+ * assigned to the new table's columns. This executes after we have
886
+ * run DefineRelation for the new table. It returns a list of utility
887
+ * commands that should be run to generate indexes etc.
888
+ */
889
+ List *
890
+ expandTableLikeClause (RangeVar * heapRel , TableLikeClause * table_like_clause )
891
+ {
892
+ List * result = NIL ;
893
+ List * atsubcmds = NIL ;
894
+ Relation relation ;
895
+ Relation childrel ;
896
+ TupleDesc tupleDesc ;
897
+ TupleConstr * constr ;
898
+ AttrNumber * attmap ;
899
+ char * comment ;
900
+
901
+ /*
902
+ * Open the relation referenced by the LIKE clause. We should still have
903
+ * the table lock obtained by transformTableLikeClause (and this'll throw
904
+ * an assertion failure if not). Hence, no need to recheck privileges
905
+ * etc.
906
+ */
907
+ relation = relation_openrv (table_like_clause -> relation , NoLock );
908
+
909
+ tupleDesc = RelationGetDescr (relation );
910
+ constr = tupleDesc -> constr ;
911
+
912
+ /*
913
+ * Open the newly-created child relation; we have lock on that too.
914
+ */
915
+ childrel = relation_openrv (heapRel , NoLock );
916
+
917
+ /*
918
+ * Construct a map from the LIKE relation's attnos to the child rel's.
919
+ * This re-checks type match etc, although it shouldn't be possible to
920
+ * have a failure since both tables are locked.
921
+ */
922
+ attmap = convert_tuples_by_name_map (RelationGetDescr (childrel ),
923
+ tupleDesc ,
924
+ gettext_noop ("could not convert row type" ));
925
+
863
926
/*
864
927
* Copy CHECK constraints if requested, being careful to adjust attribute
865
928
* numbers so they match the child.
866
929
*/
867
930
if ((table_like_clause -> options & CREATE_TABLE_LIKE_CONSTRAINTS ) &&
868
- tupleDesc -> constr )
931
+ constr != NULL )
869
932
{
870
933
int ccnum ;
871
934
872
- for (ccnum = 0 ; ccnum < tupleDesc -> constr -> num_check ; ccnum ++ )
935
+ for (ccnum = 0 ; ccnum < constr -> num_check ; ccnum ++ )
873
936
{
874
- char * ccname = tupleDesc -> constr -> check [ccnum ].ccname ;
875
- char * ccbin = tupleDesc -> constr -> check [ccnum ].ccbin ;
876
- Constraint * n = makeNode (Constraint );
937
+ char * ccname = constr -> check [ccnum ].ccname ;
938
+ char * ccbin = constr -> check [ccnum ].ccbin ;
877
939
Node * ccbin_node ;
878
940
bool found_whole_row ;
941
+ Constraint * n ;
942
+ AlterTableCmd * atsubcmd ;
879
943
880
944
ccbin_node = map_variable_attnos (stringToNode (ccbin ),
881
945
1 , 0 ,
@@ -896,12 +960,17 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
896
960
ccname ,
897
961
RelationGetRelationName (relation ))));
898
962
963
+ n = makeNode (Constraint );
899
964
n -> contype = CONSTR_CHECK ;
900
965
n -> location = -1 ;
901
966
n -> conname = pstrdup (ccname );
902
967
n -> raw_expr = NULL ;
903
968
n -> cooked_expr = nodeToString (ccbin_node );
904
- cxt -> ckconstraints = lappend (cxt -> ckconstraints , n );
969
+
970
+ atsubcmd = makeNode (AlterTableCmd );
971
+ atsubcmd -> subtype = AT_AddConstraint ;
972
+ atsubcmd -> def = (Node * ) n ;
973
+ atsubcmds = lappend (atsubcmds , atsubcmd );
905
974
906
975
/* Copy comment on constraint */
907
976
if ((table_like_clause -> options & CREATE_TABLE_LIKE_COMMENTS ) &&
@@ -913,19 +982,35 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
913
982
CommentStmt * stmt = makeNode (CommentStmt );
914
983
915
984
stmt -> objtype = OBJECT_TABCONSTRAINT ;
916
- stmt -> objname = list_make3 (makeString (cxt -> relation -> schemaname ),
917
- makeString (cxt -> relation -> relname ),
985
+ stmt -> objname = list_make3 (makeString (heapRel -> schemaname ),
986
+ makeString (heapRel -> relname ),
918
987
makeString (n -> conname ));
919
988
stmt -> objargs = NIL ;
920
989
stmt -> comment = comment ;
921
990
922
- cxt -> alist = lappend (cxt -> alist , stmt );
991
+ result = lappend (result , stmt );
923
992
}
924
993
}
925
994
}
926
995
927
996
/*
928
- * Likewise, copy indexes if requested
997
+ * If we generated any ALTER TABLE actions above, wrap them into a single
998
+ * ALTER TABLE command. Stick it at the front of the result, so it runs
999
+ * before any CommentStmts we made above.
1000
+ */
1001
+ if (atsubcmds )
1002
+ {
1003
+ AlterTableStmt * atcmd = makeNode (AlterTableStmt );
1004
+
1005
+ atcmd -> relation = copyObject (heapRel );
1006
+ atcmd -> cmds = atsubcmds ;
1007
+ atcmd -> relkind = OBJECT_TABLE ;
1008
+ atcmd -> missing_ok = false;
1009
+ result = lcons (atcmd , result );
1010
+ }
1011
+
1012
+ /*
1013
+ * Process indexes if required.
929
1014
*/
930
1015
if ((table_like_clause -> options & CREATE_TABLE_LIKE_INDEXES ) &&
931
1016
relation -> rd_rel -> relhasindex )
@@ -944,7 +1029,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
944
1029
parent_index = index_open (parent_index_oid , AccessShareLock );
945
1030
946
1031
/* Build CREATE INDEX statement to recreate the parent_index */
947
- index_stmt = generateClonedIndexStmt (cxt , parent_index ,
1032
+ index_stmt = generateClonedIndexStmt (heapRel , parent_index ,
948
1033
attmap , tupleDesc -> natts );
949
1034
950
1035
/* Copy comment on index, if requested */
@@ -959,19 +1044,23 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
959
1044
index_stmt -> idxcomment = comment ;
960
1045
}
961
1046
962
- /* Save it in the inh_indexes list for the time being */
963
- cxt -> inh_indexes = lappend (cxt -> inh_indexes , index_stmt );
1047
+ result = lappend (result , index_stmt );
964
1048
965
1049
index_close (parent_index , AccessShareLock );
966
1050
}
967
1051
}
968
1052
1053
+ /* Done with child rel */
1054
+ heap_close (childrel , NoLock );
1055
+
969
1056
/*
970
1057
* Close the parent rel, but keep our AccessShareLock on it until xact
971
1058
* commit. That will prevent someone else from deleting or ALTERing the
972
1059
* parent before the child is committed.
973
1060
*/
974
1061
heap_close (relation , NoLock );
1062
+
1063
+ return result ;
975
1064
}
976
1065
977
1066
static void
@@ -1024,7 +1113,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
1024
1113
* "source_idx". Attribute numbers should be adjusted according to attmap.
1025
1114
*/
1026
1115
static IndexStmt *
1027
- generateClonedIndexStmt (CreateStmtContext * cxt , Relation source_idx ,
1116
+ generateClonedIndexStmt (RangeVar * heapRel , Relation source_idx ,
1028
1117
const AttrNumber * attmap , int attmap_length )
1029
1118
{
1030
1119
Oid source_relid = RelationGetRelid (source_idx );
@@ -1076,7 +1165,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
1076
1165
1077
1166
/* Begin building the IndexStmt */
1078
1167
index = makeNode (IndexStmt );
1079
- index -> relation = cxt -> relation ;
1168
+ index -> relation = heapRel ;
1080
1169
index -> accessMethod = pstrdup (NameStr (amrec -> amname ));
1081
1170
if (OidIsValid (idxrelrec -> reltablespace ))
1082
1171
index -> tableSpace = get_tablespace_name (idxrelrec -> reltablespace );
@@ -1425,24 +1514,6 @@ transformIndexConstraints(CreateStmtContext *cxt)
1425
1514
indexlist = lappend (indexlist , index );
1426
1515
}
1427
1516
1428
- /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
1429
- foreach (lc , cxt -> inh_indexes )
1430
- {
1431
- index = (IndexStmt * ) lfirst (lc );
1432
-
1433
- if (index -> primary )
1434
- {
1435
- if (cxt -> pkey != NULL )
1436
- ereport (ERROR ,
1437
- (errcode (ERRCODE_INVALID_TABLE_DEFINITION ),
1438
- errmsg ("multiple primary keys for table \"%s\" are not allowed" ,
1439
- cxt -> relation -> relname )));
1440
- cxt -> pkey = index ;
1441
- }
1442
-
1443
- indexlist = lappend (indexlist , index );
1444
- }
1445
-
1446
1517
/*
1447
1518
* Scan the index list and remove any redundant index specifications. This
1448
1519
* can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
@@ -2429,7 +2500,6 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
2429
2500
cxt .ckconstraints = NIL ;
2430
2501
cxt .fkconstraints = NIL ;
2431
2502
cxt .ixconstraints = NIL ;
2432
- cxt .inh_indexes = NIL ;
2433
2503
cxt .blist = NIL ;
2434
2504
cxt .alist = NIL ;
2435
2505
cxt .pkey = NULL ;
0 commit comments