diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 1d52be6..4efb02e 100644
*** a/doc/src/sgml/ref/alter_table.sgml
--- b/doc/src/sgml/ref/alter_table.sgml
***************
*** 403,411 **** ALTER TABLE name
for details on the available parameters. Note that the table contents
will not be modified immediately by this command; depending on the
parameter you might need to rewrite the table to get the desired effects.
! That can be done with
! or one of the forms of ALTER
! TABLE> that forces a table rewrite.
--- 403,411 ----
for details on the available parameters. Note that the table contents
will not be modified immediately by this command; depending on the
parameter you might need to rewrite the table to get the desired effects.
! That can be done with VACUUM
! FULL>, or one of the forms
! of ALTER TABLE> that forces a table rewrite.
***************
*** 746,759 **** ALTER TABLE name
! Adding a column with a non-null default or changing the type of an
! existing column will require the entire table and indexes to be rewritten.
! This might take a significant amount of time for a large table; and it will
! temporarily require double the disk space. Adding or removing a system
oid> column likewise requires rewriting the entire table.
Adding a CHECK> or NOT NULL> constraint requires
scanning the table to verify that existing rows meet the constraint.
--- 746,770 ----
! Adding a column with a non-null default will rewrite the entire table and
! all indexes. Changing the type of an existing column will do the same
! unless a binary-coercible cast implements the type conversion. Refer to
! for further information. A rewrite might
! take a significant amount of time for a large table, and it will temporarily
! require double the disk space. Adding or removing a system
oid> column likewise requires rewriting the entire table.
+ Similar to the behavior of VACUUM FULL>, the
+ rewriting process eliminates any dead space in the table. Prior
+ to PostgreSQL> 9.0, SET DATA TYPE>
+ outpaced VACUUM FULL> at this task, so it was useful even absent
+ the need for a column type change. This speed advantage no longer holds,
+ and SET DATA TYPE> may not even rewrite the table.
+
+
+
Adding a CHECK> or NOT NULL> constraint requires
scanning the table to verify that existing rows meet the constraint.
***************
*** 777,797 **** ALTER TABLE name
- The fact that SET DATA TYPE> requires rewriting the whole table
- is sometimes an advantage, because the rewriting process eliminates
- any dead space in the table. For example, to reclaim the space occupied
- by a dropped column immediately, the fastest way is:
-
- ALTER TABLE table ALTER COLUMN anycol TYPE anytype;
-
- where anycol> is any remaining table column and
- anytype> is the same type that column already has.
- This results in no semantically-visible change in the table,
- but the command forces rewriting, which gets rid of no-longer-useful
- data.
-
-
-
The USING option of SET DATA TYPE> can actually
specify any expression involving the old values of the row; that is, it
can refer to other columns as well as the one being converted. This allows
--- 788,793 ----
diff --git a/src/backend/catalog/index.cindex 5254b65..411de09 100644
*** a/src/backend/catalog/index.c
--- b/src/backend/catalog/index.c
***************
*** 1673,1678 **** index_build(Relation heapRelation,
--- 1673,1688 ----
procedure = indexRelation->rd_am->ambuild;
Assert(RegProcedureIsValid(procedure));
+ if (indexInfo->ii_ToastForRelName != NULL)
+ ereport(DEBUG1,
+ (errmsg("building TOAST index for table \"%s\"",
+ indexInfo->ii_ToastForRelName)));
+ else
+ ereport(DEBUG1,
+ (errmsg("building index \"%s\" on table \"%s\"",
+ RelationGetRelationName(indexRelation),
+ RelationGetRelationName(heapRelation))));
+
/*
* Switch to the table owner's userid, so that any index functions are run
* as that user. Also lock down security-restricted operations and
***************
*** 2663,2669 **** IndexGetRelation(Oid indexId)
* reindex_index - This routine is used to recreate a single index
*/
void
! reindex_index(Oid indexId, bool skip_constraint_checks)
{
Relation iRel,
heapRelation,
--- 2673,2680 ----
* reindex_index - This routine is used to recreate a single index
*/
void
! reindex_index(Oid indexId, const char *toastFor,
! bool skip_constraint_checks)
{
Relation iRel,
heapRelation,
***************
*** 2721,2726 **** reindex_index(Oid indexId, bool skip_constraint_checks)
--- 2732,2740 ----
indexInfo->ii_ExclusionStrats = NULL;
}
+ /* Pass the name of relation this TOAST index serves, if any. */
+ indexInfo->ii_ToastForRelName = toastFor;
+
/* We'll build a new physical relation for the index */
RelationSetNewRelfilenode(iRel, InvalidTransactionId);
***************
*** 2780,2785 **** reindex_index(Oid indexId, bool skip_constraint_checks)
--- 2794,2802 ----
* reindex_relation - This routine is used to recreate all indexes
* of a relation (and optionally its toast relation too, if any).
*
+ * If this is a TOAST relation, toastFor may bear the parent relation name,
+ * facilitating improved messages.
+ *
* "flags" can include REINDEX_SUPPRESS_INDEX_USE and REINDEX_CHECK_CONSTRAINTS.
*
* If flags has REINDEX_SUPPRESS_INDEX_USE, the relation was just completely
***************
*** 2802,2808 **** reindex_index(Oid indexId, bool skip_constraint_checks)
* CommandCounterIncrement will occur after each index rebuild.
*/
bool
! reindex_relation(Oid relid, bool toast_too, int flags)
{
Relation rel;
Oid toast_relid;
--- 2819,2826 ----
* CommandCounterIncrement will occur after each index rebuild.
*/
bool
! reindex_relation(Oid relid, const char *toastFor,
! bool toast_too, int flags)
{
Relation rel;
Oid toast_relid;
***************
*** 2879,2885 **** reindex_relation(Oid relid, bool toast_too, int flags)
if (is_pg_class)
RelationSetIndexList(rel, doneIndexes, InvalidOid);
! reindex_index(indexOid, !(flags & REINDEX_CHECK_CONSTRAINTS));
CommandCounterIncrement();
--- 2897,2903 ----
if (is_pg_class)
RelationSetIndexList(rel, doneIndexes, InvalidOid);
! reindex_index(indexOid, toastFor, !(flags & REINDEX_CHECK_CONSTRAINTS));
CommandCounterIncrement();
***************
*** 2902,2912 **** reindex_relation(Oid relid, bool toast_too, int flags)
if (is_pg_class)
RelationSetIndexList(rel, indexIds, ClassOidIndexId);
- /*
- * Close rel, but continue to hold the lock.
- */
- heap_close(rel, NoLock);
-
result = (indexIds != NIL);
/*
--- 2920,2925 ----
***************
*** 2916,2922 **** reindex_relation(Oid relid, bool toast_too, int flags)
*/
Assert(!(toast_too && (flags & REINDEX_SUPPRESS_INDEX_USE)));
if (toast_too && OidIsValid(toast_relid))
! result |= reindex_relation(toast_relid, false, flags);
return result;
}
--- 2929,2941 ----
*/
Assert(!(toast_too && (flags & REINDEX_SUPPRESS_INDEX_USE)));
if (toast_too && OidIsValid(toast_relid))
! result |= reindex_relation(toast_relid, RelationGetRelationName(rel),
! false, flags);
!
! /*
! * Close rel, but continue to hold the lock.
! */
! heap_close(rel, NoLock);
return result;
}
diff --git a/src/backend/catalog/tindex c4be3a9..f60b7c1 100644
*** a/src/backend/catalog/toasting.c
--- b/src/backend/catalog/toasting.c
***************
*** 36,43 **** extern Oid binary_upgrade_next_toast_pg_class_oid;
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
! static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
! Datum reloptions);
static bool needs_toast_table(Relation rel);
--- 36,43 ----
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
! static bool create_toast_table(Relation rel, const char *finalRelName,
! Oid toastOid, Oid toastIndexOid, Datum reloptions);
static bool needs_toast_table(Relation rel);
***************
*** 46,51 **** static bool needs_toast_table(Relation rel);
--- 46,54 ----
* If the table needs a toast table, and doesn't already have one,
* then create a toast table for it.
*
+ * make_new_heap fills finalRelName, so messages display the permanent table
+ * name, not the rewrite-temporary name. Most callers should pass NULL.
+ *
* reloptions for the toast table can be passed, too. Pass (Datum) 0
* for default reloptions.
*
***************
*** 54,60 **** static bool needs_toast_table(Relation rel);
* to end with CommandCounterIncrement if it makes any changes.
*/
void
! AlterTableCreateToastTable(Oid relOid, Datum reloptions)
{
Relation rel;
--- 57,64 ----
* to end with CommandCounterIncrement if it makes any changes.
*/
void
! AlterTableCreateToastTable(Oid relOid, const char *finalRelName,
! Datum reloptions)
{
Relation rel;
***************
*** 66,72 **** AlterTableCreateToastTable(Oid relOid, Datum reloptions)
rel = heap_open(relOid, AccessExclusiveLock);
/* create_toast_table does all the work */
! (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions);
heap_close(rel, NoLock);
}
--- 70,77 ----
rel = heap_open(relOid, AccessExclusiveLock);
/* create_toast_table does all the work */
! (void) create_toast_table(rel, finalRelName,
! InvalidOid, InvalidOid, reloptions);
heap_close(rel, NoLock);
}
***************
*** 92,98 **** BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
relName)));
/* create_toast_table does all the work */
! if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0))
elog(ERROR, "\"%s\" does not require a toast table",
relName);
--- 97,103 ----
relName)));
/* create_toast_table does all the work */
! if (!create_toast_table(rel, NULL, toastOid, toastIndexOid, (Datum) 0))
elog(ERROR, "\"%s\" does not require a toast table",
relName);
***************
*** 104,114 **** BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid)
* create_toast_table --- internal workhorse
*
* rel is already opened and exclusive-locked
* toastOid and toastIndexOid are normally InvalidOid, but during
* bootstrap they can be nonzero to specify hand-assigned OIDs
*/
static bool
! create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions)
{
Oid relOid = RelationGetRelid(rel);
HeapTuple reltup;
--- 109,121 ----
* create_toast_table --- internal workhorse
*
* rel is already opened and exclusive-locked
+ * finalRelName is normally NULL; make_new_heap overrides it
* toastOid and toastIndexOid are normally InvalidOid, but during
* bootstrap they can be nonzero to specify hand-assigned OIDs
*/
static bool
! create_toast_table(Relation rel, const char *finalRelName,
! Oid toastOid, Oid toastIndexOid, Datum reloptions)
{
Oid relOid = RelationGetRelid(rel);
HeapTuple reltup;
***************
*** 259,264 **** create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
--- 266,273 ----
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
+ indexInfo->ii_ToastForRelName
+ = finalRelName != NULL ? finalRelName : RelationGetRelationName(rel);
indexInfo->ii_Unique = true;
indexInfo->ii_ReadyForInserts = true;
indexInfo->ii_Concurrent = false;
diff --git a/src/backend/commands/cluindex 59a4394..87af84f 100644
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 681,687 **** make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
if (isNull)
reloptions = (Datum) 0;
! AlterTableCreateToastTable(OIDNewHeap, reloptions);
ReleaseSysCache(tuple);
}
--- 681,688 ----
if (isNull)
reloptions = (Datum) 0;
! AlterTableCreateToastTable(OIDNewHeap, RelationGetRelationName(OldHeap),
! reloptions);
ReleaseSysCache(tuple);
}
***************
*** 1400,1406 **** finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
reindex_flags = REINDEX_SUPPRESS_INDEX_USE;
if (check_constraints)
reindex_flags |= REINDEX_CHECK_CONSTRAINTS;
! reindex_relation(OIDOldHeap, false, reindex_flags);
/* Destroy new heap with old filenode */
object.classId = RelationRelationId;
--- 1401,1407 ----
reindex_flags = REINDEX_SUPPRESS_INDEX_USE;
if (check_constraints)
reindex_flags |= REINDEX_CHECK_CONSTRAINTS;
! reindex_relation(OIDOldHeap, NULL, false, reindex_flags);
/* Destroy new heap with old filenode */
object.classId = RelationRelationId;
diff --git a/src/backend/commands/indindex 94ed437..1dab757 100644
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
***************
*** 1482,1488 **** ReindexIndex(RangeVar *indexRelation)
ReleaseSysCache(tuple);
! reindex_index(indOid, false);
}
/*
--- 1482,1488 ----
ReleaseSysCache(tuple);
! reindex_index(indOid, NULL, false);
}
/*
***************
*** 1514,1520 **** ReindexTable(RangeVar *relation)
ReleaseSysCache(tuple);
! if (!reindex_relation(heapOid, true, 0))
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes",
relation->relname)));
--- 1514,1520 ----
ReleaseSysCache(tuple);
! if (!reindex_relation(heapOid, NULL, true, 0))
ereport(NOTICE,
(errmsg("table \"%s\" has no indexes",
relation->relname)));
***************
*** 1627,1633 **** ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
! if (reindex_relation(relid, true, 0))
ereport(NOTICE,
(errmsg("table \"%s.%s\" was reindexed",
get_namespace_name(get_rel_namespace(relid)),
--- 1627,1633 ----
StartTransactionCommand();
/* functions in indexes may want a snapshot set */
PushActiveSnapshot(GetTransactionSnapshot());
! if (reindex_relation(relid, NULL, true, 0))
ereport(NOTICE,
(errmsg("table \"%s.%s\" was reindexed",
get_namespace_name(get_rel_namespace(relid)),
diff --git a/src/backend/commands/tableindex 1ecba02..67fdafc 100644
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 71,76 ****
--- 71,77 ----
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/datum.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
***************
*** 129,134 **** static List *on_commits = NIL;
--- 130,138 ----
#define AT_PASS_MISC 8 /* other stuff */
#define AT_NUM_PASSES 9
+ /* Level of effort required in Phase 3 (ATRewriteTables). */
+ typedef enum { WORK_NONE = 0, WORK_SCAN = 1, WORK_REWRITE = 2 } WorkLevel;
+
typedef struct AlteredTableInfo
{
/* Information saved before any work commences: */
***************
*** 142,147 **** typedef struct AlteredTableInfo
--- 146,152 ----
List *newvals; /* List of NewColumnValue */
bool new_notnull; /* T if we added new NOT NULL constraints */
bool new_changeoids; /* T if we added/dropped the OID column */
+ WorkLevel worklevel; /* How much work shall we do on the heap? */
Oid newTableSpace; /* new tablespace; 0 means no change */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
***************
*** 1078,1084 **** ExecuteTruncate(TruncateStmt *stmt)
/*
* Reconstruct the indexes to match, and we're done.
*/
! reindex_relation(heap_relid, true, 0);
}
}
--- 1083,1089 ----
/*
* Reconstruct the indexes to match, and we're done.
*/
! reindex_relation(heap_relid, NULL, true, 0);
}
}
***************
*** 2990,2996 **** ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode)
(tab->subcmds[AT_PASS_ADD_COL] ||
tab->subcmds[AT_PASS_ALTER_TYPE] ||
tab->subcmds[AT_PASS_COL_ATTRS]))
! AlterTableCreateToastTable(tab->relid, (Datum) 0);
}
}
--- 2995,3001 ----
(tab->subcmds[AT_PASS_ADD_COL] ||
tab->subcmds[AT_PASS_ALTER_TYPE] ||
tab->subcmds[AT_PASS_COL_ATTRS]))
! AlterTableCreateToastTable(tab->relid, NULL, (Datum) 0);
}
}
***************
*** 3193,3202 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode)
continue;
/*
* We only need to rewrite the table if at least one column needs to
* be recomputed, or we are adding/removing the OID column.
*/
! if (tab->newvals != NIL || tab->new_changeoids)
{
/* Build a temporary relation and copy data */
Relation OldHeap;
--- 3198,3232 ----
continue;
/*
+ * Any operation has to be propagated to tables that use this table's
+ * rowtype as a column type, but this is not yet implemented. Reject
+ * changes where this ommission would be particularly glaring: column
+ * type changes, new columns with default values, and OIDs changes.
+ *
+ * (Eventually this will probably become true for scans as well, but at
+ * the moment a composite type does not enforce any constraints, so it's
+ * not necessary/appropriate to enforce them just during ALTER.)
+ */
+ if (tab->newvals != NIL || tab->new_changeoids)
+ {
+ Relation rel;
+
+ rel = heap_open(tab->relid, NoLock);
+ find_composite_type_dependencies(rel->rd_rel->reltype,
+ RelationGetRelationName(rel),
+ NULL);
+ heap_close(rel, NoLock);
+ }
+
+ /* New NOT NULL constraints always require a scan. */
+ if (tab->new_notnull)
+ tab->worklevel = Max(tab->worklevel, WORK_SCAN);
+
+ /*
* We only need to rewrite the table if at least one column needs to
* be recomputed, or we are adding/removing the OID column.
*/
! if (tab->worklevel == WORK_REWRITE)
{
/* Build a temporary relation and copy data */
Relation OldHeap;
***************
*** 3263,3269 **** ATRewriteTables(List **wqueue, LOCKMODE lockmode)
* Test the current data within the table against new constraints
* generated by ALTER TABLE commands, but don't rebuild data.
*/
! if (tab->constraints != NIL || tab->new_notnull)
ATRewriteTable(tab, InvalidOid, lockmode);
/*
--- 3293,3299 ----
* Test the current data within the table against new constraints
* generated by ALTER TABLE commands, but don't rebuild data.
*/
! if (tab->worklevel == WORK_SCAN)
ATRewriteTable(tab, InvalidOid, lockmode);
/*
***************
*** 3332,3338 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
Relation newrel;
TupleDesc oldTupDesc;
TupleDesc newTupDesc;
- bool needscan = false;
List *notnull_attrs;
int i;
ListCell *l;
--- 3362,3367 ----
***************
*** 3378,3396 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
/*
- * If we need to rewrite the table, the operation has to be propagated to
- * tables that use this table's rowtype as a column type.
- *
- * (Eventually this will probably become true for scans as well, but at
- * the moment a composite type does not enforce any constraints, so it's
- * not necessary/appropriate to enforce them just during ALTER.)
- */
- if (newrel)
- find_composite_type_dependencies(oldrel->rd_rel->reltype,
- RelationGetRelationName(oldrel),
- NULL);
-
- /*
* Generate the constraint and default execution states
*/
--- 3407,3412 ----
***************
*** 3404,3410 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
switch (con->contype)
{
case CONSTR_CHECK:
- needscan = true;
con->qualstate = (List *)
ExecPrepareExpr((Expr *) con->qual, estate);
break;
--- 3420,3425 ----
***************
*** 3439,3450 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
!newTupDesc->attrs[i]->attisdropped)
notnull_attrs = lappend_int(notnull_attrs, i);
}
- if (notnull_attrs)
- needscan = true;
}
! if (newrel || needscan)
! {
ExprContext *econtext;
Datum *values;
bool *isnull;
--- 3454,3462 ----
!newTupDesc->attrs[i]->attisdropped)
notnull_attrs = lappend_int(notnull_attrs, i);
}
}
! { /* XXX reindent? */
ExprContext *econtext;
Datum *values;
bool *isnull;
***************
*** 3456,3461 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
--- 3468,3482 ----
List *dropped_attrs = NIL;
ListCell *lc;
+ if (newrel)
+ ereport(DEBUG1,
+ (errmsg("rewriting table \"%s\"",
+ RelationGetRelationName(oldrel))));
+ else
+ ereport(DEBUG1,
+ (errmsg("verifying table \"%s\"",
+ RelationGetRelationName(oldrel))));
+
econtext = GetPerTupleExprContext(estate);
/*
***************
*** 3498,3515 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
! if (newrel)
{
Oid tupOid = InvalidOid;
! /* Extract data from old tuple */
! heap_deform_tuple(tuple, oldTupDesc, values, isnull);
! if (oldTupDesc->tdhasoid)
! tupOid = HeapTupleGetOid(tuple);
!
! /* Set dropped attributes to null in new tuple */
! foreach(lc, dropped_attrs)
! isnull[lfirst_int(lc)] = true;
/*
* Process supplied expressions to replace selected columns.
--- 3519,3549 ----
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
! /*
! * If we're changing the TupleDesc, compute new tuple values using
! * each transformation expression. When rewriting, also form a new
! * physical tuple. In Assert-enabled builds, check for cases that
! * should have been WORK_REWRITE by comparing the data.
! */
! if (tab->newvals != NIL || tab->new_changeoids)
{
Oid tupOid = InvalidOid;
! if (newrel
! #ifdef USE_ASSERT_CHECKING
! || assert_enabled
! #endif
! )
! {
! /* Extract data from old tuple */
! heap_deform_tuple(tuple, oldTupDesc, values, isnull);
! if (oldTupDesc->tdhasoid)
! tupOid = HeapTupleGetOid(tuple);
!
! /* Set dropped attributes to null in new tuple */
! foreach(lc, dropped_attrs)
! isnull[lfirst_int(lc)] = true;
! }
/*
* Process supplied expressions to replace selected columns.
***************
*** 3526,3542 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
econtext,
&isnull[ex->attnum - 1],
NULL);
}
! /*
! * Form the new tuple. Note that we don't explicitly pfree it,
! * since the per-tuple memory context will be reset shortly.
! */
! tuple = heap_form_tuple(newTupDesc, values, isnull);
! /* Preserve OID, if any */
! if (newTupDesc->tdhasoid)
! HeapTupleSetOid(tuple, tupOid);
}
/* Now check any constraints on the possibly-changed tuple */
--- 3560,3603 ----
econtext,
&isnull[ex->attnum - 1],
NULL);
+
+ #ifdef USE_ASSERT_CHECKING
+ if (assert_enabled)
+ {
+ Datum oldval = values[ex->attnum - 1];
+ bool oldisnull = isnull[ex->attnum - 1];
+ Form_pg_attribute f = newTupDesc->attrs[ex->attnum - 1];
+
+ if (f->attbyval && f->attlen == -1)
+ oldval = PointerGetDatum(PG_DETOAST_DATUM(oldval));
+
+ /*
+ * We don't detect the gross error of !newrel when the
+ * typlen actually changed. attbyval could differ in
+ * theory, but we assume it does not.
+ */
+ Assert(newrel ||
+ (isnull[ex->attnum - 1] == oldisnull
+ && (oldisnull ||
+ datumIsEqual(oldval,
+ values[ex->attnum - 1],
+ f->attbyval, f->attlen))));
+ }
+ #endif
}
! if (newrel)
! {
! /*
! * Form the new tuple. Note that we don't explicitly pfree it,
! * since the per-tuple memory context will be reset shortly.
! */
! tuple = heap_form_tuple(newTupDesc, values, isnull);
! /* Preserve OID, if any */
! if (newTupDesc->tdhasoid)
! HeapTupleSetOid(tuple, tupOid);
! }
}
/* Now check any constraints on the possibly-changed tuple */
***************
*** 4283,4288 **** ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
--- 4344,4350 ----
newval->expr = defval;
tab->newvals = lappend(tab->newvals, newval);
+ tab->worklevel = WORK_REWRITE;
}
/*
***************
*** 4299,4305 **** ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
--- 4361,4370 ----
* table to fix that.
*/
if (isOid)
+ {
tab->new_changeoids = true;
+ tab->worklevel = WORK_REWRITE;
+ }
/*
* Add needed dependency entries for the new column.
***************
*** 4973,4978 **** ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
--- 5038,5044 ----
/* Tell Phase 3 to physically remove the OID column */
tab->new_changeoids = true;
+ tab->worklevel = WORK_REWRITE;
}
}
***************
*** 4996,5002 **** ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
/* suppress schema rights check when rebuilding existing index */
check_rights = !is_rebuild;
/* skip index build if phase 3 will have to rewrite table anyway */
! skip_build = (tab->newvals != NIL);
/* suppress notices when rebuilding existing index */
quiet = is_rebuild;
--- 5062,5068 ----
/* suppress schema rights check when rebuilding existing index */
check_rights = !is_rebuild;
/* skip index build if phase 3 will have to rewrite table anyway */
! skip_build = (tab->worklevel == WORK_REWRITE);
/* suppress notices when rebuilding existing index */
quiet = is_rebuild;
***************
*** 5205,5210 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
--- 5271,5277 ----
newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr);
tab->constraints = lappend(tab->constraints, newcon);
+ tab->worklevel = Max(tab->worklevel, WORK_SCAN);
/* Save the actually assigned name if it was defaulted */
if (constr->conname == NULL)
***************
*** 5551,5556 **** ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
--- 5618,5627 ----
newcon->qual = (Node *) fkconstraint;
tab->constraints = lappend(tab->constraints, newcon);
+ /*
+ * No need to set tab->worklevel; foreign key validation is a distinct
+ * aspect of Phase 3.
+ */
}
/*
***************
*** 5874,5879 **** validateForeignKeyConstraint(Constraint *fkconstraint,
--- 5945,5954 ----
HeapTuple tuple;
Trigger trig;
+ ereport(DEBUG1,
+ (errmsg("validating foreign key constraint \"%s\"",
+ fkconstraint->conname)));
+
/*
* Build a trigger call structure; we'll need it either way.
*/
***************
*** 6361,6366 **** ATPrepAlterColumnType(List **wqueue,
--- 6436,6443 ----
if (tab->relkind == RELKIND_RELATION)
{
+ CoerceExemptions exempt;
+
/*
* Set up an expression to transform the old data value to the new type.
* If a USING option was given, transform and use that expression, else
***************
*** 6421,6426 **** ATPrepAlterColumnType(List **wqueue,
--- 6498,6504 ----
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" cannot be cast to type %s",
colName, format_type_be(targettype))));
+ exempt = GetCoerceExemptions(transform, 1, attnum);
/*
* Add a work queue item to make ATRewriteTable update the column
***************
*** 6431,6436 **** ATPrepAlterColumnType(List **wqueue,
--- 6509,6519 ----
newval->expr = (Expr *) transform;
tab->newvals = lappend(tab->newvals, newval);
+ if (!(exempt & COERCE_EXEMPT_NOCHANGE))
+ tab->worklevel = WORK_REWRITE;
+ else if (!(exempt & COERCE_EXEMPT_NOERROR))
+ tab->worklevel = Max(tab->worklevel, WORK_SCAN);
+ /* else, both bits set: WORK_NONE */
}
else if (tab->relkind == RELKIND_FOREIGN_TABLE)
{
diff --git a/src/backend/executor/execMindex 600f7e0..56b10a3 100644
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 2307,2313 **** OpenIntoRel(QueryDesc *queryDesc)
(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
! AlterTableCreateToastTable(intoRelationId, reloptions);
/*
* And open the constructed table for writing.
--- 2307,2313 ----
(void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true);
! AlterTableCreateToastTable(intoRelationId, NULL, reloptions);
/*
* And open the constructed table for writing.
diff --git a/src/backend/parser/parse_index 5b0dc14..b27e5d2 100644
*** a/src/backend/parser/parse_coerce.c
--- b/src/backend/parser/parse_coerce.c
***************
*** 19,24 ****
--- 19,25 ----
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+ #include "commands/typecmds.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
***************
*** 1805,1810 **** IsBinaryCoercible(Oid srctype, Oid targettype)
--- 1806,1871 ----
}
+ /* GetCoerceExemptions()
+ * Assess invariants of a coercion expression.
+ *
+ * Various common expressions arising from type coercion are subject to
+ * optimizations. For example, a simple varchar -> text cast will never change
+ * the underlying data (COERCE_EXEMPT_NOCHANGE) and never yield an error
+ * (COERCE_EXEMPT_NOERROR). A varchar(8) -> varchar(4) will never change the
+ * data, but it may yield an error. Given a varno and varattno denoting "the"
+ * source datum, determine which invariants hold for an expression by walking it
+ * per these rules:
+ *
+ * 1. A Var with the varno/varattno in question has both invariants.
+ * 2. A RelabelType node inherits the invariants of its sole argument.
+ * 3. A CoerceToDomain node inherits any COERCE_EXEMPT_NOCHANGE invariant from
+ * its sole argument. When GetDomainConstraints() == NIL, it also inherits
+ * COERCE_EXEMPT_NOERROR. Otherwise, COERCE_EXEMPT_NOERROR becomes false.
+ * 4. All other nodes have neither invariant.
+ *
+ * Returns a bit string that may contain the following bits:
+ * COERCE_EXEMPT_NOCHANGE: expression result will always have the same binary
+ * representation as a Var expression having the given varno and
+ * varattno
+ * COERCE_EXEMPT_NOERROR: expression will never throw an error
+ */
+ CoerceExemptions
+ GetCoerceExemptions(Node *expr,
+ Index varno, AttrNumber varattno)
+ {
+ CoerceExemptions ret = COERCE_EXEMPT_NOCHANGE | COERCE_EXEMPT_NOERROR;
+
+ Assert(expr != NULL);
+
+ for (;;)
+ {
+ if (IsA(expr, Var)
+ && ((Var *) expr)->varno == varno
+ && ((Var *) expr)->varattno == varattno)
+ {
+ return ret;
+ }
+ if (IsA(expr, RelabelType))
+ {
+ expr = (Node *) ((RelabelType *) expr)->arg;
+ }
+ else if (IsA(expr, CoerceToDomain))
+ {
+ CoerceToDomain *d = (CoerceToDomain *) expr;
+
+ if (GetDomainConstraints(d->resulttype) != NIL)
+ ret &= ~COERCE_EXEMPT_NOERROR;
+ expr = (Node *) d->arg;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+
+
/*
* find_coercion_pathway
* Look for a coercion pathway between two types.
diff --git a/src/backend/tcop/utility.c index 9500037..e6416e4 100644
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 540,546 **** standard_ProcessUtility(Node *parsetree,
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
true);
! AlterTableCreateToastTable(relOid, toast_options);
}
else if (IsA(stmt, CreateForeignTableStmt))
{
--- 540,546 ----
(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
true);
! AlterTableCreateToastTable(relOid, NULL, toast_options);
}
else if (IsA(stmt, CreateForeignTableStmt))
{
diff --git a/src/include/catalog/index 60387cc..3e90656 100644
*** a/src/include/catalog/index.h
--- b/src/include/catalog/index.h
***************
*** 85,95 **** extern double IndexBuildHeapScan(Relation heapRelation,
extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
! extern void reindex_index(Oid indexId, bool skip_constraint_checks);
#define REINDEX_CHECK_CONSTRAINTS 0x1
#define REINDEX_SUPPRESS_INDEX_USE 0x2
! extern bool reindex_relation(Oid relid, bool toast_too, int flags);
extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid);
--- 85,97 ----
extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
! extern void reindex_index(Oid indexId, const char *toastFor,
! bool skip_constraint_checks);
#define REINDEX_CHECK_CONSTRAINTS 0x1
#define REINDEX_SUPPRESS_INDEX_USE 0x2
! extern bool reindex_relation(Oid relid, const char *toastFor,
! bool toast_too, int flags);
extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid);
diff --git a/src/include/catalog/tindex de3623a..7bd2bdd 100644
*** a/src/include/catalog/toasting.h
--- b/src/include/catalog/toasting.h
***************
*** 17,23 ****
/*
* toasting.c prototypes
*/
! extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions);
extern void BootstrapToastTable(char *relName,
Oid toastOid, Oid toastIndexOid);
--- 17,24 ----
/*
* toasting.c prototypes
*/
! extern void AlterTableCreateToastTable(Oid relOid, const char *finalRelName,
! Datum reloptions);
extern void BootstrapToastTable(char *relName,
Oid toastOid, Oid toastIndexOid);
diff --git a/src/include/nodes/execnoindex 546b581..46d9d1a 100644
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 64,69 **** typedef struct IndexInfo
--- 64,70 ----
Oid *ii_ExclusionOps; /* array with one entry per column */
Oid *ii_ExclusionProcs; /* array with one entry per column */
uint16 *ii_ExclusionStrats; /* array with one entry per column */
+ const char *ii_ToastForRelName; /* TOAST index only: name of main rel */
bool ii_Unique;
bool ii_ReadyForInserts;
bool ii_Concurrent;
diff --git a/src/include/parser/parsindex ceaff2f..4303acf 100644
*** a/src/include/parser/parse_coerce.h
--- b/src/include/parser/parse_coerce.h
***************
*** 30,37 **** typedef enum CoercionPathType
--- 30,44 ----
COERCION_PATH_COERCEVIAIO /* need a CoerceViaIO node */
} CoercionPathType;
+ /* Bits in the return value of GetCoerceExemptions. */
+ typedef int CoerceExemptions;
+
+ #define COERCE_EXEMPT_NOCHANGE 0x1 /* expression never changes storage */
+ #define COERCE_EXEMPT_NOERROR 0x2 /* expression never throws an error */
extern bool IsBinaryCoercible(Oid srctype, Oid targettype);
+ extern CoerceExemptions GetCoerceExemptions(Node *expr,
+ Index varno, AttrNumber varattno);
extern bool IsPreferredType(TYPCATEGORY category, Oid type);
extern TYPCATEGORY TypeCategory(Oid type);
diff --git a/src/test/regress/GNUmakefilindex 15b9ec4..c33ecb9 100644
diff --git a/src/test/regress/expecindex 3280065..7a5b96e 100644
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
***************
*** 1482,1487 **** create table tab2 (x int, y tab1);
--- 1482,1630 ----
alter table tab1 alter column b type varchar; -- fails
ERROR: cannot alter table "tab1" because column "tab2"."y" uses its rowtype
--
+ -- ALTER COLUMN ... SET DATA TYPE optimizations
+ --
+ SET client_min_messages = debug1; -- Track rewrites.
+ -- Model a type change that throws the semantics of dependent expressions.
+ CREATE DOMAIN trickint AS int;
+ CREATE FUNCTION touchy_f(trickint) RETURNS int4 LANGUAGE sql AS 'SELECT 100';
+ CREATE FUNCTION touchy_f(int4) RETURNS int4 LANGUAGE sql AS 'SELECT $1';
+ CREATE DOMAIN lendom AS varchar(8);
+ CREATE DOMAIN checkdom AS text CHECK (VALUE LIKE '<%');
+ CREATE TABLE parent (keycol numeric PRIMARY KEY);
+ DEBUG: building TOAST index for table "parent"
+ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "parent_pkey" for table "parent"
+ DEBUG: building index "parent_pkey" on table "parent"
+ INSERT INTO parent VALUES (0.12), (1.12);
+ CREATE TABLE t (
+ integral int4 NOT NULL,
+ rational numeric(9,4) UNIQUE NOT NULL REFERENCES parent,
+ string varchar(4) NOT NULL,
+ strarr varchar(2)[] NOT NULL,
+ CHECK (touchy_f(integral) < 10),
+ EXCLUDE (integral WITH =)
+ );
+ DEBUG: building TOAST index for table "t"
+ NOTICE: CREATE TABLE / UNIQUE will create implicit index "t_rational_key" for table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ NOTICE: CREATE TABLE / EXCLUDE will create implicit index "t_integral_excl" for table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ CREATE INDEX ON t USING gin (strarr);
+ DEBUG: building index "t_strarr_idx" on table "t"
+ INSERT INTO t VALUES (1, 0.12, '', '{ab,cd}'), (2, 1.12, '', '{ef,gh}');
+ -- Comments "rewrite", "verify" and "noop" signify whether ATRewriteTables
+ -- rewrites, scans or does nothing to the table proper. An "-e" suffix denotes
+ -- an error outcome.
+ ALTER TABLE t ALTER integral TYPE trickint; -- verify-e
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: verifying table "t"
+ ERROR: check constraint "t_integral_check" is violated by some row
+ ALTER TABLE t DROP CONSTRAINT t_integral_check;
+ ALTER TABLE t ALTER integral TYPE abstime USING integral::abstime; -- noop
+ DEBUG: building index "t_integral_excl" on table "t"
+ ALTER TABLE t ALTER integral TYPE oid USING integral::int4; -- noop
+ DEBUG: building index "t_integral_excl" on table "t"
+ ALTER TABLE t ALTER integral TYPE regtype; -- noop
+ DEBUG: building index "t_integral_excl" on table "t"
+ ALTER TABLE t ALTER rational TYPE numeric(7,4); -- verify
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ DEBUG: validating foreign key constraint "t_rational_fkey"
+ ALTER TABLE t ALTER rational TYPE numeric(8,4); -- noop
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ DEBUG: validating foreign key constraint "t_rational_fkey"
+ ALTER TABLE t ALTER rational TYPE numeric(8,1); -- rewrite-e
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ DEBUG: validating foreign key constraint "t_rational_fkey"
+ ERROR: insert or update on table "t" violates foreign key constraint "t_rational_fkey"
+ DETAIL: Key (rational)=(0.1) is not present in table "parent".
+ ALTER TABLE t ALTER string TYPE varchar(6); -- noop
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ ALTER TABLE t ALTER string TYPE lendom; -- noop
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ ALTER TABLE t ALTER string TYPE xml USING string::xml; -- verify
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ ALTER TABLE t ALTER string TYPE checkdom; -- verify
+ DEBUG: verifying table "t"
+ ALTER TABLE t ALTER string TYPE text USING 'foo'::varchar; -- rewrite
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ ALTER TABLE t ALTER strarr TYPE varchar(4)[]; -- noop
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ ALTER TABLE t ADD CONSTRAINT u0 UNIQUE (integral), -- build index exactly once
+ ALTER integral TYPE int8; -- rewrite
+ NOTICE: ALTER TABLE / ADD UNIQUE will create implicit index "u0" for table "t"
+ DEBUG: building TOAST index for table "t"
+ DEBUG: rewriting table "t"
+ DEBUG: building index "t_rational_key" on table "t"
+ DEBUG: building index "t_strarr_idx" on table "t"
+ DEBUG: building index "t_integral_excl" on table "t"
+ DEBUG: building index "u0" on table "t"
+ -- Data and catalog end state. We omit the columns that bear unstable OIDs.
+ SELECT * FROM t ORDER BY 1;
+ integral | rational | string | strarr
+ ----------+----------+--------+---------
+ 1 | 0.1200 | foo | {ab,cd}
+ 2 | 1.1200 | foo | {ef,gh}
+ (2 rows)
+
+ SELECT relname, indclass FROM pg_index JOIN pg_class c ON c.oid = indexrelid
+ WHERE indrelid = 't'::regclass ORDER BY 1;
+ relname | indclass
+ -----------------+----------
+ t_integral_excl | 10029
+ t_rational_key | 10037
+ t_strarr_idx | 10103
+ u0 | 10029
+ (4 rows)
+
+ SELECT relname, attname, atttypid, atttypmod
+ FROM pg_attribute JOIN pg_class c ON c.oid = attrelid
+ WHERE attnum > 0 AND
+ attrelid IN (SELECT indexrelid FROM pg_index WHERE indrelid = 't'::regclass)
+ ORDER BY 1, 2;
+ relname | attname | atttypid | atttypmod
+ -----------------+----------+----------+-----------
+ t_integral_excl | integral | 20 | -1
+ t_rational_key | rational | 1700 | 524296
+ t_strarr_idx | strarr | 1043 | -1
+ u0 | integral | 20 | -1
+ (4 rows)
+
+ -- Done. Retain the table under a less-generic name.
+ ALTER TABLE t RENAME TO alter_type_test;
+ RESET client_min_messages;
+ --
-- lock levels
--
drop type lockmodes;
diff --git a/src/test/regress/expected/big_alternew file mode 100644
index 0000000..1609c01
diff --git a/src/test/regress/sql/alter_table.sql b/index cfbfbb6..2525fe0 100644
*** a/src/test/regress/sql/alter_table.sql
--- b/src/test/regress/sql/alter_table.sql
***************
*** 1098,1103 **** create table tab2 (x int, y tab1);
--- 1098,1165 ----
alter table tab1 alter column b type varchar; -- fails
--
+ -- ALTER COLUMN ... SET DATA TYPE optimizations
+ --
+ SET client_min_messages = debug1; -- Track rewrites.
+
+ -- Model a type change that throws the semantics of dependent expressions.
+ CREATE DOMAIN trickint AS int;
+ CREATE FUNCTION touchy_f(trickint) RETURNS int4 LANGUAGE sql AS 'SELECT 100';
+ CREATE FUNCTION touchy_f(int4) RETURNS int4 LANGUAGE sql AS 'SELECT $1';
+ CREATE DOMAIN lendom AS varchar(8);
+ CREATE DOMAIN checkdom AS text CHECK (VALUE LIKE '<%');
+
+ CREATE TABLE parent (keycol numeric PRIMARY KEY);
+ INSERT INTO parent VALUES (0.12), (1.12);
+
+ CREATE TABLE t (
+ integral int4 NOT NULL,
+ rational numeric(9,4) UNIQUE NOT NULL REFERENCES parent,
+ string varchar(4) NOT NULL,
+ strarr varchar(2)[] NOT NULL,
+ CHECK (touchy_f(integral) < 10),
+ EXCLUDE (integral WITH =)
+ );
+ CREATE INDEX ON t USING gin (strarr);
+ INSERT INTO t VALUES (1, 0.12, '', '{ab,cd}'), (2, 1.12, '', '{ef,gh}');
+
+ -- Comments "rewrite", "verify" and "noop" signify whether ATRewriteTables
+ -- rewrites, scans or does nothing to the table proper. An "-e" suffix denotes
+ -- an error outcome.
+ ALTER TABLE t ALTER integral TYPE trickint; -- verify-e
+ ALTER TABLE t DROP CONSTRAINT t_integral_check;
+ ALTER TABLE t ALTER integral TYPE abstime USING integral::abstime; -- noop
+ ALTER TABLE t ALTER integral TYPE oid USING integral::int4; -- noop
+ ALTER TABLE t ALTER integral TYPE regtype; -- noop
+ ALTER TABLE t ALTER rational TYPE numeric(7,4); -- verify
+ ALTER TABLE t ALTER rational TYPE numeric(8,4); -- noop
+ ALTER TABLE t ALTER rational TYPE numeric(8,1); -- rewrite-e
+ ALTER TABLE t ALTER string TYPE varchar(6); -- noop
+ ALTER TABLE t ALTER string TYPE lendom; -- noop
+ ALTER TABLE t ALTER string TYPE xml USING string::xml; -- verify
+ ALTER TABLE t ALTER string TYPE checkdom; -- verify
+ ALTER TABLE t ALTER string TYPE text USING 'foo'::varchar; -- rewrite
+ ALTER TABLE t ALTER strarr TYPE varchar(4)[]; -- noop
+ ALTER TABLE t ADD CONSTRAINT u0 UNIQUE (integral), -- build index exactly once
+ ALTER integral TYPE int8; -- rewrite
+
+ -- Data and catalog end state. We omit the columns that bear unstable OIDs.
+ SELECT * FROM t ORDER BY 1;
+
+ SELECT relname, indclass FROM pg_index JOIN pg_class c ON c.oid = indexrelid
+ WHERE indrelid = 't'::regclass ORDER BY 1;
+
+ SELECT relname, attname, atttypid, atttypmod
+ FROM pg_attribute JOIN pg_class c ON c.oid = attrelid
+ WHERE attnum > 0 AND
+ attrelid IN (SELECT indexrelid FROM pg_index WHERE indrelid = 't'::regclass)
+ ORDER BY 1, 2;
+
+ -- Done. Retain the table under a less-generic name.
+ ALTER TABLE t RENAME TO alter_type_test;
+ RESET client_min_messages;
+
+ --
-- lock levels
--
drop type lockmodes;
diff --git a/src/test/regress/sql/big_alternew file mode 100644
index 0000000..3824d96