diff options
author | Andrew Dunstan <andrew@dunslane.net> | 2018-03-28 10:43:52 +1030 |
---|---|---|
committer | Andrew Dunstan <andrew@dunslane.net> | 2018-03-28 10:43:52 +1030 |
commit | 16828d5c0273b4fe5f10f42588005f16b415b2d8 (patch) | |
tree | bd7f858e309016473b7dac879f74ebe954fd8dad /src/backend/executor | |
parent | ef1978d6ed1e4defe18d250226460409e6cd5447 (diff) | |
download | postgresql-16828d5c0273b4fe5f10f42588005f16b415b2d8.tar.gz postgresql-16828d5c0273b4fe5f10f42588005f16b415b2d8.zip |
Fast ALTER TABLE ADD COLUMN with a non-NULL default
Currently adding a column to a table with a non-NULL default results in
a rewrite of the table. For large tables this can be both expensive and
disruptive. This patch removes the need for the rewrite as long as the
default value is not volatile. The default expression is evaluated at
the time of the ALTER TABLE and the result stored in a new column
(attmissingval) in pg_attribute, and a new column (atthasmissing) is set
to true. Any existing row when fetched will be supplied with the
attmissingval. New rows will have the supplied value or the default and
so will never need the attmissingval.
Any time the table is rewritten all the atthasmissing and attmissingval
settings for the attributes are cleared, as they are no longer needed.
The most visible code change from this is in heap_attisnull, which
acquires a third TupleDesc argument, allowing it to detect a missing
value if there is one. In many cases where it is known that there will
not be any (e.g. catalog relations) NULL can be passed for this
argument.
Andrew Dunstan, heavily modified from an original patch from Serge
Rielau.
Reviewed by Tom Lane, Andres Freund, Tomas Vondra and David Rowley.
Discussion: https://postgr.es/m/31e2e921-7002-4c27-59f5-51f08404c858@2ndQuadrant.com
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execExprInterp.c | 2 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 13 | ||||
-rw-r--r-- | src/backend/executor/execTuples.c | 28 | ||||
-rw-r--r-- | src/backend/executor/execUtils.c | 2 |
4 files changed, 40 insertions, 5 deletions
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index f7bcf6370b5..e530b262dae 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2505,7 +2505,7 @@ ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, /* ignore dropped columns */ if (TupleDescAttr(tupDesc, att - 1)->attisdropped) continue; - if (heap_attisnull(&tmptup, att)) + if (heap_attisnull(&tmptup, att, tupDesc)) { /* null field disproves IS NOT NULL */ if (!checkisnull) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 68f6450ee64..9a107aba561 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2980,8 +2980,17 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) false, NULL)) elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); - /* successful, copy tuple */ - copyTuple = heap_copytuple(&tuple); + if (HeapTupleHeaderGetNatts(tuple.t_data) < + RelationGetDescr(erm->relation)->natts) + { + copyTuple = heap_expand_tuple(&tuple, + RelationGetDescr(erm->relation)); + } + else + { + /* successful, copy tuple */ + copyTuple = heap_copytuple(&tuple); + } ReleaseBuffer(buffer); } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index acd1b97b0e6..78cfcadea03 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -625,7 +625,15 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot) if (slot->tts_mintuple) return heap_copy_minimal_tuple(slot->tts_mintuple); if (slot->tts_tuple) - return minimal_tuple_from_heap_tuple(slot->tts_tuple); + { + if (TTS_HAS_PHYSICAL_TUPLE(slot) && + HeapTupleHeaderGetNatts(slot->tts_tuple->t_data) + < slot->tts_tupleDescriptor->natts) + return minimal_expand_tuple(slot->tts_tuple, + slot->tts_tupleDescriptor); + else + return minimal_tuple_from_heap_tuple(slot->tts_tuple); + } /* * Otherwise we need to build a tuple from the Datum array. @@ -663,7 +671,23 @@ ExecFetchSlotTuple(TupleTableSlot *slot) * If we have a regular physical tuple then just return it. */ if (TTS_HAS_PHYSICAL_TUPLE(slot)) - return slot->tts_tuple; + { + if (HeapTupleHeaderGetNatts(slot->tts_tuple->t_data) < + slot->tts_tupleDescriptor->natts) + { + MemoryContext oldContext = MemoryContextSwitchTo(slot->tts_mcxt); + + slot->tts_tuple = heap_expand_tuple(slot->tts_tuple, + slot->tts_tupleDescriptor); + slot->tts_shouldFree = true; + MemoryContextSwitchTo(oldContext); + return slot->tts_tuple; + } + else + { + return slot->tts_tuple; + } + } /* * Otherwise materialize the slot... diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 14b07b5d449..b963cae730c 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -511,6 +511,8 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc return false; /* out of order */ if (att_tup->attisdropped) return false; /* table contains dropped columns */ + if (att_tup->atthasmissing) + return false; /* table contains cols with missing values */ /* * Note: usually the Var's type should match the tupdesc exactly, but |