*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.261 2008/07/16 19:33:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.273 2008/12/13 19:13:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
+#include "catalog/storage.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/defrem.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
char fires_when);
static void ATExecAddInherit(Relation rel, RangeVar *parent);
static void ATExecDropInherit(Relation rel, RangeVar *parent);
-static void copy_relation_data(Relation rel, SMgrRelation dst);
+static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
+ ForkNumber forkNum, bool istemp);
/* ----------------------------------------------------------------
static void
truncate_check_rel(Relation rel)
{
+ AclResult aclresult;
+
/* Only allow truncate on regular tables */
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
RelationGetRelationName(rel))));
/* Permissions checks */
- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
+ ACL_TRUNCATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
if (!allowSystemTableMods && IsSystemRelation(rel))
{
TupleDesc desc = RelationGetDescr(inhRelation);
Datum datum[Natts_pg_inherits];
- char nullarr[Natts_pg_inherits];
+ bool nullarr[Natts_pg_inherits];
ObjectAddress childobject,
parentobject;
HeapTuple tuple;
datum[1] = ObjectIdGetDatum(parentOid); /* inhparent */
datum[2] = Int16GetDatum(seqNumber); /* inhseqno */
- nullarr[0] = ' ';
- nullarr[1] = ' ';
- nullarr[2] = ' ';
+ nullarr[0] = false;
+ nullarr[1] = false;
+ nullarr[2] = false;
- tuple = heap_formtuple(desc, datum, nullarr);
+ tuple = heap_form_tuple(desc, datum, nullarr);
simple_heap_insert(inhRelation, tuple);
ATPrepAddColumn(wqueue, rel, recurse, cmd);
pass = AT_PASS_ADD_COL;
break;
+ case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
+ ATSimplePermissions(rel, true);
+ /* Performs own recursion */
+ ATPrepAddColumn(wqueue, rel, recurse, cmd);
+ pass = AT_PASS_ADD_COL;
+ break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
/*
switch (cmd->subtype)
{
case AT_AddColumn: /* ADD COLUMN */
+ case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def);
break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
Relation pgclass,
attrdesc;
HeapTuple reltup;
- HeapTuple attributeTuple;
- Form_pg_attribute attribute;
- FormData_pg_attribute attributeD;
+ FormData_pg_attribute attribute;
int i;
int minattnum,
maxatts;
+ char relkind;
HeapTuple typeTuple;
Oid typeOid;
int32 typmod;
colDef->colname, RelationGetRelationName(rel))));
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
+ relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
maxatts = minattnum + 1;
if (maxatts > MaxHeapAttributeNumber)
ereport(ERROR,
/* make sure datatype is legal for a column */
CheckAttributeType(colDef->colname, typeOid);
- attributeTuple = heap_addheader(Natts_pg_attribute,
- false,
- ATTRIBUTE_TUPLE_SIZE,
- (void *) &attributeD);
-
- attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
-
- attribute->attrelid = myrelid;
- namestrcpy(&(attribute->attname), colDef->colname);
- attribute->atttypid = typeOid;
- attribute->attstattarget = -1;
- attribute->attlen = tform->typlen;
- attribute->attcacheoff = -1;
- attribute->atttypmod = typmod;
- attribute->attnum = i;
- attribute->attbyval = tform->typbyval;
- attribute->attndims = list_length(colDef->typename->arrayBounds);
- attribute->attstorage = tform->typstorage;
- attribute->attalign = tform->typalign;
- attribute->attnotnull = colDef->is_not_null;
- attribute->atthasdef = false;
- attribute->attisdropped = false;
- attribute->attislocal = colDef->is_local;
- attribute->attinhcount = colDef->inhcount;
+ attribute.attrelid = myrelid;
+ namestrcpy(&(attribute.attname), colDef->colname);
+ attribute.atttypid = typeOid;
+ attribute.attstattarget = -1;
+ attribute.attlen = tform->typlen;
+ attribute.attcacheoff = -1;
+ attribute.atttypmod = typmod;
+ attribute.attnum = i;
+ attribute.attbyval = tform->typbyval;
+ attribute.attndims = list_length(colDef->typename->arrayBounds);
+ attribute.attstorage = tform->typstorage;
+ attribute.attalign = tform->typalign;
+ attribute.attnotnull = colDef->is_not_null;
+ attribute.atthasdef = false;
+ attribute.attisdropped = false;
+ attribute.attislocal = colDef->is_local;
+ attribute.attinhcount = colDef->inhcount;
ReleaseSysCache(typeTuple);
- simple_heap_insert(attrdesc, attributeTuple);
-
- /* Update indexes on pg_attribute */
- CatalogUpdateIndexes(attrdesc, attributeTuple);
+ InsertPgAttributeTuple(attrdesc, &attribute, NULL);
heap_close(attrdesc, RowExclusiveLock);
RawColumnDefault *rawEnt;
rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
- rawEnt->attnum = attribute->attnum;
+ rawEnt->attnum = attribute.attnum;
rawEnt->raw_default = copyObject(colDef->raw_default);
/*
* Note: we use build_column_default, and not just the cooked default
* returned by AddRelationNewConstraints, so that the right thing happens
* when a datatype's default applies.
+ *
+ * We skip this logic completely for views.
*/
- defval = (Expr *) build_column_default(rel, attribute->attnum);
+ if (relkind != RELKIND_VIEW) {
+ defval = (Expr *) build_column_default(rel, attribute.attnum);
- if (!defval && GetDomainConstraints(typeOid) != NIL)
- {
- Oid baseTypeId;
- int32 baseTypeMod;
-
- baseTypeMod = typmod;
- baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
- defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod);
- defval = (Expr *) coerce_to_target_type(NULL,
- (Node *) defval,
- baseTypeId,
- typeOid,
- typmod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST);
- if (defval == NULL) /* should not happen */
- elog(ERROR, "failed to coerce base type to domain");
- }
+ if (!defval && GetDomainConstraints(typeOid) != NIL)
+ {
+ Oid baseTypeId;
+ int32 baseTypeMod;
+
+ baseTypeMod = typmod;
+ baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
+ defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod);
+ defval = (Expr *) coerce_to_target_type(NULL,
+ (Node *) defval,
+ baseTypeId,
+ typeOid,
+ typmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (defval == NULL) /* should not happen */
+ elog(ERROR, "failed to coerce base type to domain");
+ }
- if (defval)
- {
- NewColumnValue *newval;
+ if (defval)
+ {
+ NewColumnValue *newval;
- newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
- newval->attnum = attribute->attnum;
- newval->expr = defval;
+ newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
+ newval->attnum = attribute.attnum;
+ newval->expr = defval;
- tab->newvals = lappend(tab->newvals, newval);
- }
+ tab->newvals = lappend(tab->newvals, newval);
+ }
- /*
- * If the new column is NOT NULL, tell Phase 3 it needs to test that.
- */
- tab->new_notnull |= colDef->is_not_null;
+ /*
+ * If the new column is NOT NULL, tell Phase 3 it needs to test that.
+ */
+ tab->new_notnull |= colDef->is_not_null;
+ }
/*
* Add needed dependency entries for the new column.
*/
- add_column_datatype_dependency(myrelid, i, attribute->atttypid);
+ add_column_datatype_dependency(myrelid, i, attribute.atttypid);
}
/*
errmsg("statistics target %d is too low",
newtarget)));
}
- else if (newtarget > 1000)
+ else if (newtarget > 10000)
{
- newtarget = 1000;
+ newtarget = 10000;
ereport(WARNING,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("lowering statistics target to %d",
* Reconstruct a RangeVar for my relation (not passed in, unfortunately).
*/
myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
- pstrdup(RelationGetRelationName(rel)));
+ pstrdup(RelationGetRelationName(rel)),
+ -1);
/* Make changes-so-far visible */
CommandCounterIncrement();
transform, exprType(transform),
targettype, targettypmod,
COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST,
+ -1);
if (transform == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("column \"%s\" cannot be cast to type \"%s\"",
- colName, TypeNameToString(typename))));
+ errmsg("column \"%s\" cannot be cast to type %s",
+ colName, format_type_be(targettype))));
/*
* Add a work queue item to make ATRewriteTable update the column
defaultexpr, exprType(defaultexpr),
targettype, targettypmod,
COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST);
+ COERCE_IMPLICIT_CAST,
+ -1);
if (defaultexpr == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("default for column \"%s\" cannot be cast to type \"%s\"",
- colName, TypeNameToString(typename))));
+ errmsg("default for column \"%s\" cannot be cast to type %s",
+ colName, format_type_be(targettype))));
}
else
defaultexpr = NULL;
if (tuple_class->relowner != newOwnerId)
{
Datum repl_val[Natts_pg_class];
- char repl_null[Natts_pg_class];
- char repl_repl[Natts_pg_class];
+ bool repl_null[Natts_pg_class];
+ bool repl_repl[Natts_pg_class];
Acl *newAcl;
Datum aclDatum;
bool isNull;
}
}
- memset(repl_null, ' ', sizeof(repl_null));
- memset(repl_repl, ' ', sizeof(repl_repl));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
- repl_repl[Anum_pg_class_relowner - 1] = 'r';
+ repl_repl[Anum_pg_class_relowner - 1] = true;
repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
/*
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
tuple_class->relowner, newOwnerId);
- repl_repl[Anum_pg_class_relacl - 1] = 'r';
+ repl_repl[Anum_pg_class_relacl - 1] = true;
repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
}
- newtuple = heap_modifytuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
simple_heap_update(class_rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(class_rel, newtuple);
bool isnull;
Datum newOptions;
Datum repl_val[Natts_pg_class];
- char repl_null[Natts_pg_class];
- char repl_repl[Natts_pg_class];
+ bool repl_null[Natts_pg_class];
+ bool repl_repl[Natts_pg_class];
if (defList == NIL)
return; /* nothing to do */
* propagated into relcaches during post-commit cache inval.
*/
memset(repl_val, 0, sizeof(repl_val));
- memset(repl_null, ' ', sizeof(repl_null));
- memset(repl_repl, ' ', sizeof(repl_repl));
+ memset(repl_null, false, sizeof(repl_null));
+ memset(repl_repl, false, sizeof(repl_repl));
if (newOptions != (Datum) 0)
repl_val[Anum_pg_class_reloptions - 1] = newOptions;
else
- repl_null[Anum_pg_class_reloptions - 1] = 'n';
+ repl_null[Anum_pg_class_reloptions - 1] = true;
- repl_repl[Anum_pg_class_reloptions - 1] = 'r';
+ repl_repl[Anum_pg_class_reloptions - 1] = true;
- newtuple = heap_modifytuple(tuple, RelationGetDescr(pgclass),
+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
repl_val, repl_null, repl_repl);
simple_heap_update(pgclass, &newtuple->t_self, newtuple);
Oid oldTableSpace;
Oid reltoastrelid;
Oid reltoastidxid;
+ Oid newrelfilenode;
RelFileNode newrnode;
SMgrRelation dstrel;
Relation pg_class;
HeapTuple tuple;
Form_pg_class rd_rel;
+ ForkNumber forkNum;
/*
* Need lock here in case we are recursing to toast table or index
elog(ERROR, "cache lookup failed for relation %u", tableOid);
rd_rel = (Form_pg_class) GETSTRUCT(tuple);
- /* create another storage file. Is it a little ugly ? */
- /* NOTE: any conflict in relfilenode value will be caught here */
+ /*
+ * Since we copy the file directly without looking at the shared buffers,
+ * we'd better first flush out any pages of the source relation that are
+ * in shared buffers. We assume no new changes will be made while we are
+ * holding exclusive lock on the rel.
+ */
+ FlushRelationBuffers(rel);
+
+ /*
+ * Relfilenodes are not unique across tablespaces, so we need to allocate
+ * a new one in the new tablespace.
+ */
+ newrelfilenode = GetNewRelFileNode(newTableSpace,
+ rel->rd_rel->relisshared,
+ NULL);
+
+ /* Open old and new relation */
newrnode = rel->rd_node;
+ newrnode.relNode = newrelfilenode;
newrnode.spcNode = newTableSpace;
-
dstrel = smgropen(newrnode);
- smgrcreate(dstrel, rel->rd_istemp, false);
- /* copy relation data to the new physical file */
- copy_relation_data(rel, dstrel);
-
- /* schedule unlinking old physical file */
RelationOpenSmgr(rel);
- smgrscheduleunlink(rel->rd_smgr, rel->rd_istemp);
/*
- * Now drop smgr references. The source was already dropped by
- * smgrscheduleunlink.
+ * Create and copy all forks of the relation, and schedule unlinking
+ * of old physical files.
+ *
+ * NOTE: any conflict in relfilenode value will be caught in
+ * RelationCreateStorage().
*/
+ RelationCreateStorage(newrnode, rel->rd_istemp);
+
+ /* copy main fork */
+ copy_relation_data(rel->rd_smgr, dstrel, MAIN_FORKNUM, rel->rd_istemp);
+
+ /* copy those extra forks that exist */
+ for (forkNum = MAIN_FORKNUM + 1; forkNum <= MAX_FORKNUM; forkNum++)
+ {
+ if (smgrexists(rel->rd_smgr, forkNum))
+ {
+ smgrcreate(dstrel, forkNum, false);
+ copy_relation_data(rel->rd_smgr, dstrel, forkNum, rel->rd_istemp);
+ }
+ }
+
+ /* drop old relation, and close new one */
+ RelationDropStorage(rel);
smgrclose(dstrel);
/* update the pg_class row */
rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
+ rd_rel->relfilenode = newrelfilenode;
simple_heap_update(pg_class, &tuple->t_self, tuple);
CatalogUpdateIndexes(pg_class, tuple);
* Copy data, block by block
*/
static void
-copy_relation_data(Relation rel, SMgrRelation dst)
+copy_relation_data(SMgrRelation src, SMgrRelation dst,
+ ForkNumber forkNum, bool istemp)
{
- SMgrRelation src;
bool use_wal;
BlockNumber nblocks;
BlockNumber blkno;
char buf[BLCKSZ];
Page page = (Page) buf;
- /*
- * Since we copy the file directly without looking at the shared buffers,
- * we'd better first flush out any pages of the source relation that are
- * in shared buffers. We assume no new changes will be made while we are
- * holding exclusive lock on the rel.
- */
- FlushRelationBuffers(rel);
-
/*
* We need to log the copied data in WAL iff WAL archiving is enabled AND
* it's not a temp rel.
*/
- use_wal = XLogArchivingActive() && !rel->rd_istemp;
+ use_wal = XLogArchivingActive() && !istemp;
- nblocks = RelationGetNumberOfBlocks(rel);
- /* RelationGetNumberOfBlocks will certainly have opened rd_smgr */
- src = rel->rd_smgr;
+ nblocks = smgrnblocks(src, forkNum);
for (blkno = 0; blkno < nblocks; blkno++)
{
- smgrread(src, blkno, buf);
+ smgrread(src, forkNum, blkno, buf);
/* XLOG stuff */
if (use_wal)
- log_newpage(&dst->smgr_rnode, blkno, page);
+ log_newpage(&dst->smgr_rnode, forkNum, blkno, page);
/*
* Now write the page. We say isTemp = true even if it's not a temp
* rel, because there's no need for smgr to schedule an fsync for this
* write; we'll do it ourselves below.
*/
- smgrextend(dst, blkno, buf, true);
+ smgrextend(dst, forkNum, blkno, buf, true);
}
/*
* wouldn't replay our earlier WAL entries. If we do not fsync those pages
* here, they might still not be on disk when the crash occurs.
*/
- if (!rel->rd_istemp)
- smgrimmedsync(dst);
+ if (!istemp)
+ smgrimmedsync(dst, forkNum);
}
/*