mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +03:00
Standard pgindent run for 8.1.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.29 2005/08/22 17:38:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.30 2005/10/15 02:49:14 momjian Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -72,8 +72,8 @@ DefineAggregate(List *names, List *parameters)
|
||||
DefElem *defel = (DefElem *) lfirst(pl);
|
||||
|
||||
/*
|
||||
* sfunc1, stype1, and initcond1 are accepted as obsolete
|
||||
* spellings for sfunc, stype, initcond.
|
||||
* sfunc1, stype1, and initcond1 are accepted as obsolete spellings
|
||||
* for sfunc, stype, initcond.
|
||||
*/
|
||||
if (pg_strcasecmp(defel->defname, "sfunc") == 0)
|
||||
transfuncName = defGetQualifiedName(defel);
|
||||
@@ -119,11 +119,11 @@ DefineAggregate(List *names, List *parameters)
|
||||
/*
|
||||
* look up the aggregate's base type (input datatype) and transtype.
|
||||
*
|
||||
* We have historically allowed the command to look like basetype = 'ANY'
|
||||
* so we must do a case-insensitive comparison for the name ANY. Ugh.
|
||||
* We have historically allowed the command to look like basetype = 'ANY' so
|
||||
* we must do a case-insensitive comparison for the name ANY. Ugh.
|
||||
*
|
||||
* basetype can be a pseudo-type, but transtype can't, since we need to
|
||||
* be able to store values of the transtype. However, we can allow
|
||||
* basetype can be a pseudo-type, but transtype can't, since we need to be
|
||||
* able to store values of the transtype. However, we can allow
|
||||
* polymorphic transtype in some cases (AggregateCreate will check).
|
||||
*/
|
||||
if (pg_strcasecmp(TypeNameToString(baseType), "ANY") == 0)
|
||||
@@ -169,11 +169,11 @@ RemoveAggregate(RemoveAggrStmt *stmt)
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* if a basetype is passed in, then attempt to find an aggregate for
|
||||
* that specific type.
|
||||
* if a basetype is passed in, then attempt to find an aggregate for that
|
||||
* specific type.
|
||||
*
|
||||
* else attempt to find an aggregate with a basetype of ANYOID. This
|
||||
* means that the aggregate is to apply to all basetypes (eg, COUNT).
|
||||
* else attempt to find an aggregate with a basetype of ANYOID. This means
|
||||
* that the aggregate is to apply to all basetypes (eg, COUNT).
|
||||
*/
|
||||
if (aggType)
|
||||
basetypeID = typenameTypeId(aggType);
|
||||
@@ -193,8 +193,8 @@ RemoveAggregate(RemoveAggrStmt *stmt)
|
||||
|
||||
/* Permission check: must own agg or its namespace */
|
||||
if (!pg_proc_ownercheck(procOid, GetUserId()) &&
|
||||
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
|
||||
GetUserId()))
|
||||
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
||||
NameListToString(aggName));
|
||||
|
||||
@@ -225,10 +225,10 @@ RenameAggregate(List *name, TypeName *basetype, const char *newname)
|
||||
AclResult aclresult;
|
||||
|
||||
/*
|
||||
* if a basetype is passed in, then attempt to find an aggregate for
|
||||
* that specific type; else attempt to find an aggregate with a
|
||||
* basetype of ANYOID. This means that the aggregate applies to all
|
||||
* basetypes (eg, COUNT).
|
||||
* if a basetype is passed in, then attempt to find an aggregate for that
|
||||
* specific type; else attempt to find an aggregate with a basetype of
|
||||
* ANYOID. This means that the aggregate applies to all basetypes (eg,
|
||||
* COUNT).
|
||||
*/
|
||||
if (basetype)
|
||||
basetypeOid = typenameTypeId(basetype);
|
||||
@@ -258,16 +258,16 @@ RenameAggregate(List *name, TypeName *basetype, const char *newname)
|
||||
if (basetypeOid == ANYOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
||||
errmsg("function %s(*) already exists in schema \"%s\"",
|
||||
newname,
|
||||
get_namespace_name(namespaceOid))));
|
||||
errmsg("function %s(*) already exists in schema \"%s\"",
|
||||
newname,
|
||||
get_namespace_name(namespaceOid))));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_FUNCTION),
|
||||
errmsg("function %s already exists in schema \"%s\"",
|
||||
funcname_signature_string(newname,
|
||||
procForm->pronargs,
|
||||
procForm->proargtypes.values),
|
||||
procForm->proargtypes.values),
|
||||
get_namespace_name(namespaceOid))));
|
||||
}
|
||||
|
||||
@@ -305,10 +305,10 @@ AlterAggregateOwner(List *name, TypeName *basetype, Oid newOwnerId)
|
||||
AclResult aclresult;
|
||||
|
||||
/*
|
||||
* if a basetype is passed in, then attempt to find an aggregate for
|
||||
* that specific type; else attempt to find an aggregate with a
|
||||
* basetype of ANYOID. This means that the aggregate applies to all
|
||||
* basetypes (eg, COUNT).
|
||||
* if a basetype is passed in, then attempt to find an aggregate for that
|
||||
* specific type; else attempt to find an aggregate with a basetype of
|
||||
* ANYOID. This means that the aggregate applies to all basetypes (eg,
|
||||
* COUNT).
|
||||
*/
|
||||
if (basetype)
|
||||
basetypeOid = typenameTypeId(basetype);
|
||||
@@ -353,8 +353,7 @@ AlterAggregateOwner(List *name, TypeName *basetype, Oid newOwnerId)
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the owner --- okay to scribble on tup because it's a
|
||||
* copy
|
||||
* Modify the owner --- okay to scribble on tup because it's a copy
|
||||
*/
|
||||
procForm->proowner = newOwnerId;
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.14 2005/08/01 04:03:55 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/alter.c,v 1.15 2005/10/15 02:49:14 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -102,8 +102,8 @@ ExecRenameStmt(RenameStmt *stmt)
|
||||
{
|
||||
/*
|
||||
* RENAME TABLE requires that we (still) hold
|
||||
* CREATE rights on the containing namespace,
|
||||
* as well as ownership of the table.
|
||||
* CREATE rights on the containing namespace, as
|
||||
* well as ownership of the table.
|
||||
*/
|
||||
Oid namespaceId = get_rel_namespace(relid);
|
||||
AclResult aclresult;
|
||||
@@ -113,7 +113,7 @@ ExecRenameStmt(RenameStmt *stmt)
|
||||
ACL_CREATE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
||||
get_namespace_name(namespaceId));
|
||||
get_namespace_name(namespaceId));
|
||||
|
||||
renamerel(relid, stmt->newname);
|
||||
break;
|
||||
@@ -122,7 +122,7 @@ ExecRenameStmt(RenameStmt *stmt)
|
||||
renameatt(relid,
|
||||
stmt->subname, /* old att name */
|
||||
stmt->newname, /* new att name */
|
||||
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
|
||||
interpretInhOption(stmt->relation->inhOpt), /* recursive? */
|
||||
false); /* recursing already? */
|
||||
break;
|
||||
case OBJECT_TRIGGER:
|
||||
@@ -156,18 +156,18 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||
AlterFunctionNamespace(stmt->object, stmt->objarg,
|
||||
stmt->newschema);
|
||||
break;
|
||||
|
||||
|
||||
case OBJECT_SEQUENCE:
|
||||
case OBJECT_TABLE:
|
||||
CheckRelationOwnership(stmt->relation, true);
|
||||
AlterTableNamespace(stmt->relation, stmt->newschema);
|
||||
break;
|
||||
|
||||
|
||||
case OBJECT_TYPE:
|
||||
case OBJECT_DOMAIN:
|
||||
AlterTypeNamespace(stmt->object, stmt->newschema);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized AlterObjectSchemaStmt type: %d",
|
||||
(int) stmt->objectType);
|
||||
@@ -181,7 +181,7 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
|
||||
void
|
||||
ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
|
||||
{
|
||||
Oid newowner = get_roleid_checked(stmt->newowner);
|
||||
Oid newowner = get_roleid_checked(stmt->newowner);
|
||||
|
||||
switch (stmt->objectType)
|
||||
{
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.88 2005/07/29 19:30:03 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.89 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -119,9 +119,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
elevel = DEBUG2;
|
||||
|
||||
/*
|
||||
* Use the current context for storing analysis info. vacuum.c
|
||||
* ensures that this context will be cleared when I return, thus
|
||||
* releasing the memory allocated here.
|
||||
* Use the current context for storing analysis info. vacuum.c ensures
|
||||
* that this context will be cleared when I return, thus releasing the
|
||||
* memory allocated here.
|
||||
*/
|
||||
anl_context = CurrentMemoryContext;
|
||||
|
||||
@@ -132,8 +132,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
/*
|
||||
* Race condition -- if the pg_class tuple has gone away since the
|
||||
* last time we saw it, we don't need to process it.
|
||||
* Race condition -- if the pg_class tuple has gone away since the last
|
||||
* time we saw it, we don't need to process it.
|
||||
*/
|
||||
if (!SearchSysCacheExists(RELOID,
|
||||
ObjectIdGetDatum(relid),
|
||||
@@ -141,8 +141,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Open the class, getting only a read lock on it, and check
|
||||
* permissions. Permissions check should match vacuum's check!
|
||||
* Open the class, getting only a read lock on it, and check permissions.
|
||||
* Permissions check should match vacuum's check!
|
||||
*/
|
||||
onerel = relation_open(relid, AccessShareLock);
|
||||
|
||||
@@ -159,8 +159,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that it's a plain table; we used to do this in get_rel_oids()
|
||||
* but seems safer to check after we've locked the relation.
|
||||
* Check that it's a plain table; we used to do this in get_rel_oids() but
|
||||
* seems safer to check after we've locked the relation.
|
||||
*/
|
||||
if (onerel->rd_rel->relkind != RELKIND_RELATION)
|
||||
{
|
||||
@@ -175,10 +175,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
|
||||
/*
|
||||
* Silently ignore tables that are temp tables of other backends ---
|
||||
* trying to analyze these is rather pointless, since their contents
|
||||
* are probably not up-to-date on disk. (We don't throw a warning
|
||||
* here; it would just lead to chatter during a database-wide
|
||||
* ANALYZE.)
|
||||
* trying to analyze these is rather pointless, since their contents are
|
||||
* probably not up-to-date on disk. (We don't throw a warning here; it
|
||||
* would just lead to chatter during a database-wide ANALYZE.)
|
||||
*/
|
||||
if (isOtherTempNamespace(RelationGetNamespace(onerel)))
|
||||
{
|
||||
@@ -239,10 +238,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Open all indexes of the relation, and see if there are any
|
||||
* analyzable columns in the indexes. We do not analyze index columns
|
||||
* if there was an explicit column list in the ANALYZE command,
|
||||
* however.
|
||||
* Open all indexes of the relation, and see if there are any analyzable
|
||||
* columns in the indexes. We do not analyze index columns if there was
|
||||
* an explicit column list in the ANALYZE command, however.
|
||||
*/
|
||||
vac_open_indexes(onerel, AccessShareLock, &nindexes, &Irel);
|
||||
hasindex = (nindexes > 0);
|
||||
@@ -280,13 +278,12 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
indexpr_item = lnext(indexpr_item);
|
||||
|
||||
/*
|
||||
* Can't analyze if the opclass uses a storage
|
||||
* type different from the expression result type.
|
||||
* We'd get confused because the type shown in
|
||||
* pg_attribute for the index column doesn't match
|
||||
* what we are getting from the expression.
|
||||
* Perhaps this can be fixed someday, but for now,
|
||||
* punt.
|
||||
* Can't analyze if the opclass uses a storage type
|
||||
* different from the expression result type. We'd get
|
||||
* confused because the type shown in pg_attribute for
|
||||
* the index column doesn't match what we are getting
|
||||
* from the expression. Perhaps this can be fixed
|
||||
* someday, but for now, punt.
|
||||
*/
|
||||
if (exprType(indexkey) !=
|
||||
Irel[ind]->rd_att->attrs[i]->atttypid)
|
||||
@@ -313,13 +310,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
{
|
||||
/*
|
||||
* We report that the table is empty; this is just so that the
|
||||
* autovacuum code doesn't go nuts trying to get stats about
|
||||
* a zero-column table.
|
||||
* autovacuum code doesn't go nuts trying to get stats about a
|
||||
* zero-column table.
|
||||
*/
|
||||
if (!vacstmt->vacuum)
|
||||
pgstat_report_analyze(RelationGetRelid(onerel),
|
||||
onerel->rd_rel->relisshared,
|
||||
0, 0);
|
||||
0, 0);
|
||||
|
||||
vac_close_indexes(nindexes, Irel, AccessShareLock);
|
||||
relation_close(onerel, AccessShareLock);
|
||||
@@ -327,9 +324,9 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine how many rows we need to sample, using the worst case
|
||||
* from all analyzable columns. We use a lower bound of 100 rows to
|
||||
* avoid possible overflow in Vitter's algorithm.
|
||||
* Determine how many rows we need to sample, using the worst case from
|
||||
* all analyzable columns. We use a lower bound of 100 rows to avoid
|
||||
* possible overflow in Vitter's algorithm.
|
||||
*/
|
||||
targrows = 100;
|
||||
for (i = 0; i < attr_cnt; i++)
|
||||
@@ -356,10 +353,10 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
&totalrows, &totaldeadrows);
|
||||
|
||||
/*
|
||||
* Compute the statistics. Temporary results during the calculations
|
||||
* for each column are stored in a child context. The calc routines
|
||||
* are responsible to make sure that whatever they store into the
|
||||
* VacAttrStats structure is allocated in anl_context.
|
||||
* Compute the statistics. Temporary results during the calculations for
|
||||
* each column are stored in a child context. The calc routines are
|
||||
* responsible to make sure that whatever they store into the VacAttrStats
|
||||
* structure is allocated in anl_context.
|
||||
*/
|
||||
if (numrows > 0)
|
||||
{
|
||||
@@ -397,9 +394,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
|
||||
/*
|
||||
* Emit the completed stats rows into pg_statistic, replacing any
|
||||
* previous statistics for the target columns. (If there are
|
||||
* stats in pg_statistic for columns we didn't process, we leave
|
||||
* them alone.)
|
||||
* previous statistics for the target columns. (If there are stats in
|
||||
* pg_statistic for columns we didn't process, we leave them alone.)
|
||||
*/
|
||||
update_attstats(relid, attr_cnt, vacattrstats);
|
||||
|
||||
@@ -413,11 +409,11 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are running a standalone ANALYZE, update pages/tuples stats
|
||||
* in pg_class. We know the accurate page count from the smgr, but
|
||||
* only an approximate number of tuples; therefore, if we are part of
|
||||
* VACUUM ANALYZE do *not* overwrite the accurate count already
|
||||
* inserted by VACUUM. The same consideration applies to indexes.
|
||||
* If we are running a standalone ANALYZE, update pages/tuples stats in
|
||||
* pg_class. We know the accurate page count from the smgr, but only an
|
||||
* approximate number of tuples; therefore, if we are part of VACUUM
|
||||
* ANALYZE do *not* overwrite the accurate count already inserted by
|
||||
* VACUUM. The same consideration applies to indexes.
|
||||
*/
|
||||
if (!vacstmt->vacuum)
|
||||
{
|
||||
@@ -440,7 +436,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
/* report results to the stats collector, too */
|
||||
pgstat_report_analyze(RelationGetRelid(onerel),
|
||||
onerel->rd_rel->relisshared,
|
||||
totalrows, totaldeadrows);
|
||||
totalrows, totaldeadrows);
|
||||
}
|
||||
|
||||
/* Done with indexes */
|
||||
@@ -448,8 +444,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt)
|
||||
|
||||
/*
|
||||
* Close source relation now, but keep lock so that no one deletes it
|
||||
* before we commit. (If someone did, they'd fail to clean up the
|
||||
* entries we made in pg_statistic.)
|
||||
* before we commit. (If someone did, they'd fail to clean up the entries
|
||||
* we made in pg_statistic.)
|
||||
*/
|
||||
relation_close(onerel, NoLock);
|
||||
}
|
||||
@@ -499,8 +495,8 @@ compute_index_stats(Relation onerel, double totalrows,
|
||||
|
||||
/*
|
||||
* Need an EState for evaluation of index expressions and
|
||||
* partial-index predicates. Create it in the per-index context
|
||||
* to be sure it gets cleaned up at the bottom of the loop.
|
||||
* partial-index predicates. Create it in the per-index context to be
|
||||
* sure it gets cleaned up at the bottom of the loop.
|
||||
*/
|
||||
estate = CreateExecutorState();
|
||||
econtext = GetPerTupleExprContext(estate);
|
||||
@@ -539,8 +535,7 @@ compute_index_stats(Relation onerel, double totalrows,
|
||||
{
|
||||
/*
|
||||
* Evaluate the index row to compute expression values. We
|
||||
* could do this by hand, but FormIndexDatum is
|
||||
* convenient.
|
||||
* could do this by hand, but FormIndexDatum is convenient.
|
||||
*/
|
||||
FormIndexDatum(indexInfo,
|
||||
slot,
|
||||
@@ -564,9 +559,8 @@ compute_index_stats(Relation onerel, double totalrows,
|
||||
}
|
||||
|
||||
/*
|
||||
* Having counted the number of rows that pass the predicate in
|
||||
* the sample, we can estimate the total number of rows in the
|
||||
* index.
|
||||
* Having counted the number of rows that pass the predicate in the
|
||||
* sample, we can estimate the total number of rows in the index.
|
||||
*/
|
||||
thisdata->tupleFract = (double) numindexrows / (double) numrows;
|
||||
totalindexrows = ceil(thisdata->tupleFract * totalrows);
|
||||
@@ -644,8 +638,8 @@ examine_attribute(Relation onerel, int attnum)
|
||||
stats->tupattnum = attnum;
|
||||
|
||||
/*
|
||||
* Call the type-specific typanalyze function. If none is specified,
|
||||
* use std_typanalyze().
|
||||
* Call the type-specific typanalyze function. If none is specified, use
|
||||
* std_typanalyze().
|
||||
*/
|
||||
if (OidIsValid(stats->attrtype->typanalyze))
|
||||
ok = DatumGetBool(OidFunctionCall1(stats->attrtype->typanalyze,
|
||||
@@ -683,8 +677,8 @@ BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, int samplesize)
|
||||
bs->N = nblocks; /* measured table size */
|
||||
|
||||
/*
|
||||
* If we decide to reduce samplesize for tables that have less or not
|
||||
* much more than samplesize blocks, here is the place to do it.
|
||||
* If we decide to reduce samplesize for tables that have less or not much
|
||||
* more than samplesize blocks, here is the place to do it.
|
||||
*/
|
||||
bs->n = samplesize;
|
||||
bs->t = 0; /* blocks scanned so far */
|
||||
@@ -815,12 +809,11 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
|
||||
vacuum_delay_point();
|
||||
|
||||
/*
|
||||
* We must maintain a pin on the target page's buffer to ensure
|
||||
* that the maxoffset value stays good (else concurrent VACUUM
|
||||
* might delete tuples out from under us). Hence, pin the page
|
||||
* until we are done looking at it. We don't maintain a lock on
|
||||
* the page, so tuples could get added to it, but we ignore such
|
||||
* tuples.
|
||||
* We must maintain a pin on the target page's buffer to ensure that
|
||||
* the maxoffset value stays good (else concurrent VACUUM might delete
|
||||
* tuples out from under us). Hence, pin the page until we are done
|
||||
* looking at it. We don't maintain a lock on the page, so tuples
|
||||
* could get added to it, but we ignore such tuples.
|
||||
*/
|
||||
targbuffer = ReadBuffer(onerel, targblock);
|
||||
LockBuffer(targbuffer, BUFFER_LOCK_SHARE);
|
||||
@@ -842,24 +835,24 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
|
||||
/*
|
||||
* The first targrows live rows are simply copied into the
|
||||
* reservoir. Then we start replacing tuples in the sample
|
||||
* until we reach the end of the relation. This algorithm
|
||||
* is from Jeff Vitter's paper (see full citation below).
|
||||
* It works by repeatedly computing the number of tuples
|
||||
* to skip before selecting a tuple, which replaces a
|
||||
* randomly chosen element of the reservoir (current set
|
||||
* of tuples). At all times the reservoir is a true
|
||||
* random sample of the tuples we've passed over so far,
|
||||
* so when we fall off the end of the relation we're done.
|
||||
* until we reach the end of the relation. This algorithm is
|
||||
* from Jeff Vitter's paper (see full citation below). It
|
||||
* works by repeatedly computing the number of tuples to skip
|
||||
* before selecting a tuple, which replaces a randomly chosen
|
||||
* element of the reservoir (current set of tuples). At all
|
||||
* times the reservoir is a true random sample of the tuples
|
||||
* we've passed over so far, so when we fall off the end of
|
||||
* the relation we're done.
|
||||
*/
|
||||
if (numrows < targrows)
|
||||
rows[numrows++] = heap_copytuple(&targtuple);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* t in Vitter's paper is the number of records
|
||||
* already processed. If we need to compute a new S
|
||||
* value, we must use the not-yet-incremented value of
|
||||
* liverows as t.
|
||||
* t in Vitter's paper is the number of records already
|
||||
* processed. If we need to compute a new S value, we
|
||||
* must use the not-yet-incremented value of liverows as
|
||||
* t.
|
||||
*/
|
||||
if (rowstoskip < 0)
|
||||
rowstoskip = get_next_S(liverows, targrows, &rstate);
|
||||
@@ -867,8 +860,8 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
|
||||
if (rowstoskip <= 0)
|
||||
{
|
||||
/*
|
||||
* Found a suitable tuple, so save it, replacing
|
||||
* one old tuple at random
|
||||
* Found a suitable tuple, so save it, replacing one
|
||||
* old tuple at random
|
||||
*/
|
||||
int k = (int) (targrows * random_fract());
|
||||
|
||||
@@ -895,12 +888,12 @@ acquire_sample_rows(Relation onerel, HeapTuple *rows, int targrows,
|
||||
}
|
||||
|
||||
/*
|
||||
* If we didn't find as many tuples as we wanted then we're done. No
|
||||
* sort is needed, since they're already in order.
|
||||
* If we didn't find as many tuples as we wanted then we're done. No sort
|
||||
* is needed, since they're already in order.
|
||||
*
|
||||
* Otherwise we need to sort the collected tuples by position
|
||||
* (itempointer). It's not worth worrying about corner cases where
|
||||
* the tuples are already sorted.
|
||||
* Otherwise we need to sort the collected tuples by position (itempointer).
|
||||
* It's not worth worrying about corner cases where the tuples are already
|
||||
* sorted.
|
||||
*/
|
||||
if (numrows == targrows)
|
||||
qsort((void *) rows, numrows, sizeof(HeapTuple), compare_rows);
|
||||
@@ -1455,8 +1448,7 @@ compute_minimal_stats(VacAttrStatsP stats,
|
||||
StdAnalyzeData *mystats = (StdAnalyzeData *) stats->extra_data;
|
||||
|
||||
/*
|
||||
* We track up to 2*n values for an n-element MCV list; but at least
|
||||
* 10
|
||||
* We track up to 2*n values for an n-element MCV list; but at least 10
|
||||
*/
|
||||
track_max = 2 * num_mcv;
|
||||
if (track_max < 10)
|
||||
@@ -1488,9 +1480,9 @@ compute_minimal_stats(VacAttrStatsP stats,
|
||||
|
||||
/*
|
||||
* If it's a variable-width field, add up widths for average width
|
||||
* calculation. Note that if the value is toasted, we use the
|
||||
* toasted width. We don't bother with this calculation if it's a
|
||||
* fixed-width type.
|
||||
* calculation. Note that if the value is toasted, we use the toasted
|
||||
* width. We don't bother with this calculation if it's a fixed-width
|
||||
* type.
|
||||
*/
|
||||
if (is_varlena)
|
||||
{
|
||||
@@ -1498,10 +1490,10 @@ compute_minimal_stats(VacAttrStatsP stats,
|
||||
|
||||
/*
|
||||
* If the value is toasted, we want to detoast it just once to
|
||||
* avoid repeated detoastings and resultant excess memory
|
||||
* usage during the comparisons. Also, check to see if the
|
||||
* value is excessively wide, and if so don't detoast at all
|
||||
* --- just ignore the value.
|
||||
* avoid repeated detoastings and resultant excess memory usage
|
||||
* during the comparisons. Also, check to see if the value is
|
||||
* excessively wide, and if so don't detoast at all --- just
|
||||
* ignore the value.
|
||||
*/
|
||||
if (toast_raw_datum_size(value) > WIDTH_THRESHOLD)
|
||||
{
|
||||
@@ -1594,9 +1586,9 @@ compute_minimal_stats(VacAttrStatsP stats,
|
||||
nmultiple == track_cnt)
|
||||
{
|
||||
/*
|
||||
* Our track list includes every value in the sample, and
|
||||
* every value appeared more than once. Assume the column has
|
||||
* just these values.
|
||||
* Our track list includes every value in the sample, and every
|
||||
* value appeared more than once. Assume the column has just
|
||||
* these values.
|
||||
*/
|
||||
stats->stadistinct = track_cnt;
|
||||
}
|
||||
@@ -1641,22 +1633,22 @@ compute_minimal_stats(VacAttrStatsP stats,
|
||||
}
|
||||
|
||||
/*
|
||||
* If we estimated the number of distinct values at more than 10%
|
||||
* of the total row count (a very arbitrary limit), then assume
|
||||
* that stadistinct should scale with the row count rather than be
|
||||
* a fixed value.
|
||||
* If we estimated the number of distinct values at more than 10% of
|
||||
* the total row count (a very arbitrary limit), then assume that
|
||||
* stadistinct should scale with the row count rather than be a fixed
|
||||
* value.
|
||||
*/
|
||||
if (stats->stadistinct > 0.1 * totalrows)
|
||||
stats->stadistinct = -(stats->stadistinct / totalrows);
|
||||
|
||||
/*
|
||||
* Decide how many values are worth storing as most-common values.
|
||||
* If we are able to generate a complete MCV list (all the values
|
||||
* in the sample will fit, and we think these are all the ones in
|
||||
* the table), then do so. Otherwise, store only those values
|
||||
* that are significantly more common than the (estimated)
|
||||
* average. We set the threshold rather arbitrarily at 25% more
|
||||
* than average, with at least 2 instances in the sample.
|
||||
* Decide how many values are worth storing as most-common values. If
|
||||
* we are able to generate a complete MCV list (all the values in the
|
||||
* sample will fit, and we think these are all the ones in the table),
|
||||
* then do so. Otherwise, store only those values that are
|
||||
* significantly more common than the (estimated) average. We set the
|
||||
* threshold rather arbitrarily at 25% more than average, with at
|
||||
* least 2 instances in the sample.
|
||||
*/
|
||||
if (track_cnt < track_max && toowide_cnt == 0 &&
|
||||
stats->stadistinct > 0 &&
|
||||
@@ -1725,10 +1717,10 @@ compute_minimal_stats(VacAttrStatsP stats,
|
||||
stats->stats_valid = true;
|
||||
stats->stanullfrac = 1.0;
|
||||
if (is_varwidth)
|
||||
stats->stawidth = 0; /* "unknown" */
|
||||
stats->stawidth = 0; /* "unknown" */
|
||||
else
|
||||
stats->stawidth = stats->attrtype->typlen;
|
||||
stats->stadistinct = 0.0; /* "unknown" */
|
||||
stats->stadistinct = 0.0; /* "unknown" */
|
||||
}
|
||||
|
||||
/* We don't need to bother cleaning up any of our temporary palloc's */
|
||||
@@ -1802,9 +1794,9 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
|
||||
/*
|
||||
* If it's a variable-width field, add up widths for average width
|
||||
* calculation. Note that if the value is toasted, we use the
|
||||
* toasted width. We don't bother with this calculation if it's a
|
||||
* fixed-width type.
|
||||
* calculation. Note that if the value is toasted, we use the toasted
|
||||
* width. We don't bother with this calculation if it's a fixed-width
|
||||
* type.
|
||||
*/
|
||||
if (is_varlena)
|
||||
{
|
||||
@@ -1812,10 +1804,10 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
|
||||
/*
|
||||
* If the value is toasted, we want to detoast it just once to
|
||||
* avoid repeated detoastings and resultant excess memory
|
||||
* usage during the comparisons. Also, check to see if the
|
||||
* value is excessively wide, and if so don't detoast at all
|
||||
* --- just ignore the value.
|
||||
* avoid repeated detoastings and resultant excess memory usage
|
||||
* during the comparisons. Also, check to see if the value is
|
||||
* excessively wide, and if so don't detoast at all --- just
|
||||
* ignore the value.
|
||||
*/
|
||||
if (toast_raw_datum_size(value) > WIDTH_THRESHOLD)
|
||||
{
|
||||
@@ -1854,24 +1846,23 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
sizeof(ScalarItem), compare_scalars);
|
||||
|
||||
/*
|
||||
* Now scan the values in order, find the most common ones, and
|
||||
* also accumulate ordering-correlation statistics.
|
||||
* Now scan the values in order, find the most common ones, and also
|
||||
* accumulate ordering-correlation statistics.
|
||||
*
|
||||
* To determine which are most common, we first have to count the
|
||||
* number of duplicates of each value. The duplicates are
|
||||
* adjacent in the sorted list, so a brute-force approach is to
|
||||
* compare successive datum values until we find two that are not
|
||||
* equal. However, that requires N-1 invocations of the datum
|
||||
* comparison routine, which are completely redundant with work
|
||||
* that was done during the sort. (The sort algorithm must at
|
||||
* some point have compared each pair of items that are adjacent
|
||||
* in the sorted order; otherwise it could not know that it's
|
||||
* ordered the pair correctly.) We exploit this by having
|
||||
* To determine which are most common, we first have to count the number
|
||||
* of duplicates of each value. The duplicates are adjacent in the
|
||||
* sorted list, so a brute-force approach is to compare successive
|
||||
* datum values until we find two that are not equal. However, that
|
||||
* requires N-1 invocations of the datum comparison routine, which are
|
||||
* completely redundant with work that was done during the sort. (The
|
||||
* sort algorithm must at some point have compared each pair of items
|
||||
* that are adjacent in the sorted order; otherwise it could not know
|
||||
* that it's ordered the pair correctly.) We exploit this by having
|
||||
* compare_scalars remember the highest tupno index that each
|
||||
* ScalarItem has been found equal to. At the end of the sort, a
|
||||
* ScalarItem's tupnoLink will still point to itself if and only
|
||||
* if it is the last item of its group of duplicates (since the
|
||||
* group will be ordered by tupno).
|
||||
* ScalarItem's tupnoLink will still point to itself if and only if it
|
||||
* is the last item of its group of duplicates (since the group will
|
||||
* be ordered by tupno).
|
||||
*/
|
||||
corr_xysum = 0;
|
||||
ndistinct = 0;
|
||||
@@ -1895,9 +1886,9 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
{
|
||||
/*
|
||||
* Found a new item for the mcv list; find its
|
||||
* position, bubbling down old items if needed.
|
||||
* Loop invariant is that j points at an empty/
|
||||
* replaceable slot.
|
||||
* position, bubbling down old items if needed. Loop
|
||||
* invariant is that j points at an empty/ replaceable
|
||||
* slot.
|
||||
*/
|
||||
int j;
|
||||
|
||||
@@ -1934,8 +1925,8 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
else if (toowide_cnt == 0 && nmultiple == ndistinct)
|
||||
{
|
||||
/*
|
||||
* Every value in the sample appeared more than once. Assume
|
||||
* the column has just these values.
|
||||
* Every value in the sample appeared more than once. Assume the
|
||||
* column has just these values.
|
||||
*/
|
||||
stats->stadistinct = ndistinct;
|
||||
}
|
||||
@@ -1976,26 +1967,25 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
}
|
||||
|
||||
/*
|
||||
* If we estimated the number of distinct values at more than 10%
|
||||
* of the total row count (a very arbitrary limit), then assume
|
||||
* that stadistinct should scale with the row count rather than be
|
||||
* a fixed value.
|
||||
* If we estimated the number of distinct values at more than 10% of
|
||||
* the total row count (a very arbitrary limit), then assume that
|
||||
* stadistinct should scale with the row count rather than be a fixed
|
||||
* value.
|
||||
*/
|
||||
if (stats->stadistinct > 0.1 * totalrows)
|
||||
stats->stadistinct = -(stats->stadistinct / totalrows);
|
||||
|
||||
/*
|
||||
* Decide how many values are worth storing as most-common values.
|
||||
* If we are able to generate a complete MCV list (all the values
|
||||
* in the sample will fit, and we think these are all the ones in
|
||||
* the table), then do so. Otherwise, store only those values
|
||||
* that are significantly more common than the (estimated)
|
||||
* average. We set the threshold rather arbitrarily at 25% more
|
||||
* than average, with at least 2 instances in the sample. Also,
|
||||
* we won't suppress values that have a frequency of at least 1/K
|
||||
* where K is the intended number of histogram bins; such values
|
||||
* might otherwise cause us to emit duplicate histogram bin
|
||||
* boundaries.
|
||||
* Decide how many values are worth storing as most-common values. If
|
||||
* we are able to generate a complete MCV list (all the values in the
|
||||
* sample will fit, and we think these are all the ones in the table),
|
||||
* then do so. Otherwise, store only those values that are
|
||||
* significantly more common than the (estimated) average. We set the
|
||||
* threshold rather arbitrarily at 25% more than average, with at
|
||||
* least 2 instances in the sample. Also, we won't suppress values
|
||||
* that have a frequency of at least 1/K where K is the intended
|
||||
* number of histogram bins; such values might otherwise cause us to
|
||||
* emit duplicate histogram bin boundaries.
|
||||
*/
|
||||
if (track_cnt == ndistinct && toowide_cnt == 0 &&
|
||||
stats->stadistinct > 0 &&
|
||||
@@ -2065,9 +2055,9 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a histogram slot entry if there are at least two
|
||||
* distinct values not accounted for in the MCV list. (This
|
||||
* ensures the histogram won't collapse to empty or a singleton.)
|
||||
* Generate a histogram slot entry if there are at least two distinct
|
||||
* values not accounted for in the MCV list. (This ensures the
|
||||
* histogram won't collapse to empty or a singleton.)
|
||||
*/
|
||||
num_hist = ndistinct - num_mcv;
|
||||
if (num_hist > num_bins)
|
||||
@@ -2085,10 +2075,9 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
/*
|
||||
* Collapse out the MCV items from the values[] array.
|
||||
*
|
||||
* Note we destroy the values[] array here... but we don't need
|
||||
* it for anything more. We do, however, still need
|
||||
* values_cnt. nvals will be the number of remaining entries
|
||||
* in values[].
|
||||
* Note we destroy the values[] array here... but we don't need it
|
||||
* for anything more. We do, however, still need values_cnt.
|
||||
* nvals will be the number of remaining entries in values[].
|
||||
*/
|
||||
if (num_mcv > 0)
|
||||
{
|
||||
@@ -2193,10 +2182,10 @@ compute_scalar_stats(VacAttrStatsP stats,
|
||||
stats->stats_valid = true;
|
||||
stats->stanullfrac = 1.0;
|
||||
if (is_varwidth)
|
||||
stats->stawidth = 0; /* "unknown" */
|
||||
stats->stawidth = 0; /* "unknown" */
|
||||
else
|
||||
stats->stawidth = stats->attrtype->typlen;
|
||||
stats->stadistinct = 0.0; /* "unknown" */
|
||||
stats->stadistinct = 0.0; /* "unknown" */
|
||||
}
|
||||
|
||||
/* We don't need to bother cleaning up any of our temporary palloc's */
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.125 2005/10/06 21:30:32 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.126 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -106,8 +106,7 @@
|
||||
*/
|
||||
static List *pendingNotifies = NIL;
|
||||
|
||||
static List *upperPendingNotifies = NIL; /* list of upper-xact
|
||||
* lists */
|
||||
static List *upperPendingNotifies = NIL; /* list of upper-xact lists */
|
||||
|
||||
/*
|
||||
* State for inbound notifies consists of two flags: one saying whether
|
||||
@@ -158,8 +157,8 @@ Async_Notify(const char *relname)
|
||||
if (!AsyncExistsPendingNotify(relname))
|
||||
{
|
||||
/*
|
||||
* The name list needs to live until end of transaction, so store
|
||||
* it in the transaction context.
|
||||
* The name list needs to live until end of transaction, so store it
|
||||
* in the transaction context.
|
||||
*/
|
||||
MemoryContext oldcontext;
|
||||
|
||||
@@ -208,7 +207,7 @@ Async_Listen(const char *relname)
|
||||
Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(tuple);
|
||||
|
||||
if (listener->listenerpid == MyProcPid &&
|
||||
strncmp(NameStr(listener->relname), relname, NAMEDATALEN) == 0)
|
||||
strncmp(NameStr(listener->relname), relname, NAMEDATALEN) == 0)
|
||||
{
|
||||
alreadyListener = true;
|
||||
/* No need to scan the rest of the table */
|
||||
@@ -298,14 +297,14 @@ Async_Unlisten(const char *relname)
|
||||
Form_pg_listener listener = (Form_pg_listener) GETSTRUCT(tuple);
|
||||
|
||||
if (listener->listenerpid == MyProcPid &&
|
||||
strncmp(NameStr(listener->relname), relname, NAMEDATALEN) == 0)
|
||||
strncmp(NameStr(listener->relname), relname, NAMEDATALEN) == 0)
|
||||
{
|
||||
/* Found the matching tuple, delete it */
|
||||
simple_heap_delete(lRel, &tuple->t_self);
|
||||
|
||||
/*
|
||||
* We assume there can be only one match, so no need to scan
|
||||
* the rest of the table
|
||||
* We assume there can be only one match, so no need to scan the
|
||||
* rest of the table
|
||||
*/
|
||||
break;
|
||||
}
|
||||
@@ -387,10 +386,10 @@ static void
|
||||
Async_UnlistenOnExit(int code, Datum arg)
|
||||
{
|
||||
/*
|
||||
* We need to start/commit a transaction for the unlisten, but if
|
||||
* there is already an active transaction we had better abort that one
|
||||
* first. Otherwise we'd end up committing changes that probably
|
||||
* ought to be discarded.
|
||||
* We need to start/commit a transaction for the unlisten, but if there is
|
||||
* already an active transaction we had better abort that one first.
|
||||
* Otherwise we'd end up committing changes that probably ought to be
|
||||
* discarded.
|
||||
*/
|
||||
AbortOutOfAnyTransaction();
|
||||
/* Now we can do the unlisten */
|
||||
@@ -404,14 +403,14 @@ Async_UnlistenOnExit(int code, Datum arg)
|
||||
*--------------------------------------------------------------
|
||||
* AtPrepare_Notify
|
||||
*
|
||||
* This is called at the prepare phase of a two-phase
|
||||
* This is called at the prepare phase of a two-phase
|
||||
* transaction. Save the state for possible commit later.
|
||||
*--------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
AtPrepare_Notify(void)
|
||||
{
|
||||
ListCell *p;
|
||||
ListCell *p;
|
||||
|
||||
foreach(p, pendingNotifies)
|
||||
{
|
||||
@@ -423,8 +422,8 @@ AtPrepare_Notify(void)
|
||||
|
||||
/*
|
||||
* We can clear the state immediately, rather than needing a separate
|
||||
* PostPrepare call, because if the transaction fails we'd just
|
||||
* discard the state anyway.
|
||||
* PostPrepare call, because if the transaction fails we'd just discard
|
||||
* the state anyway.
|
||||
*/
|
||||
ClearPendingNotifies();
|
||||
}
|
||||
@@ -464,12 +463,11 @@ AtCommit_Notify(void)
|
||||
nulls[Natts_pg_listener];
|
||||
|
||||
if (pendingNotifies == NIL)
|
||||
return; /* no NOTIFY statements in this
|
||||
* transaction */
|
||||
return; /* no NOTIFY statements in this transaction */
|
||||
|
||||
/*
|
||||
* NOTIFY is disabled if not normal processing mode. This test used to
|
||||
* be in xact.c, but it seems cleaner to do it here.
|
||||
* NOTIFY is disabled if not normal processing mode. This test used to be
|
||||
* in xact.c, but it seems cleaner to do it here.
|
||||
*/
|
||||
if (!IsNormalProcessingMode())
|
||||
{
|
||||
@@ -503,10 +501,10 @@ AtCommit_Notify(void)
|
||||
if (listenerPID == MyProcPid)
|
||||
{
|
||||
/*
|
||||
* Self-notify: no need to bother with table update. Indeed,
|
||||
* we *must not* clear the notification field in this path, or
|
||||
* we could lose an outside notify, which'd be bad for
|
||||
* applications that ignore self-notify messages.
|
||||
* Self-notify: no need to bother with table update. Indeed, we
|
||||
* *must not* clear the notification field in this path, or we
|
||||
* could lose an outside notify, which'd be bad for applications
|
||||
* that ignore self-notify messages.
|
||||
*/
|
||||
|
||||
if (Trace_notify)
|
||||
@@ -521,27 +519,27 @@ AtCommit_Notify(void)
|
||||
listenerPID);
|
||||
|
||||
/*
|
||||
* If someone has already notified this listener, we don't
|
||||
* bother modifying the table, but we do still send a SIGUSR2
|
||||
* signal, just in case that backend missed the earlier signal
|
||||
* for some reason. It's OK to send the signal first, because
|
||||
* the other guy can't read pg_listener until we unlock it.
|
||||
* If someone has already notified this listener, we don't bother
|
||||
* modifying the table, but we do still send a SIGUSR2 signal,
|
||||
* just in case that backend missed the earlier signal for some
|
||||
* reason. It's OK to send the signal first, because the other
|
||||
* guy can't read pg_listener until we unlock it.
|
||||
*/
|
||||
if (kill(listenerPID, SIGUSR2) < 0)
|
||||
{
|
||||
/*
|
||||
* Get rid of pg_listener entry if it refers to a PID that
|
||||
* no longer exists. Presumably, that backend crashed
|
||||
* without deleting its pg_listener entries. This code
|
||||
* used to only delete the entry if errno==ESRCH, but as
|
||||
* far as I can see we should just do it for any failure
|
||||
* (certainly at least for EPERM too...)
|
||||
* Get rid of pg_listener entry if it refers to a PID that no
|
||||
* longer exists. Presumably, that backend crashed without
|
||||
* deleting its pg_listener entries. This code used to only
|
||||
* delete the entry if errno==ESRCH, but as far as I can see
|
||||
* we should just do it for any failure (certainly at least
|
||||
* for EPERM too...)
|
||||
*/
|
||||
simple_heap_delete(lRel, &lTuple->t_self);
|
||||
}
|
||||
else if (listener->notification == 0)
|
||||
{
|
||||
HTSU_Result result;
|
||||
HTSU_Result result;
|
||||
ItemPointerData update_ctid;
|
||||
TransactionId update_xmax;
|
||||
|
||||
@@ -551,17 +549,16 @@ AtCommit_Notify(void)
|
||||
/*
|
||||
* We cannot use simple_heap_update here because the tuple
|
||||
* could have been modified by an uncommitted transaction;
|
||||
* specifically, since UNLISTEN releases exclusive lock on
|
||||
* the table before commit, the other guy could already
|
||||
* have tried to unlisten. There are no other cases where
|
||||
* we should be able to see an uncommitted update or
|
||||
* delete. Therefore, our response to a
|
||||
* HeapTupleBeingUpdated result is just to ignore it. We
|
||||
* do *not* wait for the other guy to commit --- that
|
||||
* would risk deadlock, and we don't want to block while
|
||||
* holding the table lock anyway for performance reasons.
|
||||
* We also ignore HeapTupleUpdated, which could occur if
|
||||
* the other guy commits between our heap_getnext and
|
||||
* specifically, since UNLISTEN releases exclusive lock on the
|
||||
* table before commit, the other guy could already have tried
|
||||
* to unlisten. There are no other cases where we should be
|
||||
* able to see an uncommitted update or delete. Therefore, our
|
||||
* response to a HeapTupleBeingUpdated result is just to
|
||||
* ignore it. We do *not* wait for the other guy to commit
|
||||
* --- that would risk deadlock, and we don't want to block
|
||||
* while holding the table lock anyway for performance
|
||||
* reasons. We also ignore HeapTupleUpdated, which could occur
|
||||
* if the other guy commits between our heap_getnext and
|
||||
* heap_update calls.
|
||||
*/
|
||||
result = heap_update(lRel, &lTuple->t_self, rTuple,
|
||||
@@ -603,10 +600,10 @@ AtCommit_Notify(void)
|
||||
|
||||
/*
|
||||
* We do NOT release the lock on pg_listener here; we need to hold it
|
||||
* until end of transaction (which is about to happen, anyway) to
|
||||
* ensure that notified backends see our tuple updates when they look.
|
||||
* Else they might disregard the signal, which would make the
|
||||
* application programmer very unhappy.
|
||||
* until end of transaction (which is about to happen, anyway) to ensure
|
||||
* that notified backends see our tuple updates when they look. Else they
|
||||
* might disregard the signal, which would make the application programmer
|
||||
* very unhappy.
|
||||
*/
|
||||
heap_close(lRel, NoLock);
|
||||
|
||||
@@ -676,8 +673,7 @@ AtSubCommit_Notify(void)
|
||||
GetCurrentTransactionNestLevel() - 2);
|
||||
|
||||
/*
|
||||
* We could try to eliminate duplicates here, but it seems not
|
||||
* worthwhile.
|
||||
* We could try to eliminate duplicates here, but it seems not worthwhile.
|
||||
*/
|
||||
pendingNotifies = list_concat(parentPendingNotifies, pendingNotifies);
|
||||
}
|
||||
@@ -695,10 +691,10 @@ AtSubAbort_Notify(void)
|
||||
* subxact are no longer interesting, and the space will be freed when
|
||||
* CurTransactionContext is recycled.
|
||||
*
|
||||
* This routine could be called more than once at a given nesting level
|
||||
* if there is trouble during subxact abort. Avoid dumping core by
|
||||
* using GetCurrentTransactionNestLevel as the indicator of how far
|
||||
* we need to prune the list.
|
||||
* This routine could be called more than once at a given nesting level if
|
||||
* there is trouble during subxact abort. Avoid dumping core by using
|
||||
* GetCurrentTransactionNestLevel as the indicator of how far we need to
|
||||
* prune the list.
|
||||
*/
|
||||
while (list_length(upperPendingNotifies) > my_level - 2)
|
||||
{
|
||||
@@ -731,9 +727,9 @@ NotifyInterruptHandler(SIGNAL_ARGS)
|
||||
|
||||
/*
|
||||
* Note: this is a SIGNAL HANDLER. You must be very wary what you do
|
||||
* here. Some helpful soul had this routine sprinkled with TPRINTFs,
|
||||
* which would likely lead to corruption of stdio buffers if they were
|
||||
* ever turned on.
|
||||
* here. Some helpful soul had this routine sprinkled with TPRINTFs, which
|
||||
* would likely lead to corruption of stdio buffers if they were ever
|
||||
* turned on.
|
||||
*/
|
||||
|
||||
/* Don't joggle the elbow of proc_exit */
|
||||
@@ -745,19 +741,18 @@ NotifyInterruptHandler(SIGNAL_ARGS)
|
||||
bool save_ImmediateInterruptOK = ImmediateInterruptOK;
|
||||
|
||||
/*
|
||||
* We may be called while ImmediateInterruptOK is true; turn it
|
||||
* off while messing with the NOTIFY state. (We would have to
|
||||
* save and restore it anyway, because PGSemaphore operations
|
||||
* inside ProcessIncomingNotify() might reset it.)
|
||||
* We may be called while ImmediateInterruptOK is true; turn it off
|
||||
* while messing with the NOTIFY state. (We would have to save and
|
||||
* restore it anyway, because PGSemaphore operations inside
|
||||
* ProcessIncomingNotify() might reset it.)
|
||||
*/
|
||||
ImmediateInterruptOK = false;
|
||||
|
||||
/*
|
||||
* I'm not sure whether some flavors of Unix might allow another
|
||||
* SIGUSR2 occurrence to recursively interrupt this routine. To
|
||||
* cope with the possibility, we do the same sort of dance that
|
||||
* EnableNotifyInterrupt must do --- see that routine for
|
||||
* comments.
|
||||
* SIGUSR2 occurrence to recursively interrupt this routine. To cope
|
||||
* with the possibility, we do the same sort of dance that
|
||||
* EnableNotifyInterrupt must do --- see that routine for comments.
|
||||
*/
|
||||
notifyInterruptEnabled = 0; /* disable any recursive signal */
|
||||
notifyInterruptOccurred = 1; /* do at least one iteration */
|
||||
@@ -781,8 +776,7 @@ NotifyInterruptHandler(SIGNAL_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore ImmediateInterruptOK, and check for interrupts if
|
||||
* needed.
|
||||
* Restore ImmediateInterruptOK, and check for interrupts if needed.
|
||||
*/
|
||||
ImmediateInterruptOK = save_ImmediateInterruptOK;
|
||||
if (save_ImmediateInterruptOK)
|
||||
@@ -791,8 +785,7 @@ NotifyInterruptHandler(SIGNAL_ARGS)
|
||||
else
|
||||
{
|
||||
/*
|
||||
* In this path it is NOT SAFE to do much of anything, except
|
||||
* this:
|
||||
* In this path it is NOT SAFE to do much of anything, except this:
|
||||
*/
|
||||
notifyInterruptOccurred = 1;
|
||||
}
|
||||
@@ -820,27 +813,25 @@ EnableNotifyInterrupt(void)
|
||||
return; /* not really idle */
|
||||
|
||||
/*
|
||||
* This code is tricky because we are communicating with a signal
|
||||
* handler that could interrupt us at any point. If we just checked
|
||||
* notifyInterruptOccurred and then set notifyInterruptEnabled, we
|
||||
* could fail to respond promptly to a signal that happens in between
|
||||
* those two steps. (A very small time window, perhaps, but Murphy's
|
||||
* Law says you can hit it...) Instead, we first set the enable flag,
|
||||
* then test the occurred flag. If we see an unserviced interrupt has
|
||||
* occurred, we re-clear the enable flag before going off to do the
|
||||
* service work. (That prevents re-entrant invocation of
|
||||
* ProcessIncomingNotify() if another interrupt occurs.) If an
|
||||
* interrupt comes in between the setting and clearing of
|
||||
* notifyInterruptEnabled, then it will have done the service work and
|
||||
* left notifyInterruptOccurred zero, so we have to check again after
|
||||
* clearing enable. The whole thing has to be in a loop in case
|
||||
* another interrupt occurs while we're servicing the first. Once we
|
||||
* get out of the loop, enable is set and we know there is no
|
||||
* unserviced interrupt.
|
||||
* This code is tricky because we are communicating with a signal handler
|
||||
* that could interrupt us at any point. If we just checked
|
||||
* notifyInterruptOccurred and then set notifyInterruptEnabled, we could
|
||||
* fail to respond promptly to a signal that happens in between those two
|
||||
* steps. (A very small time window, perhaps, but Murphy's Law says you
|
||||
* can hit it...) Instead, we first set the enable flag, then test the
|
||||
* occurred flag. If we see an unserviced interrupt has occurred, we
|
||||
* re-clear the enable flag before going off to do the service work.
|
||||
* (That prevents re-entrant invocation of ProcessIncomingNotify() if
|
||||
* another interrupt occurs.) If an interrupt comes in between the setting
|
||||
* and clearing of notifyInterruptEnabled, then it will have done the
|
||||
* service work and left notifyInterruptOccurred zero, so we have to check
|
||||
* again after clearing enable. The whole thing has to be in a loop in
|
||||
* case another interrupt occurs while we're servicing the first. Once we
|
||||
* get out of the loop, enable is set and we know there is no unserviced
|
||||
* interrupt.
|
||||
*
|
||||
* NB: an overenthusiastic optimizing compiler could easily break this
|
||||
* code. Hopefully, they all understand what "volatile" means these
|
||||
* days.
|
||||
* NB: an overenthusiastic optimizing compiler could easily break this code.
|
||||
* Hopefully, they all understand what "volatile" means these days.
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
@@ -960,8 +951,7 @@ ProcessIncomingNotify(void)
|
||||
* Rewrite the tuple with 0 in notification column.
|
||||
*
|
||||
* simple_heap_update is safe here because no one else would have
|
||||
* tried to UNLISTEN us, so there can be no uncommitted
|
||||
* changes.
|
||||
* tried to UNLISTEN us, so there can be no uncommitted changes.
|
||||
*/
|
||||
rTuple = heap_modifytuple(lTuple, tdesc, value, nulls, repl);
|
||||
simple_heap_update(lRel, &lTuple->t_self, rTuple);
|
||||
@@ -975,18 +965,17 @@ ProcessIncomingNotify(void)
|
||||
|
||||
/*
|
||||
* We do NOT release the lock on pg_listener here; we need to hold it
|
||||
* until end of transaction (which is about to happen, anyway) to
|
||||
* ensure that other backends see our tuple updates when they look.
|
||||
* Otherwise, a transaction started after this one might mistakenly
|
||||
* think it doesn't need to send this backend a new NOTIFY.
|
||||
* until end of transaction (which is about to happen, anyway) to ensure
|
||||
* that other backends see our tuple updates when they look. Otherwise, a
|
||||
* transaction started after this one might mistakenly think it doesn't
|
||||
* need to send this backend a new NOTIFY.
|
||||
*/
|
||||
heap_close(lRel, NoLock);
|
||||
|
||||
CommitTransactionCommand();
|
||||
|
||||
/*
|
||||
* Must flush the notify messages to ensure frontend gets them
|
||||
* promptly.
|
||||
* Must flush the notify messages to ensure frontend gets them promptly.
|
||||
*/
|
||||
pq_flush();
|
||||
|
||||
@@ -1022,8 +1011,7 @@ NotifyMyFrontEnd(char *relname, int32 listenerPID)
|
||||
/*
|
||||
* NOTE: we do not do pq_flush() here. For a self-notify, it will
|
||||
* happen at the end of the transaction, and for incoming notifies
|
||||
* ProcessIncomingNotify will do it after finding all the
|
||||
* notifies.
|
||||
* ProcessIncomingNotify will do it after finding all the notifies.
|
||||
*/
|
||||
}
|
||||
else
|
||||
@@ -1052,11 +1040,11 @@ static void
|
||||
ClearPendingNotifies(void)
|
||||
{
|
||||
/*
|
||||
* We used to have to explicitly deallocate the list members and
|
||||
* nodes, because they were malloc'd. Now, since we know they are
|
||||
* palloc'd in CurTransactionContext, we need not do that --- they'll
|
||||
* go away automatically at transaction exit. We need only reset the
|
||||
* list head pointer.
|
||||
* We used to have to explicitly deallocate the list members and nodes,
|
||||
* because they were malloc'd. Now, since we know they are palloc'd in
|
||||
* CurTransactionContext, we need not do that --- they'll go away
|
||||
* automatically at transaction exit. We need only reset the list head
|
||||
* pointer.
|
||||
*/
|
||||
pendingNotifies = NIL;
|
||||
}
|
||||
@@ -1071,11 +1059,10 @@ notify_twophase_postcommit(TransactionId xid, uint16 info,
|
||||
void *recdata, uint32 len)
|
||||
{
|
||||
/*
|
||||
* Set up to issue the NOTIFY at the end of my own
|
||||
* current transaction. (XXX this has some issues if my own
|
||||
* transaction later rolls back, or if there is any significant
|
||||
* delay before I commit. OK for now because we disallow
|
||||
* COMMIT PREPARED inside a transaction block.)
|
||||
* Set up to issue the NOTIFY at the end of my own current transaction.
|
||||
* (XXX this has some issues if my own transaction later rolls back, or if
|
||||
* there is any significant delay before I commit. OK for now because we
|
||||
* disallow COMMIT PREPARED inside a transaction block.)
|
||||
*/
|
||||
Async_Notify((char *) recdata);
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.139 2005/08/26 03:07:16 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.140 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -144,8 +144,8 @@ cluster(ClusterStmt *stmt)
|
||||
if (!OidIsValid(indexOid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("index \"%s\" for table \"%s\" does not exist",
|
||||
stmt->indexname, stmt->relation->relname)));
|
||||
errmsg("index \"%s\" for table \"%s\" does not exist",
|
||||
stmt->indexname, stmt->relation->relname)));
|
||||
}
|
||||
|
||||
/* All other checks are done in cluster_rel() */
|
||||
@@ -161,24 +161,24 @@ cluster(ClusterStmt *stmt)
|
||||
else
|
||||
{
|
||||
/*
|
||||
* This is the "multi relation" case. We need to cluster all
|
||||
* tables that have some index with indisclustered set.
|
||||
* This is the "multi relation" case. We need to cluster all tables
|
||||
* that have some index with indisclustered set.
|
||||
*/
|
||||
MemoryContext cluster_context;
|
||||
List *rvs;
|
||||
ListCell *rv;
|
||||
|
||||
/*
|
||||
* We cannot run this form of CLUSTER inside a user transaction
|
||||
* block; we'd be holding locks way too long.
|
||||
* We cannot run this form of CLUSTER inside a user transaction block;
|
||||
* we'd be holding locks way too long.
|
||||
*/
|
||||
PreventTransactionChain((void *) stmt, "CLUSTER");
|
||||
|
||||
/*
|
||||
* Create special memory context for cross-transaction storage.
|
||||
*
|
||||
* Since it is a child of PortalContext, it will go away even in case
|
||||
* of error.
|
||||
* Since it is a child of PortalContext, it will go away even in case of
|
||||
* error.
|
||||
*/
|
||||
cluster_context = AllocSetContextCreate(PortalContext,
|
||||
"Cluster",
|
||||
@@ -187,8 +187,8 @@ cluster(ClusterStmt *stmt)
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
/*
|
||||
* Build the list of relations to cluster. Note that this lives
|
||||
* in cluster_context.
|
||||
* Build the list of relations to cluster. Note that this lives in
|
||||
* cluster_context.
|
||||
*/
|
||||
rvs = get_tables_to_cluster(cluster_context);
|
||||
|
||||
@@ -239,12 +239,12 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
/*
|
||||
* Since we may open a new transaction for each relation, we have to
|
||||
* check that the relation still is what we think it is.
|
||||
* Since we may open a new transaction for each relation, we have to check
|
||||
* that the relation still is what we think it is.
|
||||
*
|
||||
* If this is a single-transaction CLUSTER, we can skip these tests. We
|
||||
* *must* skip the one on indisclustered since it would reject an
|
||||
* attempt to cluster a not-previously-clustered index.
|
||||
* If this is a single-transaction CLUSTER, we can skip these tests. We *must*
|
||||
* skip the one on indisclustered since it would reject an attempt to
|
||||
* cluster a not-previously-clustered index.
|
||||
*/
|
||||
if (recheck)
|
||||
{
|
||||
@@ -284,10 +284,10 @@ cluster_rel(RelToCluster *rvtc, bool recheck)
|
||||
}
|
||||
|
||||
/*
|
||||
* We grab exclusive access to the target rel and index for the
|
||||
* duration of the transaction. (This is redundant for the single-
|
||||
* transaction case, since cluster() already did it.) The index lock
|
||||
* is taken inside check_index_is_clusterable.
|
||||
* We grab exclusive access to the target rel and index for the duration
|
||||
* of the transaction. (This is redundant for the single- transaction
|
||||
* case, since cluster() already did it.) The index lock is taken inside
|
||||
* check_index_is_clusterable.
|
||||
*/
|
||||
OldHeap = heap_open(rvtc->tableOid, AccessExclusiveLock);
|
||||
|
||||
@@ -328,26 +328,26 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
|
||||
RelationGetRelationName(OldHeap))));
|
||||
|
||||
/*
|
||||
* Disallow clustering on incomplete indexes (those that might not
|
||||
* index every row of the relation). We could relax this by making a
|
||||
* separate seqscan pass over the table to copy the missing rows, but
|
||||
* that seems expensive and tedious.
|
||||
* Disallow clustering on incomplete indexes (those that might not index
|
||||
* every row of the relation). We could relax this by making a separate
|
||||
* seqscan pass over the table to copy the missing rows, but that seems
|
||||
* expensive and tedious.
|
||||
*/
|
||||
if (!heap_attisnull(OldIndex->rd_indextuple, Anum_pg_index_indpred))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot cluster on partial index \"%s\"",
|
||||
RelationGetRelationName(OldIndex))));
|
||||
|
||||
|
||||
if (!OldIndex->rd_am->amindexnulls)
|
||||
{
|
||||
AttrNumber colno;
|
||||
|
||||
/*
|
||||
* If the AM doesn't index nulls, then it's a partial index unless
|
||||
* we can prove all the rows are non-null. Note we only need look
|
||||
* at the first column; multicolumn-capable AMs are *required* to
|
||||
* index nulls in columns after the first.
|
||||
* If the AM doesn't index nulls, then it's a partial index unless we
|
||||
* can prove all the rows are non-null. Note we only need look at the
|
||||
* first column; multicolumn-capable AMs are *required* to index nulls
|
||||
* in columns after the first.
|
||||
*/
|
||||
colno = OldIndex->rd_index->indkey.values[0];
|
||||
if (colno > 0)
|
||||
@@ -358,11 +358,11 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot cluster on index \"%s\" because access method\n"
|
||||
"does not handle null values",
|
||||
RelationGetRelationName(OldIndex)),
|
||||
RelationGetRelationName(OldIndex)),
|
||||
errhint("You may be able to work around this by marking column \"%s\" NOT NULL%s",
|
||||
NameStr(OldHeap->rd_att->attrs[colno - 1]->attname),
|
||||
recheck ? ",\nor use ALTER TABLE ... SET WITHOUT CLUSTER to remove the cluster\n"
|
||||
"specification from the table." : ".")));
|
||||
NameStr(OldHeap->rd_att->attrs[colno - 1]->attname),
|
||||
recheck ? ",\nor use ALTER TABLE ... SET WITHOUT CLUSTER to remove the cluster\n"
|
||||
"specification from the table." : ".")));
|
||||
}
|
||||
else if (colno < 0)
|
||||
{
|
||||
@@ -374,15 +374,15 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot cluster on expressional index \"%s\" because its index access\n"
|
||||
"method does not handle null values",
|
||||
RelationGetRelationName(OldIndex))));
|
||||
RelationGetRelationName(OldIndex))));
|
||||
}
|
||||
|
||||
/*
|
||||
* Disallow clustering system relations. This will definitely NOT
|
||||
* work for shared relations (we have no way to update pg_class rows
|
||||
* in other databases), nor for nailed-in-cache relations (the
|
||||
* relfilenode values for those are hardwired, see relcache.c). It
|
||||
* might work for other system relations, but I ain't gonna risk it.
|
||||
* Disallow clustering system relations. This will definitely NOT work
|
||||
* for shared relations (we have no way to update pg_class rows in other
|
||||
* databases), nor for nailed-in-cache relations (the relfilenode values
|
||||
* for those are hardwired, see relcache.c). It might work for other
|
||||
* system relations, but I ain't gonna risk it.
|
||||
*/
|
||||
if (IsSystemRelation(OldHeap))
|
||||
ereport(ERROR,
|
||||
@@ -391,13 +391,13 @@ check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck)
|
||||
RelationGetRelationName(OldHeap))));
|
||||
|
||||
/*
|
||||
* Don't allow cluster on temp tables of other backends ... their
|
||||
* local buffer manager is not going to cope.
|
||||
* Don't allow cluster on temp tables of other backends ... their local
|
||||
* buffer manager is not going to cope.
|
||||
*/
|
||||
if (isOtherTempNamespace(RelationGetNamespace(OldHeap)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot cluster temporary tables of other sessions")));
|
||||
errmsg("cannot cluster temporary tables of other sessions")));
|
||||
|
||||
/* Drop relcache refcnt on OldIndex, but keep lock */
|
||||
index_close(OldIndex);
|
||||
@@ -454,8 +454,8 @@ mark_index_clustered(Relation rel, Oid indexOid)
|
||||
indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
|
||||
|
||||
/*
|
||||
* Unset the bit if set. We know it's wrong because we checked
|
||||
* this earlier.
|
||||
* Unset the bit if set. We know it's wrong because we checked this
|
||||
* earlier.
|
||||
*/
|
||||
if (indexForm->indisclustered)
|
||||
{
|
||||
@@ -503,20 +503,18 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
|
||||
heap_close(OldHeap, NoLock);
|
||||
|
||||
/*
|
||||
* Create the new heap, using a temporary name in the same namespace
|
||||
* as the existing table. NOTE: there is some risk of collision with
|
||||
* user relnames. Working around this seems more trouble than it's
|
||||
* worth; in particular, we can't create the new heap in a different
|
||||
* namespace from the old, or we will have problems with the TEMP
|
||||
* status of temp tables.
|
||||
* Create the new heap, using a temporary name in the same namespace as
|
||||
* the existing table. NOTE: there is some risk of collision with user
|
||||
* relnames. Working around this seems more trouble than it's worth; in
|
||||
* particular, we can't create the new heap in a different namespace from
|
||||
* the old, or we will have problems with the TEMP status of temp tables.
|
||||
*/
|
||||
snprintf(NewHeapName, sizeof(NewHeapName), "pg_temp_%u", tableOid);
|
||||
|
||||
OIDNewHeap = make_new_heap(tableOid, NewHeapName, tableSpace);
|
||||
|
||||
/*
|
||||
* We don't need CommandCounterIncrement() because make_new_heap did
|
||||
* it.
|
||||
* We don't need CommandCounterIncrement() because make_new_heap did it.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -546,9 +544,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
|
||||
/* performDeletion does CommandCounterIncrement at end */
|
||||
|
||||
/*
|
||||
* Rebuild each index on the relation (but not the toast table, which
|
||||
* is all-new at this point). We do not need
|
||||
* CommandCounterIncrement() because reindex_relation does it.
|
||||
* Rebuild each index on the relation (but not the toast table, which is
|
||||
* all-new at this point). We do not need CommandCounterIncrement()
|
||||
* because reindex_relation does it.
|
||||
*/
|
||||
reindex_relation(tableOid, false);
|
||||
}
|
||||
@@ -587,15 +585,15 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
|
||||
allowSystemTableMods);
|
||||
|
||||
/*
|
||||
* Advance command counter so that the newly-created relation's
|
||||
* catalog tuples will be visible to heap_open.
|
||||
* Advance command counter so that the newly-created relation's catalog
|
||||
* tuples will be visible to heap_open.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* If necessary, create a TOAST table for the new relation. Note that
|
||||
* AlterTableCreateToastTable ends with CommandCounterIncrement(), so
|
||||
* that the TOAST table will be visible for insertion.
|
||||
* AlterTableCreateToastTable ends with CommandCounterIncrement(), so that
|
||||
* the TOAST table will be visible for insertion.
|
||||
*/
|
||||
AlterTableCreateToastTable(OIDNewHeap, true);
|
||||
|
||||
@@ -629,8 +627,8 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
||||
OldIndex = index_open(OIDOldIndex);
|
||||
|
||||
/*
|
||||
* Their tuple descriptors should be exactly alike, but here we only
|
||||
* need assume that they have the same number of columns.
|
||||
* Their tuple descriptors should be exactly alike, but here we only need
|
||||
* assume that they have the same number of columns.
|
||||
*/
|
||||
oldTupDesc = RelationGetDescr(OldHeap);
|
||||
newTupDesc = RelationGetDescr(NewHeap);
|
||||
@@ -654,15 +652,14 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
||||
* We cannot simply pass the tuple to heap_insert(), for several
|
||||
* reasons:
|
||||
*
|
||||
* 1. heap_insert() will overwrite the commit-status fields of the
|
||||
* tuple it's handed. This would trash the source relation, which is
|
||||
* bad news if we abort later on. (This was a bug in releases thru
|
||||
* 7.0)
|
||||
* 1. heap_insert() will overwrite the commit-status fields of the tuple
|
||||
* it's handed. This would trash the source relation, which is bad
|
||||
* news if we abort later on. (This was a bug in releases thru 7.0)
|
||||
*
|
||||
* 2. We'd like to squeeze out the values of any dropped columns,
|
||||
* both to save space and to ensure we have no corner-case failures.
|
||||
* (It's possible for example that the new table hasn't got a TOAST
|
||||
* table and so is unable to store any large values of dropped cols.)
|
||||
* 2. We'd like to squeeze out the values of any dropped columns, both to
|
||||
* save space and to ensure we have no corner-case failures. (It's
|
||||
* possible for example that the new table hasn't got a TOAST table
|
||||
* and so is unable to store any large values of dropped cols.)
|
||||
*
|
||||
* 3. The tuple might not even be legal for the new table; this is
|
||||
* currently only known to happen as an after-effect of ALTER TABLE
|
||||
@@ -784,19 +781,18 @@ swap_relation_files(Oid r1, Oid r2)
|
||||
CatalogCloseIndexes(indstate);
|
||||
|
||||
/*
|
||||
* If we have toast tables associated with the relations being
|
||||
* swapped, change their dependency links to re-associate them with
|
||||
* their new owning relations. Otherwise the wrong one will get
|
||||
* dropped ...
|
||||
* If we have toast tables associated with the relations being swapped,
|
||||
* change their dependency links to re-associate them with their new
|
||||
* owning relations. Otherwise the wrong one will get dropped ...
|
||||
*
|
||||
* NOTE: it is possible that only one table has a toast table; this can
|
||||
* happen in CLUSTER if there were dropped columns in the old table,
|
||||
* and in ALTER TABLE when adding or changing type of columns.
|
||||
* happen in CLUSTER if there were dropped columns in the old table, and
|
||||
* in ALTER TABLE when adding or changing type of columns.
|
||||
*
|
||||
* NOTE: at present, a TOAST table's only dependency is the one on its
|
||||
* owning table. If more are ever created, we'd need to use something
|
||||
* more selective than deleteDependencyRecordsFor() to get rid of only
|
||||
* the link we want.
|
||||
* NOTE: at present, a TOAST table's only dependency is the one on its owning
|
||||
* table. If more are ever created, we'd need to use something more
|
||||
* selective than deleteDependencyRecordsFor() to get rid of only the link
|
||||
* we want.
|
||||
*/
|
||||
if (relform1->reltoastrelid || relform2->reltoastrelid)
|
||||
{
|
||||
@@ -845,16 +841,16 @@ swap_relation_files(Oid r1, Oid r2)
|
||||
|
||||
/*
|
||||
* Blow away the old relcache entries now. We need this kluge because
|
||||
* relcache.c keeps a link to the smgr relation for the physical file,
|
||||
* and that will be out of date as soon as we do
|
||||
* CommandCounterIncrement. Whichever of the rels is the second to be
|
||||
* cleared during cache invalidation will have a dangling reference to
|
||||
* an already-deleted smgr relation. Rather than trying to avoid this
|
||||
* by ordering operations just so, it's easiest to not have the
|
||||
* relcache entries there at all. (Fortunately, since one of the
|
||||
* entries is local in our transaction, it's sufficient to clear out
|
||||
* our own relcache this way; the problem cannot arise for other
|
||||
* backends when they see our update on the non-local relation.)
|
||||
* relcache.c keeps a link to the smgr relation for the physical file, and
|
||||
* that will be out of date as soon as we do CommandCounterIncrement.
|
||||
* Whichever of the rels is the second to be cleared during cache
|
||||
* invalidation will have a dangling reference to an already-deleted smgr
|
||||
* relation. Rather than trying to avoid this by ordering operations just
|
||||
* so, it's easiest to not have the relcache entries there at all.
|
||||
* (Fortunately, since one of the entries is local in our transaction,
|
||||
* it's sufficient to clear out our own relcache this way; the problem
|
||||
* cannot arise for other backends when they see our update on the
|
||||
* non-local relation.)
|
||||
*/
|
||||
RelationForgetRelation(r1);
|
||||
RelationForgetRelation(r2);
|
||||
@@ -886,9 +882,9 @@ get_tables_to_cluster(MemoryContext cluster_context)
|
||||
|
||||
/*
|
||||
* Get all indexes that have indisclustered set and are owned by
|
||||
* appropriate user. System relations or nailed-in relations cannot
|
||||
* ever have indisclustered set, because CLUSTER will refuse to set it
|
||||
* when called with one of them as argument.
|
||||
* appropriate user. System relations or nailed-in relations cannot ever
|
||||
* have indisclustered set, because CLUSTER will refuse to set it when
|
||||
* called with one of them as argument.
|
||||
*/
|
||||
indRelation = heap_open(IndexRelationId, AccessShareLock);
|
||||
ScanKeyInit(&entry,
|
||||
@@ -904,8 +900,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We have to build the list in a different memory context so it
|
||||
* will survive the cross-transaction processing
|
||||
* We have to build the list in a different memory context so it will
|
||||
* survive the cross-transaction processing
|
||||
*/
|
||||
old_context = MemoryContextSwitchTo(cluster_context);
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.83 2005/04/14 20:03:23 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.84 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -310,10 +310,9 @@ CommentRelation(int objtype, List *relname, char *comment)
|
||||
tgtrel = makeRangeVarFromNameList(relname);
|
||||
|
||||
/*
|
||||
* Open the relation. We do this mainly to acquire a lock that
|
||||
* ensures no one else drops the relation before we commit. (If they
|
||||
* did, they'd fail to remove the entry we are about to make in
|
||||
* pg_description.)
|
||||
* Open the relation. We do this mainly to acquire a lock that ensures no
|
||||
* one else drops the relation before we commit. (If they did, they'd
|
||||
* fail to remove the entry we are about to make in pg_description.)
|
||||
*/
|
||||
relation = relation_openrv(tgtrel, AccessShareLock);
|
||||
|
||||
@@ -441,17 +440,16 @@ CommentDatabase(List *qualname, char *comment)
|
||||
database = strVal(linitial(qualname));
|
||||
|
||||
/*
|
||||
* We cannot currently support cross-database comments (since other
|
||||
* DBs cannot see pg_description of this database). So, we reject
|
||||
* attempts to comment on a database other than the current one.
|
||||
* Someday this might be improved, but it would take a redesigned
|
||||
* infrastructure.
|
||||
* We cannot currently support cross-database comments (since other DBs
|
||||
* cannot see pg_description of this database). So, we reject attempts to
|
||||
* comment on a database other than the current one. Someday this might be
|
||||
* improved, but it would take a redesigned infrastructure.
|
||||
*
|
||||
* When loading a dump, we may see a COMMENT ON DATABASE for the old name
|
||||
* of the database. Erroring out would prevent pg_restore from
|
||||
* completing (which is really pg_restore's fault, but for now we will
|
||||
* work around the problem here). Consensus is that the best fix is
|
||||
* to treat wrong database name as a WARNING not an ERROR.
|
||||
* When loading a dump, we may see a COMMENT ON DATABASE for the old name of
|
||||
* the database. Erroring out would prevent pg_restore from completing
|
||||
* (which is really pg_restore's fault, but for now we will work around
|
||||
* the problem here). Consensus is that the best fix is to treat wrong
|
||||
* database name as a WARNING not an ERROR.
|
||||
*/
|
||||
|
||||
/* First get the database OID */
|
||||
@@ -467,8 +465,8 @@ CommentDatabase(List *qualname, char *comment)
|
||||
/* Only allow comments on the current database */
|
||||
if (oid != MyDatabaseId)
|
||||
{
|
||||
ereport(WARNING, /* throw just a warning so pg_restore
|
||||
* doesn't fail */
|
||||
ereport(WARNING, /* throw just a warning so pg_restore doesn't
|
||||
* fail */
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("database comments may only be applied to the current database")));
|
||||
return;
|
||||
@@ -587,8 +585,8 @@ CommentRule(List *qualname, char *comment)
|
||||
ForwardScanDirection)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("there are multiple rules named \"%s\"", rulename),
|
||||
errhint("Specify a relation name as well as a rule name.")));
|
||||
errmsg("there are multiple rules named \"%s\"", rulename),
|
||||
errhint("Specify a relation name as well as a rule name.")));
|
||||
|
||||
heap_endscan(scanDesc);
|
||||
heap_close(RewriteRelation, AccessShareLock);
|
||||
@@ -616,8 +614,8 @@ CommentRule(List *qualname, char *comment)
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("rule \"%s\" for relation \"%s\" does not exist",
|
||||
rulename, RelationGetRelationName(relation))));
|
||||
errmsg("rule \"%s\" for relation \"%s\" does not exist",
|
||||
rulename, RelationGetRelationName(relation))));
|
||||
Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
|
||||
ruleoid = HeapTupleGetOid(tuple);
|
||||
ReleaseSysCache(tuple);
|
||||
@@ -802,8 +800,8 @@ CommentTrigger(List *qualname, char *comment)
|
||||
RelationGetRelationName(relation));
|
||||
|
||||
/*
|
||||
* Fetch the trigger tuple from pg_trigger. There can be only one
|
||||
* because of the unique index.
|
||||
* Fetch the trigger tuple from pg_trigger. There can be only one because
|
||||
* of the unique index.
|
||||
*/
|
||||
pg_trigger = heap_open(TriggerRelationId, AccessShareLock);
|
||||
ScanKeyInit(&entry[0],
|
||||
@@ -879,9 +877,9 @@ CommentConstraint(List *qualname, char *comment)
|
||||
RelationGetRelationName(relation));
|
||||
|
||||
/*
|
||||
* Fetch the constraint tuple from pg_constraint. There may be more
|
||||
* than one match, because constraints are not required to have unique
|
||||
* names; if so, error out.
|
||||
* Fetch the constraint tuple from pg_constraint. There may be more than
|
||||
* one match, because constraints are not required to have unique names;
|
||||
* if so, error out.
|
||||
*/
|
||||
pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
|
||||
|
||||
@@ -902,8 +900,8 @@ CommentConstraint(List *qualname, char *comment)
|
||||
if (OidIsValid(conOid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("table \"%s\" has multiple constraints named \"%s\"",
|
||||
RelationGetRelationName(relation), conName)));
|
||||
errmsg("table \"%s\" has multiple constraints named \"%s\"",
|
||||
RelationGetRelationName(relation), conName)));
|
||||
conOid = HeapTupleGetOid(tuple);
|
||||
}
|
||||
}
|
||||
@@ -914,8 +912,8 @@ CommentConstraint(List *qualname, char *comment)
|
||||
if (!OidIsValid(conOid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("constraint \"%s\" for table \"%s\" does not exist",
|
||||
conName, RelationGetRelationName(relation))));
|
||||
errmsg("constraint \"%s\" for table \"%s\" does not exist",
|
||||
conName, RelationGetRelationName(relation))));
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(conOid, ConstraintRelationId, 0, comment);
|
||||
@@ -988,7 +986,7 @@ CommentLanguage(List *qualname, char *comment)
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to comment on procedural language")));
|
||||
errmsg("must be superuser to comment on procedural language")));
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(oid, LanguageRelationId, 0, comment);
|
||||
@@ -1111,7 +1109,7 @@ CommentLargeObject(List *qualname, char *comment)
|
||||
* strings.
|
||||
*/
|
||||
loid = DatumGetObjectId(DirectFunctionCall1(oidin,
|
||||
CStringGetDatum(strVal(node))));
|
||||
CStringGetDatum(strVal(node))));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.22 2005/08/22 17:38:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/conversioncmds.c,v 1.23 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -74,8 +74,8 @@ CreateConversionCommand(CreateConversionStmt *stmt)
|
||||
to_encoding_name)));
|
||||
|
||||
/*
|
||||
* Check the existence of the conversion function. Function name could
|
||||
* be a qualified name.
|
||||
* Check the existence of the conversion function. Function name could be
|
||||
* a qualified name.
|
||||
*/
|
||||
funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid),
|
||||
funcargs, false);
|
||||
@@ -87,8 +87,8 @@ CreateConversionCommand(CreateConversionStmt *stmt)
|
||||
NameListToString(func_name));
|
||||
|
||||
/*
|
||||
* All seem ok, go ahead (possible failure would be a duplicate
|
||||
* conversion name)
|
||||
* All seem ok, go ahead (possible failure would be a duplicate conversion
|
||||
* name)
|
||||
*/
|
||||
ConversionCreate(conversion_name, namespaceId, GetUserId(),
|
||||
from_encoding, to_encoding, funcoid, stmt->def);
|
||||
@@ -148,11 +148,11 @@ RenameConversion(List *name, const char *newname)
|
||||
0, 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("conversion \"%s\" already exists in schema \"%s\"",
|
||||
newname, get_namespace_name(namespaceOid))));
|
||||
errmsg("conversion \"%s\" already exists in schema \"%s\"",
|
||||
newname, get_namespace_name(namespaceOid))));
|
||||
|
||||
/* must be owner */
|
||||
if (!pg_conversion_ownercheck(conversionOid,GetUserId()))
|
||||
if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
|
||||
NameListToString(name));
|
||||
|
||||
@@ -210,7 +210,7 @@ AlterConversionOwner(List *name, Oid newOwnerId)
|
||||
if (!superuser())
|
||||
{
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_conversion_ownercheck(HeapTupleGetOid(tup),GetUserId()))
|
||||
if (!pg_conversion_ownercheck(HeapTupleGetOid(tup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
|
||||
NameListToString(name));
|
||||
|
||||
@@ -227,8 +227,7 @@ AlterConversionOwner(List *name, Oid newOwnerId)
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the owner --- okay to scribble on tup because it's a
|
||||
* copy
|
||||
* Modify the owner --- okay to scribble on tup because it's a copy
|
||||
*/
|
||||
convForm->conowner = newOwnerId;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.172 2005/10/10 20:02:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.173 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -202,11 +202,11 @@ createdb(const CreatedbStmt *stmt)
|
||||
datdba = GetUserId();
|
||||
|
||||
/*
|
||||
* To create a database, must have createdb privilege and must be able
|
||||
* to become the target role (this does not imply that the target role
|
||||
* itself must have createdb privilege). The latter provision guards
|
||||
* against "giveaway" attacks. Note that a superuser will always have
|
||||
* both of these privileges a fortiori.
|
||||
* To create a database, must have createdb privilege and must be able to
|
||||
* become the target role (this does not imply that the target role itself
|
||||
* must have createdb privilege). The latter provision guards against
|
||||
* "giveaway" attacks. Note that a superuser will always have both of
|
||||
* these privileges a fortiori.
|
||||
*/
|
||||
if (!have_createdb_privilege())
|
||||
ereport(ERROR,
|
||||
@@ -218,10 +218,10 @@ createdb(const CreatedbStmt *stmt)
|
||||
/*
|
||||
* Check for db name conflict. There is a race condition here, since
|
||||
* another backend could create the same DB name before we commit.
|
||||
* However, holding an exclusive lock on pg_database for the whole
|
||||
* time we are copying the source database doesn't seem like a good
|
||||
* idea, so accept possibility of race to create. We will check again
|
||||
* after we grab the exclusive lock.
|
||||
* However, holding an exclusive lock on pg_database for the whole time we
|
||||
* are copying the source database doesn't seem like a good idea, so
|
||||
* accept possibility of race to create. We will check again after we
|
||||
* grab the exclusive lock.
|
||||
*/
|
||||
if (get_db_info(dbname, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL))
|
||||
@@ -240,7 +240,7 @@ createdb(const CreatedbStmt *stmt)
|
||||
&src_vacuumxid, &src_frozenxid, &src_deftablespace))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
errmsg("template database \"%s\" does not exist", dbtemplate)));
|
||||
errmsg("template database \"%s\" does not exist", dbtemplate)));
|
||||
|
||||
/*
|
||||
* Permission check: to copy a DB that's not marked datistemplate, you
|
||||
@@ -264,8 +264,8 @@ createdb(const CreatedbStmt *stmt)
|
||||
if (DatabaseHasActiveBackends(src_dboid, true))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_IN_USE),
|
||||
errmsg("source database \"%s\" is being accessed by other users",
|
||||
dbtemplate)));
|
||||
errmsg("source database \"%s\" is being accessed by other users",
|
||||
dbtemplate)));
|
||||
|
||||
/* If encoding is defaulted, use source's encoding */
|
||||
if (encoding < 0)
|
||||
@@ -300,7 +300,7 @@ createdb(const CreatedbStmt *stmt)
|
||||
/*
|
||||
* If we are trying to change the default tablespace of the template,
|
||||
* we require that the template not have any files in the new default
|
||||
* tablespace. This is necessary because otherwise the copied
|
||||
* tablespace. This is necessary because otherwise the copied
|
||||
* database would contain pg_class rows that refer to its default
|
||||
* tablespace both explicitly (by OID) and implicitly (as zero), which
|
||||
* would cause problems. For example another CREATE DATABASE using
|
||||
@@ -337,7 +337,7 @@ createdb(const CreatedbStmt *stmt)
|
||||
|
||||
/*
|
||||
* Normally we mark the new database with the same datvacuumxid and
|
||||
* datfrozenxid as the source. However, if the source is not allowing
|
||||
* datfrozenxid as the source. However, if the source is not allowing
|
||||
* connections then we assume it is fully frozen, and we can set the
|
||||
* current transaction ID as the xid limits. This avoids immediately
|
||||
* starting to generate warnings after cloning template0.
|
||||
@@ -346,9 +346,9 @@ createdb(const CreatedbStmt *stmt)
|
||||
src_vacuumxid = src_frozenxid = GetCurrentTransactionId();
|
||||
|
||||
/*
|
||||
* Preassign OID for pg_database tuple, so that we can compute db
|
||||
* path. We have to open pg_database to do this, but we don't want
|
||||
* to take ExclusiveLock yet, so just do it and close again.
|
||||
* Preassign OID for pg_database tuple, so that we can compute db path.
|
||||
* We have to open pg_database to do this, but we don't want to take
|
||||
* ExclusiveLock yet, so just do it and close again.
|
||||
*/
|
||||
pg_database_rel = heap_open(DatabaseRelationId, AccessShareLock);
|
||||
dboid = GetNewOid(pg_database_rel);
|
||||
@@ -357,23 +357,23 @@ createdb(const CreatedbStmt *stmt)
|
||||
|
||||
/*
|
||||
* Force dirty buffers out to disk, to ensure source database is
|
||||
* up-to-date for the copy. (We really only need to flush buffers for
|
||||
* the source database, but bufmgr.c provides no API for that.)
|
||||
* up-to-date for the copy. (We really only need to flush buffers for the
|
||||
* source database, but bufmgr.c provides no API for that.)
|
||||
*/
|
||||
BufferSync();
|
||||
|
||||
/*
|
||||
* Once we start copying subdirectories, we need to be able to clean
|
||||
* 'em up if we fail. Establish a TRY block to make sure this happens.
|
||||
* (This is not a 100% solution, because of the possibility of failure
|
||||
* during transaction commit after we leave this routine, but it should
|
||||
* handle most scenarios.)
|
||||
* Once we start copying subdirectories, we need to be able to clean 'em
|
||||
* up if we fail. Establish a TRY block to make sure this happens. (This
|
||||
* is not a 100% solution, because of the possibility of failure during
|
||||
* transaction commit after we leave this routine, but it should handle
|
||||
* most scenarios.)
|
||||
*/
|
||||
PG_TRY();
|
||||
{
|
||||
/*
|
||||
* Iterate through all tablespaces of the template database,
|
||||
* and copy each one to the new database.
|
||||
* Iterate through all tablespaces of the template database, and copy
|
||||
* each one to the new database.
|
||||
*/
|
||||
rel = heap_open(TableSpaceRelationId, AccessShareLock);
|
||||
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
|
||||
@@ -478,8 +478,8 @@ createdb(const CreatedbStmt *stmt)
|
||||
|
||||
tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls);
|
||||
|
||||
HeapTupleSetOid(tuple, dboid); /* override heap_insert's OID
|
||||
* selection */
|
||||
HeapTupleSetOid(tuple, dboid); /* override heap_insert's OID
|
||||
* selection */
|
||||
|
||||
simple_heap_insert(pg_database_rel, tuple);
|
||||
|
||||
@@ -495,30 +495,31 @@ createdb(const CreatedbStmt *stmt)
|
||||
/*
|
||||
* We force a checkpoint before committing. This effectively means
|
||||
* that committed XLOG_DBASE_CREATE operations will never need to be
|
||||
* replayed (at least not in ordinary crash recovery; we still have
|
||||
* to make the XLOG entry for the benefit of PITR operations).
|
||||
* This avoids two nasty scenarios:
|
||||
* replayed (at least not in ordinary crash recovery; we still have to
|
||||
* make the XLOG entry for the benefit of PITR operations). This
|
||||
* avoids two nasty scenarios:
|
||||
*
|
||||
* #1: When PITR is off, we don't XLOG the contents of newly created
|
||||
* indexes; therefore the drop-and-recreate-whole-directory behavior
|
||||
* of DBASE_CREATE replay would lose such indexes.
|
||||
*
|
||||
* #2: Since we have to recopy the source database during DBASE_CREATE
|
||||
* replay, we run the risk of copying changes in it that were committed
|
||||
* after the original CREATE DATABASE command but before the system
|
||||
* crash that led to the replay. This is at least unexpected and at
|
||||
* worst could lead to inconsistencies, eg duplicate table names.
|
||||
* replay, we run the risk of copying changes in it that were
|
||||
* committed after the original CREATE DATABASE command but before the
|
||||
* system crash that led to the replay. This is at least unexpected
|
||||
* and at worst could lead to inconsistencies, eg duplicate table
|
||||
* names.
|
||||
*
|
||||
* (Both of these were real bugs in releases 8.0 through 8.0.3.)
|
||||
*
|
||||
* In PITR replay, the first of these isn't an issue, and the second
|
||||
* is only a risk if the CREATE DATABASE and subsequent template
|
||||
* database change both occur while a base backup is being taken.
|
||||
* There doesn't seem to be much we can do about that except document
|
||||
* it as a limitation.
|
||||
* In PITR replay, the first of these isn't an issue, and the second is
|
||||
* only a risk if the CREATE DATABASE and subsequent template database
|
||||
* change both occur while a base backup is being taken. There doesn't
|
||||
* seem to be much we can do about that except document it as a
|
||||
* limitation.
|
||||
*
|
||||
* Perhaps if we ever implement CREATE DATABASE in a less cheesy
|
||||
* way, we can avoid this.
|
||||
* Perhaps if we ever implement CREATE DATABASE in a less cheesy way, we
|
||||
* can avoid this.
|
||||
*/
|
||||
RequestCheckpoint(true, false);
|
||||
|
||||
@@ -569,16 +570,16 @@ dropdb(const char *dbname)
|
||||
errmsg("cannot drop the currently open database")));
|
||||
|
||||
/*
|
||||
* Obtain exclusive lock on pg_database. We need this to ensure that
|
||||
* no new backend starts up in the target database while we are
|
||||
* deleting it. (Actually, a new backend might still manage to start
|
||||
* up, because it isn't able to lock pg_database while starting. But
|
||||
* it will detect its error in ReverifyMyDatabase and shut down before
|
||||
* any serious damage is done. See postinit.c.)
|
||||
* Obtain exclusive lock on pg_database. We need this to ensure that no
|
||||
* new backend starts up in the target database while we are deleting it.
|
||||
* (Actually, a new backend might still manage to start up, because it
|
||||
* isn't able to lock pg_database while starting. But it will detect its
|
||||
* error in ReverifyMyDatabase and shut down before any serious damage is
|
||||
* done. See postinit.c.)
|
||||
*
|
||||
* An ExclusiveLock, rather than AccessExclusiveLock, is sufficient
|
||||
* since ReverifyMyDatabase takes RowShareLock. This allows ordinary
|
||||
* readers of pg_database to proceed in parallel.
|
||||
* An ExclusiveLock, rather than AccessExclusiveLock, is sufficient since
|
||||
* ReverifyMyDatabase takes RowShareLock. This allows ordinary readers of
|
||||
* pg_database to proceed in parallel.
|
||||
*/
|
||||
pgdbrel = heap_open(DatabaseRelationId, ExclusiveLock);
|
||||
|
||||
@@ -594,8 +595,8 @@ dropdb(const char *dbname)
|
||||
|
||||
/*
|
||||
* Disallow dropping a DB that is marked istemplate. This is just to
|
||||
* prevent people from accidentally dropping template0 or template1;
|
||||
* they can do so if they're really determined ...
|
||||
* prevent people from accidentally dropping template0 or template1; they
|
||||
* can do so if they're really determined ...
|
||||
*/
|
||||
if (db_istemplate)
|
||||
ereport(ERROR,
|
||||
@@ -608,8 +609,8 @@ dropdb(const char *dbname)
|
||||
if (DatabaseHasActiveBackends(db_id, false))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_IN_USE),
|
||||
errmsg("database \"%s\" is being accessed by other users",
|
||||
dbname)));
|
||||
errmsg("database \"%s\" is being accessed by other users",
|
||||
dbname)));
|
||||
|
||||
/*
|
||||
* Find the database's tuple by OID (should be unique).
|
||||
@@ -626,8 +627,8 @@ dropdb(const char *dbname)
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
/*
|
||||
* This error should never come up since the existence of the
|
||||
* database is checked earlier
|
||||
* This error should never come up since the existence of the database
|
||||
* is checked earlier
|
||||
*/
|
||||
elog(ERROR, "database \"%s\" doesn't exist despite earlier reports to the contrary",
|
||||
dbname);
|
||||
@@ -641,8 +642,8 @@ dropdb(const char *dbname)
|
||||
/*
|
||||
* Delete any comments associated with the database
|
||||
*
|
||||
* NOTE: this is probably dead code since any such comments should have
|
||||
* been in that database, not mine.
|
||||
* NOTE: this is probably dead code since any such comments should have been
|
||||
* in that database, not mine.
|
||||
*/
|
||||
DeleteComments(db_id, DatabaseRelationId, 0);
|
||||
|
||||
@@ -652,9 +653,9 @@ dropdb(const char *dbname)
|
||||
dropDatabaseDependencies(db_id);
|
||||
|
||||
/*
|
||||
* Drop pages for this database that are in the shared buffer cache.
|
||||
* This is important to ensure that no remaining backend tries to
|
||||
* write out a dirty buffer to the dead database later...
|
||||
* Drop pages for this database that are in the shared buffer cache. This
|
||||
* is important to ensure that no remaining backend tries to write out a
|
||||
* dirty buffer to the dead database later...
|
||||
*/
|
||||
DropBuffers(db_id);
|
||||
|
||||
@@ -701,8 +702,8 @@ RenameDatabase(const char *oldname, const char *newname)
|
||||
key2;
|
||||
|
||||
/*
|
||||
* Obtain ExclusiveLock so that no new session gets started
|
||||
* while the rename is in progress.
|
||||
* Obtain ExclusiveLock so that no new session gets started while the
|
||||
* rename is in progress.
|
||||
*/
|
||||
rel = heap_open(DatabaseRelationId, ExclusiveLock);
|
||||
|
||||
@@ -720,10 +721,10 @@ RenameDatabase(const char *oldname, const char *newname)
|
||||
errmsg("database \"%s\" does not exist", oldname)));
|
||||
|
||||
/*
|
||||
* XXX Client applications probably store the current database
|
||||
* somewhere, so renaming it could cause confusion. On the other
|
||||
* hand, there may not be an actual problem besides a little
|
||||
* confusion, so think about this and decide.
|
||||
* XXX Client applications probably store the current database somewhere,
|
||||
* so renaming it could cause confusion. On the other hand, there may not
|
||||
* be an actual problem besides a little confusion, so think about this
|
||||
* and decide.
|
||||
*/
|
||||
if (HeapTupleGetOid(tup) == MyDatabaseId)
|
||||
ereport(ERROR,
|
||||
@@ -737,8 +738,8 @@ RenameDatabase(const char *oldname, const char *newname)
|
||||
if (DatabaseHasActiveBackends(HeapTupleGetOid(tup), false))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_IN_USE),
|
||||
errmsg("database \"%s\" is being accessed by other users",
|
||||
oldname)));
|
||||
errmsg("database \"%s\" is being accessed by other users",
|
||||
oldname)));
|
||||
|
||||
/* make sure the new name doesn't exist */
|
||||
ScanKeyInit(&key2,
|
||||
@@ -822,8 +823,7 @@ AlterDatabase(AlterDatabaseStmt *stmt)
|
||||
connlimit = intVal(dconnlimit->arg);
|
||||
|
||||
/*
|
||||
* We don't need ExclusiveLock since we aren't updating the
|
||||
* flat file.
|
||||
* We don't need ExclusiveLock since we aren't updating the flat file.
|
||||
*/
|
||||
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
ScanKeyInit(&scankey,
|
||||
@@ -868,8 +868,8 @@ AlterDatabase(AlterDatabaseStmt *stmt)
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
/*
|
||||
* We don't bother updating the flat file since the existing options
|
||||
* for ALTER DATABASE don't affect it.
|
||||
* We don't bother updating the flat file since the existing options for
|
||||
* ALTER DATABASE don't affect it.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -893,8 +893,7 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
|
||||
valuestr = flatten_set_variable_args(stmt->variable, stmt->value);
|
||||
|
||||
/*
|
||||
* We don't need ExclusiveLock since we aren't updating the
|
||||
* flat file.
|
||||
* We don't need ExclusiveLock since we aren't updating the flat file.
|
||||
*/
|
||||
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
ScanKeyInit(&scankey,
|
||||
@@ -958,8 +957,8 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
/*
|
||||
* We don't bother updating the flat file since ALTER DATABASE SET
|
||||
* doesn't affect it.
|
||||
* We don't bother updating the flat file since ALTER DATABASE SET doesn't
|
||||
* affect it.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -977,8 +976,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
|
||||
Form_pg_database datForm;
|
||||
|
||||
/*
|
||||
* We don't need ExclusiveLock since we aren't updating the
|
||||
* flat file.
|
||||
* We don't need ExclusiveLock since we aren't updating the flat file.
|
||||
*/
|
||||
rel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
||||
ScanKeyInit(&scankey,
|
||||
@@ -1011,7 +1009,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
|
||||
HeapTuple newtuple;
|
||||
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_database_ownercheck(HeapTupleGetOid(tuple),GetUserId()))
|
||||
if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
||||
dbname);
|
||||
|
||||
@@ -1019,18 +1017,18 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
|
||||
check_is_member_of_role(GetUserId(), newOwnerId);
|
||||
|
||||
/*
|
||||
* must have createdb rights
|
||||
* must have createdb rights
|
||||
*
|
||||
* NOTE: This is different from other alter-owner checks in
|
||||
* that the current user is checked for createdb privileges
|
||||
* instead of the destination owner. This is consistent
|
||||
* with the CREATE case for databases. Because superusers
|
||||
* will always have this right, we need no special case for them.
|
||||
* NOTE: This is different from other alter-owner checks in that the
|
||||
* current user is checked for createdb privileges instead of the
|
||||
* destination owner. This is consistent with the CREATE case for
|
||||
* databases. Because superusers will always have this right, we need
|
||||
* no special case for them.
|
||||
*/
|
||||
if (!have_createdb_privilege())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("permission denied to change owner of database")));
|
||||
errmsg("permission denied to change owner of database")));
|
||||
|
||||
memset(repl_null, ' ', sizeof(repl_null));
|
||||
memset(repl_repl, ' ', sizeof(repl_repl));
|
||||
@@ -1332,10 +1330,9 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
dst_path = GetDatabasePath(xlrec->db_id, xlrec->tablespace_id);
|
||||
|
||||
/*
|
||||
* Our theory for replaying a CREATE is to forcibly drop the
|
||||
* target subdirectory if present, then re-copy the source data.
|
||||
* This may be more work than needed, but it is simple to
|
||||
* implement.
|
||||
* Our theory for replaying a CREATE is to forcibly drop the target
|
||||
* subdirectory if present, then re-copy the source data. This may be
|
||||
* more work than needed, but it is simple to implement.
|
||||
*/
|
||||
if (stat(dst_path, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
{
|
||||
@@ -1367,8 +1364,7 @@ dbase_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
dst_path = GetDatabasePath(xlrec->db_id, xlrec->tablespace_id);
|
||||
|
||||
/*
|
||||
* Drop pages for this database that are in the shared buffer
|
||||
* cache
|
||||
* Drop pages for this database that are in the shared buffer cache
|
||||
*/
|
||||
DropBuffers(xlrec->db_id);
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.92 2004/12/31 21:59:41 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/define.c,v 1.93 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -157,11 +157,11 @@ defGetInt64(DefElem *def)
|
||||
|
||||
/*
|
||||
* Values too large for int4 will be represented as Float
|
||||
* constants by the lexer. Accept these if they are valid
|
||||
* int8 strings.
|
||||
* constants by the lexer. Accept these if they are valid int8
|
||||
* strings.
|
||||
*/
|
||||
return DatumGetInt64(DirectFunctionCall1(int8in,
|
||||
CStringGetDatum(strVal(def->arg))));
|
||||
CStringGetDatum(strVal(def->arg))));
|
||||
default:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.137 2005/06/04 02:07:09 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.138 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -75,12 +75,12 @@ ExplainQuery(ExplainStmt *stmt, DestReceiver *dest)
|
||||
ListCell *l;
|
||||
|
||||
/*
|
||||
* Because the planner is not cool about not scribbling on its input,
|
||||
* we make a preliminary copy of the source querytree. This prevents
|
||||
* Because the planner is not cool about not scribbling on its input, we
|
||||
* make a preliminary copy of the source querytree. This prevents
|
||||
* problems in the case that the EXPLAIN is in a portal or plpgsql
|
||||
* function and is executed repeatedly. (See also the same hack in
|
||||
* DECLARE CURSOR and PREPARE.) XXX the planner really shouldn't
|
||||
* modify its input ... FIXME someday.
|
||||
* DECLARE CURSOR and PREPARE.) XXX the planner really shouldn't modify
|
||||
* its input ... FIXME someday.
|
||||
*/
|
||||
query = copyObject(query);
|
||||
|
||||
@@ -219,7 +219,7 @@ void
|
||||
ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
TupOutputState *tstate)
|
||||
{
|
||||
instr_time starttime;
|
||||
instr_time starttime;
|
||||
double totaltime = 0;
|
||||
ExplainState *es;
|
||||
StringInfo str;
|
||||
@@ -264,7 +264,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
pfree(s);
|
||||
do_text_output_multiline(tstate, f);
|
||||
pfree(f);
|
||||
do_text_output_oneline(tstate, ""); /* separator line */
|
||||
do_text_output_oneline(tstate, ""); /* separator line */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,21 +289,21 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
if (es->printAnalyze)
|
||||
{
|
||||
ResultRelInfo *rInfo;
|
||||
int numrels = queryDesc->estate->es_num_result_relations;
|
||||
int nr;
|
||||
int numrels = queryDesc->estate->es_num_result_relations;
|
||||
int nr;
|
||||
|
||||
rInfo = queryDesc->estate->es_result_relations;
|
||||
for (nr = 0; nr < numrels; rInfo++, nr++)
|
||||
{
|
||||
int nt;
|
||||
int nt;
|
||||
|
||||
if (!rInfo->ri_TrigDesc || !rInfo->ri_TrigInstrument)
|
||||
continue;
|
||||
for (nt = 0; nt < rInfo->ri_TrigDesc->numtriggers; nt++)
|
||||
{
|
||||
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
|
||||
Trigger *trig = rInfo->ri_TrigDesc->triggers + nt;
|
||||
Instrumentation *instr = rInfo->ri_TrigInstrument + nt;
|
||||
char *conname;
|
||||
char *conname;
|
||||
|
||||
/* Must clean up instrumentation state */
|
||||
InstrEndLoop(instr);
|
||||
@@ -316,7 +316,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
continue;
|
||||
|
||||
if (trig->tgisconstraint &&
|
||||
(conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)
|
||||
(conname = GetConstraintNameForTrigger(trig->tgoid)) != NULL)
|
||||
{
|
||||
appendStringInfo(str, "Trigger for constraint %s",
|
||||
conname);
|
||||
@@ -327,7 +327,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
|
||||
if (numrels > 1)
|
||||
appendStringInfo(str, " on %s",
|
||||
RelationGetRelationName(rInfo->ri_RelationDesc));
|
||||
RelationGetRelationName(rInfo->ri_RelationDesc));
|
||||
|
||||
appendStringInfo(str, ": time=%.3f calls=%.0f\n",
|
||||
1000.0 * instr->total,
|
||||
@@ -337,8 +337,8 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
}
|
||||
|
||||
/*
|
||||
* Close down the query and free resources. Include time for this
|
||||
* in the total runtime (although it should be pretty minimal).
|
||||
* Close down the query and free resources. Include time for this in the
|
||||
* total runtime (although it should be pretty minimal).
|
||||
*/
|
||||
INSTR_TIME_SET_CURRENT(starttime);
|
||||
|
||||
@@ -366,7 +366,7 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
|
||||
static double
|
||||
elapsed_time(instr_time *starttime)
|
||||
{
|
||||
instr_time endtime;
|
||||
instr_time endtime;
|
||||
|
||||
INSTR_TIME_SET_CURRENT(endtime);
|
||||
|
||||
@@ -378,7 +378,7 @@ elapsed_time(instr_time *starttime)
|
||||
endtime.tv_usec += 1000000;
|
||||
endtime.tv_sec--;
|
||||
}
|
||||
#else /* WIN32 */
|
||||
#else /* WIN32 */
|
||||
endtime.QuadPart -= starttime->QuadPart;
|
||||
#endif
|
||||
|
||||
@@ -583,7 +583,7 @@ explain_outNode(StringInfo str,
|
||||
if (ScanDirectionIsBackward(((IndexScan *) plan)->indexorderdir))
|
||||
appendStringInfoString(str, " Backward");
|
||||
appendStringInfo(str, " using %s",
|
||||
quote_identifier(get_rel_name(((IndexScan *) plan)->indexid)));
|
||||
quote_identifier(get_rel_name(((IndexScan *) plan)->indexid)));
|
||||
/* FALL THRU */
|
||||
case T_SeqScan:
|
||||
case T_BitmapHeapScan:
|
||||
@@ -604,7 +604,7 @@ explain_outNode(StringInfo str,
|
||||
quote_identifier(relname));
|
||||
if (strcmp(rte->eref->aliasname, relname) != 0)
|
||||
appendStringInfo(str, " %s",
|
||||
quote_identifier(rte->eref->aliasname));
|
||||
quote_identifier(rte->eref->aliasname));
|
||||
}
|
||||
break;
|
||||
case T_BitmapIndexScan:
|
||||
@@ -632,10 +632,10 @@ explain_outNode(StringInfo str,
|
||||
Assert(rte->rtekind == RTE_FUNCTION);
|
||||
|
||||
/*
|
||||
* If the expression is still a function call, we can get
|
||||
* the real name of the function. Otherwise, punt (this
|
||||
* can happen if the optimizer simplified away the
|
||||
* function call, for example).
|
||||
* If the expression is still a function call, we can get the
|
||||
* real name of the function. Otherwise, punt (this can
|
||||
* happen if the optimizer simplified away the function call,
|
||||
* for example).
|
||||
*/
|
||||
if (rte->funcexpr && IsA(rte->funcexpr, FuncExpr))
|
||||
{
|
||||
@@ -652,20 +652,20 @@ explain_outNode(StringInfo str,
|
||||
quote_identifier(proname));
|
||||
if (strcmp(rte->eref->aliasname, proname) != 0)
|
||||
appendStringInfo(str, " %s",
|
||||
quote_identifier(rte->eref->aliasname));
|
||||
quote_identifier(rte->eref->aliasname));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
|
||||
plan->startup_cost, plan->total_cost,
|
||||
plan->plan_rows, plan->plan_width);
|
||||
|
||||
/*
|
||||
* We have to forcibly clean up the instrumentation state because
|
||||
* we haven't done ExecutorEnd yet. This is pretty grotty ...
|
||||
* We have to forcibly clean up the instrumentation state because we
|
||||
* haven't done ExecutorEnd yet. This is pretty grotty ...
|
||||
*/
|
||||
if (planstate->instrument)
|
||||
InstrEndLoop(planstate->instrument);
|
||||
@@ -675,8 +675,8 @@ explain_outNode(StringInfo str,
|
||||
double nloops = planstate->instrument->nloops;
|
||||
|
||||
appendStringInfo(str, " (actual time=%.3f..%.3f rows=%.0f loops=%.0f)",
|
||||
1000.0 * planstate->instrument->startup / nloops,
|
||||
1000.0 * planstate->instrument->total / nloops,
|
||||
1000.0 * planstate->instrument->startup / nloops,
|
||||
1000.0 * planstate->instrument->total / nloops,
|
||||
planstate->instrument->ntuples / nloops,
|
||||
planstate->instrument->nloops);
|
||||
}
|
||||
@@ -833,9 +833,10 @@ explain_outNode(StringInfo str,
|
||||
for (i = 0; i < indent; i++)
|
||||
appendStringInfo(str, " ");
|
||||
appendStringInfo(str, " -> ");
|
||||
|
||||
/*
|
||||
* Ordinarily we don't pass down our own outer_plan value to our
|
||||
* child nodes, but in bitmap scan trees we must, since the bottom
|
||||
* Ordinarily we don't pass down our own outer_plan value to our child
|
||||
* nodes, but in bitmap scan trees we must, since the bottom
|
||||
* BitmapIndexScan nodes may have outer references.
|
||||
*/
|
||||
explain_outNode(str, outerPlan(plan),
|
||||
@@ -882,7 +883,7 @@ explain_outNode(StringInfo str,
|
||||
|
||||
if (IsA(plan, BitmapAnd))
|
||||
{
|
||||
BitmapAnd *bitmapandplan = (BitmapAnd *) plan;
|
||||
BitmapAnd *bitmapandplan = (BitmapAnd *) plan;
|
||||
BitmapAndState *bitmapandstate = (BitmapAndState *) planstate;
|
||||
ListCell *lst;
|
||||
int j;
|
||||
@@ -898,7 +899,7 @@ explain_outNode(StringInfo str,
|
||||
|
||||
explain_outNode(str, subnode,
|
||||
bitmapandstate->bitmapplans[j],
|
||||
outer_plan, /* pass down same outer plan */
|
||||
outer_plan, /* pass down same outer plan */
|
||||
indent + 3, es);
|
||||
j++;
|
||||
}
|
||||
@@ -906,7 +907,7 @@ explain_outNode(StringInfo str,
|
||||
|
||||
if (IsA(plan, BitmapOr))
|
||||
{
|
||||
BitmapOr *bitmaporplan = (BitmapOr *) plan;
|
||||
BitmapOr *bitmaporplan = (BitmapOr *) plan;
|
||||
BitmapOrState *bitmaporstate = (BitmapOrState *) planstate;
|
||||
ListCell *lst;
|
||||
int j;
|
||||
@@ -922,7 +923,7 @@ explain_outNode(StringInfo str,
|
||||
|
||||
explain_outNode(str, subnode,
|
||||
bitmaporstate->bitmapplans[j],
|
||||
outer_plan, /* pass down same outer plan */
|
||||
outer_plan, /* pass down same outer plan */
|
||||
indent + 3, es);
|
||||
j++;
|
||||
}
|
||||
@@ -1008,9 +1009,9 @@ show_scan_qual(List *qual, const char *qlabel,
|
||||
scancontext = deparse_context_for_rte(rte);
|
||||
|
||||
/*
|
||||
* If we have an outer plan that is referenced by the qual, add it to
|
||||
* the deparse context. If not, don't (so that we don't force
|
||||
* prefixes unnecessarily).
|
||||
* If we have an outer plan that is referenced by the qual, add it to the
|
||||
* deparse context. If not, don't (so that we don't force prefixes
|
||||
* unnecessarily).
|
||||
*/
|
||||
if (outer_plan)
|
||||
{
|
||||
@@ -1018,7 +1019,7 @@ show_scan_qual(List *qual, const char *qlabel,
|
||||
|
||||
if (bms_is_member(OUTER, varnos))
|
||||
outercontext = deparse_context_for_subplan("outer",
|
||||
outer_plan->targetlist,
|
||||
outer_plan->targetlist,
|
||||
es->rtable);
|
||||
else
|
||||
outercontext = NULL;
|
||||
@@ -1111,11 +1112,10 @@ show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
|
||||
|
||||
/*
|
||||
* In this routine we expect that the plan node's tlist has not been
|
||||
* processed by set_plan_references(). Normally, any Vars will
|
||||
* contain valid varnos referencing the actual rtable. But we might
|
||||
* instead be looking at a dummy tlist generated by prepunion.c; if
|
||||
* there are Vars with zero varno, use the tlist itself to determine
|
||||
* their names.
|
||||
* processed by set_plan_references(). Normally, any Vars will contain
|
||||
* valid varnos referencing the actual rtable. But we might instead be
|
||||
* looking at a dummy tlist generated by prepunion.c; if there are Vars
|
||||
* with zero varno, use the tlist itself to determine their names.
|
||||
*/
|
||||
varnos = pull_varnos((Node *) tlist);
|
||||
if (bms_is_member(0, varnos))
|
||||
|
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.68 2005/09/24 22:54:36 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.69 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* These routines take the parse tree and pick out the
|
||||
@@ -83,8 +83,8 @@ compute_return_type(TypeName *returnType, Oid languageOid,
|
||||
if (languageOid == SQLlanguageId)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("SQL function cannot return shell type %s",
|
||||
TypeNameToString(returnType))));
|
||||
errmsg("SQL function cannot return shell type %s",
|
||||
TypeNameToString(returnType))));
|
||||
else
|
||||
ereport(NOTICE,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
@@ -158,7 +158,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
|
||||
ListCell *x;
|
||||
int i;
|
||||
|
||||
*requiredResultType = InvalidOid; /* default result */
|
||||
*requiredResultType = InvalidOid; /* default result */
|
||||
|
||||
inTypes = (Oid *) palloc(parameterCount * sizeof(Oid));
|
||||
allTypes = (Datum *) palloc(parameterCount * sizeof(Datum));
|
||||
@@ -182,8 +182,8 @@ examine_parameter_list(List *parameters, Oid languageOid,
|
||||
if (languageOid == SQLlanguageId)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("SQL function cannot accept shell type %s",
|
||||
TypeNameToString(t))));
|
||||
errmsg("SQL function cannot accept shell type %s",
|
||||
TypeNameToString(t))));
|
||||
else
|
||||
ereport(NOTICE,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
@@ -307,13 +307,13 @@ duplicate_error:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting or redundant options")));
|
||||
return false; /* keep compiler quiet */
|
||||
return false; /* keep compiler quiet */
|
||||
}
|
||||
|
||||
static char
|
||||
interpret_func_volatility(DefElem *defel)
|
||||
{
|
||||
char *str = strVal(defel->arg);
|
||||
char *str = strVal(defel->arg);
|
||||
|
||||
if (strcmp(str, "immutable") == 0)
|
||||
return PROVOLATILE_IMMUTABLE;
|
||||
@@ -324,7 +324,7 @@ interpret_func_volatility(DefElem *defel)
|
||||
else
|
||||
{
|
||||
elog(ERROR, "invalid volatility \"%s\"", str);
|
||||
return 0; /* keep compiler quiet */
|
||||
return 0; /* keep compiler quiet */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,8 +445,8 @@ compute_attributes_with_style(List *parameters, bool *isStrict_p, char *volatili
|
||||
else
|
||||
ereport(WARNING,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unrecognized function attribute \"%s\" ignored",
|
||||
param->defname)));
|
||||
errmsg("unrecognized function attribute \"%s\" ignored",
|
||||
param->defname)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,8 +469,8 @@ interpret_AS_clause(Oid languageOid, const char *languageName, List *as,
|
||||
if (languageOid == ClanguageId)
|
||||
{
|
||||
/*
|
||||
* For "C" language, store the file name in probin and, when
|
||||
* given, the link symbol name in prosrc.
|
||||
* For "C" language, store the file name in probin and, when given,
|
||||
* the link symbol name in prosrc.
|
||||
*/
|
||||
*probin_str_p = strVal(linitial(as));
|
||||
if (list_length(as) == 1)
|
||||
@@ -541,7 +541,7 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
|
||||
/* override attributes from explicit list */
|
||||
compute_attributes_sql_style(stmt->options,
|
||||
&as_clause, &language, &volatility, &isStrict, &security);
|
||||
&as_clause, &language, &volatility, &isStrict, &security);
|
||||
|
||||
/* Convert language name to canonical case */
|
||||
languageName = case_translate_language_name(language);
|
||||
@@ -630,10 +630,10 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
/*
|
||||
* In PostgreSQL versions before 6.5, the SQL name of the created
|
||||
* function could not be different from the internal name, and
|
||||
* "prosrc" wasn't used. So there is code out there that does
|
||||
* CREATE FUNCTION xyz AS '' LANGUAGE 'internal'. To preserve some
|
||||
* modicum of backwards compatibility, accept an empty "prosrc"
|
||||
* value as meaning the supplied SQL function name.
|
||||
* "prosrc" wasn't used. So there is code out there that does CREATE
|
||||
* FUNCTION xyz AS '' LANGUAGE 'internal'. To preserve some modicum of
|
||||
* backwards compatibility, accept an empty "prosrc" value as meaning
|
||||
* the supplied SQL function name.
|
||||
*/
|
||||
if (strlen(prosrc_str) == 0)
|
||||
prosrc_str = funcname;
|
||||
@@ -647,8 +647,8 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* And now that we have all the parameters, and know we're permitted
|
||||
* to do so, go ahead and create the function.
|
||||
* And now that we have all the parameters, and know we're permitted to do
|
||||
* so, go ahead and create the function.
|
||||
*/
|
||||
ProcedureCreate(funcname,
|
||||
namespaceId,
|
||||
@@ -696,8 +696,8 @@ RemoveFunction(RemoveFuncStmt *stmt)
|
||||
|
||||
/* Permission check: must own func or its namespace */
|
||||
if (!pg_proc_ownercheck(funcOid, GetUserId()) &&
|
||||
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
|
||||
GetUserId()))
|
||||
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
||||
NameListToString(functionName));
|
||||
|
||||
@@ -706,7 +706,7 @@ RemoveFunction(RemoveFuncStmt *stmt)
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("\"%s\" is an aggregate function",
|
||||
NameListToString(functionName)),
|
||||
errhint("Use DROP AGGREGATE to drop aggregate functions.")));
|
||||
errhint("Use DROP AGGREGATE to drop aggregate functions.")));
|
||||
|
||||
if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
|
||||
{
|
||||
@@ -812,7 +812,7 @@ RenameFunction(List *name, List *argtypes, const char *newname)
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("\"%s\" is an aggregate function",
|
||||
NameListToString(name)),
|
||||
errhint("Use ALTER AGGREGATE to rename aggregate functions.")));
|
||||
errhint("Use ALTER AGGREGATE to rename aggregate functions.")));
|
||||
|
||||
namespaceOid = procForm->pronamespace;
|
||||
|
||||
@@ -828,7 +828,7 @@ RenameFunction(List *name, List *argtypes, const char *newname)
|
||||
errmsg("function %s already exists in schema \"%s\"",
|
||||
funcname_signature_string(newname,
|
||||
procForm->pronargs,
|
||||
procForm->proargtypes.values),
|
||||
procForm->proargtypes.values),
|
||||
get_namespace_name(namespaceOid))));
|
||||
}
|
||||
|
||||
@@ -900,7 +900,7 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
|
||||
if (!superuser())
|
||||
{
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_proc_ownercheck(procOid,GetUserId()))
|
||||
if (!pg_proc_ownercheck(procOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
||||
NameListToString(name));
|
||||
|
||||
@@ -960,14 +960,14 @@ AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
|
||||
void
|
||||
AlterFunction(AlterFunctionStmt *stmt)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Oid funcOid;
|
||||
HeapTuple tup;
|
||||
Oid funcOid;
|
||||
Form_pg_proc procForm;
|
||||
Relation rel;
|
||||
ListCell *l;
|
||||
DefElem *volatility_item = NULL;
|
||||
DefElem *strict_item = NULL;
|
||||
DefElem *security_def_item = NULL;
|
||||
Relation rel;
|
||||
ListCell *l;
|
||||
DefElem *volatility_item = NULL;
|
||||
DefElem *strict_item = NULL;
|
||||
DefElem *security_def_item = NULL;
|
||||
|
||||
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
||||
|
||||
@@ -995,9 +995,9 @@ AlterFunction(AlterFunctionStmt *stmt)
|
||||
NameListToString(stmt->func->funcname))));
|
||||
|
||||
/* Examine requested actions. */
|
||||
foreach (l, stmt->actions)
|
||||
foreach(l, stmt->actions)
|
||||
{
|
||||
DefElem *defel = (DefElem *) lfirst(l);
|
||||
DefElem *defel = (DefElem *) lfirst(l);
|
||||
|
||||
if (compute_common_attribute(defel,
|
||||
&volatility_item,
|
||||
@@ -1182,27 +1182,27 @@ CreateCast(CreateCastStmt *stmt)
|
||||
if (nargs < 1 || nargs > 3)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("cast function must take one to three arguments")));
|
||||
errmsg("cast function must take one to three arguments")));
|
||||
if (procstruct->proargtypes.values[0] != sourcetypeid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("argument of cast function must match source data type")));
|
||||
errmsg("argument of cast function must match source data type")));
|
||||
if (nargs > 1 && procstruct->proargtypes.values[1] != INT4OID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("second argument of cast function must be type integer")));
|
||||
errmsg("second argument of cast function must be type integer")));
|
||||
if (nargs > 2 && procstruct->proargtypes.values[2] != BOOLOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("third argument of cast function must be type boolean")));
|
||||
errmsg("third argument of cast function must be type boolean")));
|
||||
if (procstruct->prorettype != targettypeid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("return data type of cast function must match target data type")));
|
||||
|
||||
/*
|
||||
* Restricting the volatility of a cast function may or may not be
|
||||
* a good idea in the abstract, but it definitely breaks many old
|
||||
* Restricting the volatility of a cast function may or may not be a
|
||||
* good idea in the abstract, but it definitely breaks many old
|
||||
* user-defined types. Disable this check --- tgl 2/1/03
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
@@ -1214,7 +1214,7 @@ CreateCast(CreateCastStmt *stmt)
|
||||
if (procstruct->proisagg)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("cast function must not be an aggregate function")));
|
||||
errmsg("cast function must not be an aggregate function")));
|
||||
if (procstruct->proretset)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
@@ -1242,13 +1242,13 @@ CreateCast(CreateCastStmt *stmt)
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
|
||||
errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
|
||||
|
||||
/*
|
||||
* Also, insist that the types match as to size, alignment, and
|
||||
* pass-by-value attributes; this provides at least a crude check
|
||||
* that they have similar representations. A pair of types that
|
||||
* fail this test should certainly not be equated.
|
||||
* pass-by-value attributes; this provides at least a crude check that
|
||||
* they have similar representations. A pair of types that fail this
|
||||
* test should certainly not be equated.
|
||||
*/
|
||||
get_typlenbyvalalign(sourcetypeid, &typ1len, &typ1byval, &typ1align);
|
||||
get_typlenbyvalalign(targettypeid, &typ2len, &typ2byval, &typ2align);
|
||||
@@ -1267,7 +1267,7 @@ CreateCast(CreateCastStmt *stmt)
|
||||
if (sourcetypeid == targettypeid && nargs < 2)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("source data type and target data type are the same")));
|
||||
errmsg("source data type and target data type are the same")));
|
||||
|
||||
/* convert CoercionContext enum to char value for castcontext */
|
||||
switch (stmt->context)
|
||||
@@ -1290,9 +1290,9 @@ CreateCast(CreateCastStmt *stmt)
|
||||
relation = heap_open(CastRelationId, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Check for duplicate. This is just to give a friendly error
|
||||
* message, the unique index would catch it anyway (so no need to
|
||||
* sweat about race conditions).
|
||||
* Check for duplicate. This is just to give a friendly error message,
|
||||
* the unique index would catch it anyway (so no need to sweat about race
|
||||
* conditions).
|
||||
*/
|
||||
tuple = SearchSysCache(CASTSOURCETARGET,
|
||||
ObjectIdGetDatum(sourcetypeid),
|
||||
@@ -1442,12 +1442,12 @@ DropCastById(Oid castOid)
|
||||
void
|
||||
AlterFunctionNamespace(List *name, List *argtypes, const char *newschema)
|
||||
{
|
||||
Oid procOid;
|
||||
Oid oldNspOid;
|
||||
Oid nspOid;
|
||||
HeapTuple tup;
|
||||
Relation procRel;
|
||||
Form_pg_proc proc;
|
||||
Oid procOid;
|
||||
Oid oldNspOid;
|
||||
Oid nspOid;
|
||||
HeapTuple tup;
|
||||
Relation procRel;
|
||||
Form_pg_proc proc;
|
||||
|
||||
procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
||||
|
||||
@@ -1482,7 +1482,7 @@ AlterFunctionNamespace(List *name, List *argtypes, const char *newschema)
|
||||
if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot move objects into or out of temporary schemas")));
|
||||
errmsg("cannot move objects into or out of temporary schemas")));
|
||||
|
||||
/* same for TOAST schema */
|
||||
if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.133 2005/06/22 21:14:29 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.134 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -145,10 +145,9 @@ DefineIndex(RangeVar *heapRelation,
|
||||
|
||||
/*
|
||||
* Verify we (still) have CREATE rights in the rel's namespace.
|
||||
* (Presumably we did when the rel was created, but maybe not
|
||||
* anymore.) Skip check if caller doesn't want it. Also skip check
|
||||
* if bootstrapping, since permissions machinery may not be working
|
||||
* yet.
|
||||
* (Presumably we did when the rel was created, but maybe not anymore.)
|
||||
* Skip check if caller doesn't want it. Also skip check if
|
||||
* bootstrapping, since permissions machinery may not be working yet.
|
||||
*/
|
||||
if (check_rights && !IsBootstrapProcessingMode())
|
||||
{
|
||||
@@ -193,8 +192,8 @@ DefineIndex(RangeVar *heapRelation,
|
||||
}
|
||||
|
||||
/*
|
||||
* Force shared indexes into the pg_global tablespace. This is a bit of
|
||||
* a hack but seems simpler than marking them in the BKI commands.
|
||||
* Force shared indexes into the pg_global tablespace. This is a bit of a
|
||||
* hack but seems simpler than marking them in the BKI commands.
|
||||
*/
|
||||
if (rel->rd_rel->relisshared)
|
||||
tablespaceId = GLOBALTABLESPACE_OID;
|
||||
@@ -221,8 +220,7 @@ DefineIndex(RangeVar *heapRelation,
|
||||
}
|
||||
|
||||
/*
|
||||
* look up the access method, verify it can handle the requested
|
||||
* features
|
||||
* look up the access method, verify it can handle the requested features
|
||||
*/
|
||||
tuple = SearchSysCache(AMNAME,
|
||||
PointerGetDatum(accessMethodName),
|
||||
@@ -238,13 +236,13 @@ DefineIndex(RangeVar *heapRelation,
|
||||
if (unique && !accessMethodForm->amcanunique)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("access method \"%s\" does not support unique indexes",
|
||||
accessMethodName)));
|
||||
errmsg("access method \"%s\" does not support unique indexes",
|
||||
accessMethodName)));
|
||||
if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("access method \"%s\" does not support multicolumn indexes",
|
||||
accessMethodName)));
|
||||
errmsg("access method \"%s\" does not support multicolumn indexes",
|
||||
accessMethodName)));
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
@@ -275,23 +273,23 @@ DefineIndex(RangeVar *heapRelation,
|
||||
ListCell *keys;
|
||||
|
||||
/*
|
||||
* If ALTER TABLE, check that there isn't already a PRIMARY KEY.
|
||||
* In CREATE TABLE, we have faith that the parser rejected
|
||||
* multiple pkey clauses; and CREATE INDEX doesn't have a way to
|
||||
* say PRIMARY KEY, so it's no problem either.
|
||||
* If ALTER TABLE, check that there isn't already a PRIMARY KEY. In
|
||||
* CREATE TABLE, we have faith that the parser rejected multiple pkey
|
||||
* clauses; and CREATE INDEX doesn't have a way to say PRIMARY KEY, so
|
||||
* it's no problem either.
|
||||
*/
|
||||
if (is_alter_table &&
|
||||
relationHasPrimaryKey(rel))
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||
errmsg("multiple primary keys for table \"%s\" are not allowed",
|
||||
RelationGetRelationName(rel))));
|
||||
errmsg("multiple primary keys for table \"%s\" are not allowed",
|
||||
RelationGetRelationName(rel))));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that all of the attributes in a primary key are marked as
|
||||
* not null, otherwise attempt to ALTER TABLE .. SET NOT NULL
|
||||
* Check that all of the attributes in a primary key are marked as not
|
||||
* null, otherwise attempt to ALTER TABLE .. SET NOT NULL
|
||||
*/
|
||||
cmds = NIL;
|
||||
foreach(keys, attributeList)
|
||||
@@ -326,35 +324,35 @@ DefineIndex(RangeVar *heapRelation,
|
||||
else
|
||||
{
|
||||
/*
|
||||
* This shouldn't happen during CREATE TABLE, but can
|
||||
* happen during ALTER TABLE. Keep message in sync with
|
||||
* This shouldn't happen during CREATE TABLE, but can happen
|
||||
* during ALTER TABLE. Keep message in sync with
|
||||
* transformIndexConstraints() in parser/analyze.c.
|
||||
*/
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column \"%s\" named in key does not exist",
|
||||
key->name)));
|
||||
errmsg("column \"%s\" named in key does not exist",
|
||||
key->name)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade to child
|
||||
* tables? Currently, since the PRIMARY KEY itself doesn't
|
||||
* cascade, we don't cascade the notnull constraint(s) either; but
|
||||
* this is pretty debatable.
|
||||
* tables? Currently, since the PRIMARY KEY itself doesn't cascade,
|
||||
* we don't cascade the notnull constraint(s) either; but this is
|
||||
* pretty debatable.
|
||||
*
|
||||
* XXX: possible future improvement: when being called from ALTER
|
||||
* TABLE, it would be more efficient to merge this with the outer
|
||||
* ALTER TABLE, so as to avoid two scans. But that seems to
|
||||
* complicate DefineIndex's API unduly.
|
||||
* XXX: possible future improvement: when being called from ALTER TABLE,
|
||||
* it would be more efficient to merge this with the outer ALTER
|
||||
* TABLE, so as to avoid two scans. But that seems to complicate
|
||||
* DefineIndex's API unduly.
|
||||
*/
|
||||
if (cmds)
|
||||
AlterTableInternal(relationId, cmds, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare arguments for index_create, primarily an IndexInfo
|
||||
* structure. Note that ii_Predicate must be in implicit-AND format.
|
||||
* Prepare arguments for index_create, primarily an IndexInfo structure.
|
||||
* Note that ii_Predicate must be in implicit-AND format.
|
||||
*/
|
||||
indexInfo = makeNode(IndexInfo);
|
||||
indexInfo->ii_NumIndexAttrs = numberOfAttributes;
|
||||
@@ -372,15 +370,15 @@ DefineIndex(RangeVar *heapRelation,
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
/*
|
||||
* Report index creation if appropriate (delay this till after most of
|
||||
* the error checks)
|
||||
* Report index creation if appropriate (delay this till after most of the
|
||||
* error checks)
|
||||
*/
|
||||
if (isconstraint && !quiet)
|
||||
ereport(NOTICE,
|
||||
(errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
|
||||
is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
|
||||
primary ? "PRIMARY KEY" : "UNIQUE",
|
||||
indexRelationName, RelationGetRelationName(rel))));
|
||||
(errmsg("%s %s will create implicit index \"%s\" for table \"%s\"",
|
||||
is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
|
||||
primary ? "PRIMARY KEY" : "UNIQUE",
|
||||
indexRelationName, RelationGetRelationName(rel))));
|
||||
|
||||
index_create(relationId, indexRelationName, indexRelationId,
|
||||
indexInfo, accessMethodId, tablespaceId, classObjectId,
|
||||
@@ -391,8 +389,8 @@ DefineIndex(RangeVar *heapRelation,
|
||||
* We update the relation's pg_class tuple even if it already has
|
||||
* relhasindex = true. This is needed to cause a shared-cache-inval
|
||||
* message to be sent for the pg_class tuple, which will cause other
|
||||
* backends to flush their relcache entries and in particular their
|
||||
* cached lists of the indexes for this relation.
|
||||
* backends to flush their relcache entries and in particular their cached
|
||||
* lists of the indexes for this relation.
|
||||
*/
|
||||
setRelhasindex(relationId, true, primary, InvalidOid);
|
||||
}
|
||||
@@ -414,8 +412,7 @@ CheckPredicate(Expr *predicate)
|
||||
{
|
||||
/*
|
||||
* We don't currently support generation of an actual query plan for a
|
||||
* predicate, only simple scalar expressions; hence these
|
||||
* restrictions.
|
||||
* predicate, only simple scalar expressions; hence these restrictions.
|
||||
*/
|
||||
if (contain_subplans((Node *) predicate))
|
||||
ereport(ERROR,
|
||||
@@ -433,7 +430,7 @@ CheckPredicate(Expr *predicate)
|
||||
if (contain_mutable_functions((Node *) predicate))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("functions in index predicate must be marked IMMUTABLE")));
|
||||
errmsg("functions in index predicate must be marked IMMUTABLE")));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -470,8 +467,8 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
|
||||
if (isconstraint)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("column \"%s\" named in key does not exist",
|
||||
attribute->name)));
|
||||
errmsg("column \"%s\" named in key does not exist",
|
||||
attribute->name)));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
@@ -501,24 +498,23 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
|
||||
atttype = exprType(attribute->expr);
|
||||
|
||||
/*
|
||||
* We don't currently support generation of an actual query
|
||||
* plan for an index expression, only simple scalar
|
||||
* expressions; hence these restrictions.
|
||||
* We don't currently support generation of an actual query plan
|
||||
* for an index expression, only simple scalar expressions; hence
|
||||
* these restrictions.
|
||||
*/
|
||||
if (contain_subplans(attribute->expr))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot use subquery in index expression")));
|
||||
errmsg("cannot use subquery in index expression")));
|
||||
if (contain_agg_clause(attribute->expr))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_GROUPING_ERROR),
|
||||
errmsg("cannot use aggregate function in index expression")));
|
||||
errmsg("cannot use aggregate function in index expression")));
|
||||
|
||||
/*
|
||||
* A expression using mutable functions is probably wrong,
|
||||
* since if you aren't going to get the same result for the
|
||||
* same data every time, it's not clear what the index entries
|
||||
* mean at all.
|
||||
* A expression using mutable functions is probably wrong, since
|
||||
* if you aren't going to get the same result for the same data
|
||||
* every time, it's not clear what the index entries mean at all.
|
||||
*/
|
||||
if (contain_mutable_functions(attribute->expr))
|
||||
ereport(ERROR,
|
||||
@@ -548,16 +544,16 @@ GetIndexOpClass(List *opclass, Oid attrType,
|
||||
opInputType;
|
||||
|
||||
/*
|
||||
* Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so
|
||||
* we ignore those opclass names so the default *_ops is used. This
|
||||
* can be removed in some later release. bjm 2000/02/07
|
||||
* Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we
|
||||
* ignore those opclass names so the default *_ops is used. This can be
|
||||
* removed in some later release. bjm 2000/02/07
|
||||
*
|
||||
* Release 7.1 removes lztext_ops, so suppress that too for a while. tgl
|
||||
* 2000/07/30
|
||||
*
|
||||
* Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
|
||||
* too for awhile. I'm starting to think we need a better approach.
|
||||
* tgl 2000/10/01
|
||||
* Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that too
|
||||
* for awhile. I'm starting to think we need a better approach. tgl
|
||||
* 2000/10/01
|
||||
*
|
||||
* Release 8.0 removes bigbox_ops (which was dead code for a long while
|
||||
* anyway). tgl 2003/11/11
|
||||
@@ -628,8 +624,8 @@ GetIndexOpClass(List *opclass, Oid attrType,
|
||||
NameListToString(opclass), accessMethodName)));
|
||||
|
||||
/*
|
||||
* Verify that the index operator class accepts this datatype. Note
|
||||
* we will accept binary compatibility.
|
||||
* Verify that the index operator class accepts this datatype. Note we
|
||||
* will accept binary compatibility.
|
||||
*/
|
||||
opClassId = HeapTupleGetOid(tuple);
|
||||
opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
|
||||
@@ -637,8 +633,8 @@ GetIndexOpClass(List *opclass, Oid attrType,
|
||||
if (!IsBinaryCoercible(attrType, opInputType))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("operator class \"%s\" does not accept data type %s",
|
||||
NameListToString(opclass), format_type_be(attrType))));
|
||||
errmsg("operator class \"%s\" does not accept data type %s",
|
||||
NameListToString(opclass), format_type_be(attrType))));
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
@@ -663,8 +659,8 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId)
|
||||
* (either exactly or binary-compatibly, but prefer an exact match).
|
||||
*
|
||||
* We could find more than one binary-compatible match, in which case we
|
||||
* require the user to specify which one he wants. If we find more
|
||||
* than one exact match, then someone put bogus entries in pg_opclass.
|
||||
* require the user to specify which one he wants. If we find more than
|
||||
* one exact match, then someone put bogus entries in pg_opclass.
|
||||
*
|
||||
* The initial search is done by namespace.c so that we only consider
|
||||
* opclasses visible in the current namespace search path. (See also
|
||||
@@ -694,8 +690,8 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId)
|
||||
if (nexact != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("there are multiple default operator classes for data type %s",
|
||||
format_type_be(attrType))));
|
||||
errmsg("there are multiple default operator classes for data type %s",
|
||||
format_type_be(attrType))));
|
||||
if (ncompatible == 1)
|
||||
return compatibleOid;
|
||||
|
||||
@@ -749,8 +745,8 @@ makeObjectName(const char *name1, const char *name2, const char *label)
|
||||
|
||||
/*
|
||||
* If we must truncate, preferentially truncate the longer name. This
|
||||
* logic could be expressed without a loop, but it's simple and
|
||||
* obvious as a loop.
|
||||
* logic could be expressed without a loop, but it's simple and obvious as
|
||||
* a loop.
|
||||
*/
|
||||
while (name1chars + name2chars > availchars)
|
||||
{
|
||||
@@ -842,9 +838,9 @@ relationHasPrimaryKey(Relation rel)
|
||||
ListCell *indexoidscan;
|
||||
|
||||
/*
|
||||
* Get the list of index OIDs for the table from the relcache, and
|
||||
* look up each one in the pg_index syscache until we find one marked
|
||||
* primary key (hopefully there isn't more than one such).
|
||||
* Get the list of index OIDs for the table from the relcache, and look up
|
||||
* each one in the pg_index syscache until we find one marked primary key
|
||||
* (hopefully there isn't more than one such).
|
||||
*/
|
||||
indexoidlist = RelationGetIndexList(rel);
|
||||
|
||||
@@ -1004,16 +1000,16 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
|
||||
|
||||
/*
|
||||
* We cannot run inside a user transaction block; if we were inside a
|
||||
* transaction, then our commit- and start-transaction-command calls
|
||||
* would not have the intended effect!
|
||||
* transaction, then our commit- and start-transaction-command calls would
|
||||
* not have the intended effect!
|
||||
*/
|
||||
PreventTransactionChain((void *) databaseName, "REINDEX DATABASE");
|
||||
|
||||
/*
|
||||
* Create a memory context that will survive forced transaction
|
||||
* commits we do below. Since it is a child of PortalContext, it will
|
||||
* go away eventually even if we suffer an error; there's no need for
|
||||
* special abort cleanup logic.
|
||||
* Create a memory context that will survive forced transaction commits we
|
||||
* do below. Since it is a child of PortalContext, it will go away
|
||||
* eventually even if we suffer an error; there's no need for special
|
||||
* abort cleanup logic.
|
||||
*/
|
||||
private_context = AllocSetContextCreate(PortalContext,
|
||||
"ReindexDatabase",
|
||||
@@ -1022,10 +1018,10 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
/*
|
||||
* We always want to reindex pg_class first. This ensures that if
|
||||
* there is any corruption in pg_class' indexes, they will be fixed
|
||||
* before we process any other tables. This is critical because
|
||||
* reindexing itself will try to update pg_class.
|
||||
* We always want to reindex pg_class first. This ensures that if there
|
||||
* is any corruption in pg_class' indexes, they will be fixed before we
|
||||
* process any other tables. This is critical because reindexing itself
|
||||
* will try to update pg_class.
|
||||
*/
|
||||
if (do_system)
|
||||
{
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.12 2004/12/31 21:59:41 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/lockcmds.c,v 1.13 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -31,8 +31,8 @@ LockTableCommand(LockStmt *lockstmt)
|
||||
ListCell *p;
|
||||
|
||||
/*
|
||||
* Iterate over the list and open, lock, and close the relations one
|
||||
* at a time
|
||||
* Iterate over the list and open, lock, and close the relations one at a
|
||||
* time
|
||||
*/
|
||||
|
||||
foreach(p, lockstmt->relations)
|
||||
@@ -43,8 +43,8 @@ LockTableCommand(LockStmt *lockstmt)
|
||||
Relation rel;
|
||||
|
||||
/*
|
||||
* We don't want to open the relation until we've checked
|
||||
* privilege. So, manually get the relation OID.
|
||||
* We don't want to open the relation until we've checked privilege.
|
||||
* So, manually get the relation OID.
|
||||
*/
|
||||
reloid = RangeVarGetRelid(relation, false);
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.37 2005/08/23 01:41:30 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/opclasscmds.c,v 1.38 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -117,16 +117,16 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
/*
|
||||
* Currently, we require superuser privileges to create an opclass.
|
||||
* This seems necessary because we have no way to validate that the
|
||||
* offered set of operators and functions are consistent with the AM's
|
||||
* expectations. It would be nice to provide such a check someday, if
|
||||
* it can be done without solving the halting problem :-(
|
||||
* Currently, we require superuser privileges to create an opclass. This
|
||||
* seems necessary because we have no way to validate that the offered set
|
||||
* of operators and functions are consistent with the AM's expectations.
|
||||
* It would be nice to provide such a check someday, if it can be done
|
||||
* without solving the halting problem :-(
|
||||
*/
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to create an operator class")));
|
||||
errmsg("must be superuser to create an operator class")));
|
||||
|
||||
/* Look up the datatype */
|
||||
typeoid = typenameTypeId(stmt->datatype);
|
||||
@@ -223,7 +223,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
if (OidIsValid(storageoid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("storage type specified more than once")));
|
||||
errmsg("storage type specified more than once")));
|
||||
storageoid = typenameTypeId(item->storedtype);
|
||||
break;
|
||||
default:
|
||||
@@ -244,8 +244,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
{
|
||||
/*
|
||||
* Currently, only GiST allows storagetype different from
|
||||
* datatype. This hardcoded test should be eliminated in
|
||||
* favor of adding another boolean column to pg_am ...
|
||||
* datatype. This hardcoded test should be eliminated in favor of
|
||||
* adding another boolean column to pg_am ...
|
||||
*/
|
||||
if (amoid != GIST_AM_OID)
|
||||
ereport(ERROR,
|
||||
@@ -258,8 +258,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Make sure there is no existing opclass of this name (this is just
|
||||
* to give a more friendly error message than "duplicate key").
|
||||
* Make sure there is no existing opclass of this name (this is just to
|
||||
* give a more friendly error message than "duplicate key").
|
||||
*/
|
||||
if (SearchSysCacheExists(CLAAMNAMENSP,
|
||||
ObjectIdGetDatum(amoid),
|
||||
@@ -272,10 +272,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
opcname, stmt->amname)));
|
||||
|
||||
/*
|
||||
* If we are creating a default opclass, check there isn't one
|
||||
* already. (Note we do not restrict this test to visible opclasses;
|
||||
* this ensures that typcache.c can find unique solutions to its
|
||||
* questions.)
|
||||
* If we are creating a default opclass, check there isn't one already.
|
||||
* (Note we do not restrict this test to visible opclasses; this ensures
|
||||
* that typcache.c can find unique solutions to its questions.)
|
||||
*/
|
||||
if (stmt->isDefault)
|
||||
{
|
||||
@@ -300,8 +299,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
errmsg("could not make operator class \"%s\" be default for type %s",
|
||||
opcname,
|
||||
TypeNameToString(stmt->datatype)),
|
||||
errdetail("Operator class \"%s\" already is the default.",
|
||||
NameStr(opclass->opcname))));
|
||||
errdetail("Operator class \"%s\" already is the default.",
|
||||
NameStr(opclass->opcname))));
|
||||
}
|
||||
|
||||
systable_endscan(scan);
|
||||
@@ -321,7 +320,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
namestrcpy(&opcName, opcname);
|
||||
values[i++] = NameGetDatum(&opcName); /* opcname */
|
||||
values[i++] = ObjectIdGetDatum(namespaceoid); /* opcnamespace */
|
||||
values[i++] = ObjectIdGetDatum(GetUserId()); /* opcowner */
|
||||
values[i++] = ObjectIdGetDatum(GetUserId()); /* opcowner */
|
||||
values[i++] = ObjectIdGetDatum(typeoid); /* opcintype */
|
||||
values[i++] = BoolGetDatum(stmt->isDefault); /* opcdefault */
|
||||
values[i++] = ObjectIdGetDatum(storageoid); /* opckeytype */
|
||||
@@ -342,8 +341,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
||||
storeProcedures(opclassoid, procedures);
|
||||
|
||||
/*
|
||||
* Create dependencies. Note: we do not create a dependency link to
|
||||
* the AM, because we don't currently support DROP ACCESS METHOD.
|
||||
* Create dependencies. Note: we do not create a dependency link to the
|
||||
* AM, because we don't currently support DROP ACCESS METHOD.
|
||||
*/
|
||||
myself.classId = OperatorClassRelationId;
|
||||
myself.objectId = opclassoid;
|
||||
@@ -424,8 +423,8 @@ assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid)
|
||||
opform = (Form_pg_operator) GETSTRUCT(optup);
|
||||
|
||||
/*
|
||||
* btree operators must be binary ops returning boolean, and the
|
||||
* left-side input type must match the operator class' input type.
|
||||
* btree operators must be binary ops returning boolean, and the left-side
|
||||
* input type must match the operator class' input type.
|
||||
*/
|
||||
if (opform->oprkind != 'b')
|
||||
ereport(ERROR,
|
||||
@@ -438,11 +437,11 @@ assignOperSubtype(Oid amoid, Oid typeoid, Oid operOid)
|
||||
if (opform->oprleft != typeoid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("btree operators must have index type as left input")));
|
||||
errmsg("btree operators must have index type as left input")));
|
||||
|
||||
/*
|
||||
* The subtype is "default" (0) if oprright matches the operator
|
||||
* class, otherwise it is oprright.
|
||||
* The subtype is "default" (0) if oprright matches the operator class,
|
||||
* otherwise it is oprright.
|
||||
*/
|
||||
if (opform->oprright == typeoid)
|
||||
subtype = InvalidOid;
|
||||
@@ -478,8 +477,8 @@ assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid)
|
||||
procform = (Form_pg_proc) GETSTRUCT(proctup);
|
||||
|
||||
/*
|
||||
* btree support procs must be 2-arg procs returning int4, and the
|
||||
* first input type must match the operator class' input type.
|
||||
* btree support procs must be 2-arg procs returning int4, and the first
|
||||
* input type must match the operator class' input type.
|
||||
*/
|
||||
if (procform->pronargs != 2)
|
||||
ereport(ERROR,
|
||||
@@ -492,11 +491,11 @@ assignProcSubtype(Oid amoid, Oid typeoid, Oid procOid)
|
||||
if (procform->proargtypes.values[0] != typeoid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("btree procedures must have index type as first input")));
|
||||
errmsg("btree procedures must have index type as first input")));
|
||||
|
||||
/*
|
||||
* The subtype is "default" (0) if second input type matches the
|
||||
* operator class, otherwise it is the second input type.
|
||||
* The subtype is "default" (0) if second input type matches the operator
|
||||
* class, otherwise it is the second input type.
|
||||
*/
|
||||
if (procform->proargtypes.values[1] == typeoid)
|
||||
subtype = InvalidOid;
|
||||
@@ -525,13 +524,13 @@ addClassMember(List **list, OpClassMember *member, bool isProc)
|
||||
if (isProc)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("procedure number %d appears more than once",
|
||||
member->number)));
|
||||
errmsg("procedure number %d appears more than once",
|
||||
member->number)));
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("operator number %d appears more than once",
|
||||
member->number)));
|
||||
errmsg("operator number %d appears more than once",
|
||||
member->number)));
|
||||
}
|
||||
}
|
||||
*list = lappend(*list, member);
|
||||
@@ -688,7 +687,7 @@ RemoveOpClass(RemoveOpClassStmt *stmt)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
|
||||
NameListToString(stmt->opclassname), stmt->amname)));
|
||||
NameListToString(stmt->opclassname), stmt->amname)));
|
||||
|
||||
opcID = HeapTupleGetOid(tuple);
|
||||
|
||||
@@ -956,7 +955,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
|
||||
if (!superuser())
|
||||
{
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_opclass_ownercheck(HeapTupleGetOid(tup),GetUserId()))
|
||||
if (!pg_opclass_ownercheck(HeapTupleGetOid(tup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
|
||||
NameListToString(name));
|
||||
|
||||
@@ -972,8 +971,7 @@ AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId)
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the owner --- okay to scribble on tup because it's a
|
||||
* copy
|
||||
* Modify the owner --- okay to scribble on tup because it's a copy
|
||||
*/
|
||||
opcForm->opcowner = newOwnerId;
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.25 2005/08/22 17:38:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/operatorcmds.c,v 1.26 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -69,11 +69,9 @@ DefineOperator(List *names, List *parameters)
|
||||
TypeName *typeName2 = NULL; /* second type name */
|
||||
Oid typeId1 = InvalidOid; /* types converted to OID */
|
||||
Oid typeId2 = InvalidOid;
|
||||
List *commutatorName = NIL; /* optional commutator operator
|
||||
* name */
|
||||
List *commutatorName = NIL; /* optional commutator operator name */
|
||||
List *negatorName = NIL; /* optional negator operator name */
|
||||
List *restrictionName = NIL; /* optional restrict. sel.
|
||||
* procedure */
|
||||
List *restrictionName = NIL; /* optional restrict. sel. procedure */
|
||||
List *joinName = NIL; /* optional join sel. procedure */
|
||||
List *leftSortName = NIL; /* optional left sort operator */
|
||||
List *rightSortName = NIL; /* optional right sort operator */
|
||||
@@ -103,7 +101,7 @@ DefineOperator(List *names, List *parameters)
|
||||
if (typeName1->setof)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("setof type not allowed for operator argument")));
|
||||
errmsg("setof type not allowed for operator argument")));
|
||||
}
|
||||
else if (pg_strcasecmp(defel->defname, "rightarg") == 0)
|
||||
{
|
||||
@@ -111,7 +109,7 @@ DefineOperator(List *names, List *parameters)
|
||||
if (typeName2->setof)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
|
||||
errmsg("setof type not allowed for operator argument")));
|
||||
errmsg("setof type not allowed for operator argument")));
|
||||
}
|
||||
else if (pg_strcasecmp(defel->defname, "procedure") == 0)
|
||||
functionName = defGetQualifiedName(defel);
|
||||
@@ -157,8 +155,8 @@ DefineOperator(List *names, List *parameters)
|
||||
typeId2 = typenameTypeId(typeName2);
|
||||
|
||||
/*
|
||||
* If any of the mergejoin support operators were given, then canMerge
|
||||
* is implicit. If canMerge is specified or implicit, fill in default
|
||||
* If any of the mergejoin support operators were given, then canMerge is
|
||||
* implicit. If canMerge is specified or implicit, fill in default
|
||||
* operator names for any missing mergejoin support operators.
|
||||
*/
|
||||
if (leftSortName || rightSortName || ltCompareName || gtCompareName)
|
||||
@@ -184,11 +182,9 @@ DefineOperator(List *names, List *parameters)
|
||||
typeId1, /* left type id */
|
||||
typeId2, /* right type id */
|
||||
functionName, /* function for operator */
|
||||
commutatorName, /* optional commutator operator
|
||||
* name */
|
||||
commutatorName, /* optional commutator operator name */
|
||||
negatorName, /* optional negator operator name */
|
||||
restrictionName, /* optional restrict. sel.
|
||||
* procedure */
|
||||
restrictionName, /* optional restrict. sel. procedure */
|
||||
joinName, /* optional join sel. procedure name */
|
||||
canHash, /* operator hashes */
|
||||
leftSortName, /* optional left sort operator */
|
||||
@@ -300,7 +296,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
|
||||
if (!superuser())
|
||||
{
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_oper_ownercheck(operOid,GetUserId()))
|
||||
if (!pg_oper_ownercheck(operOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
|
||||
NameListToString(name));
|
||||
|
||||
@@ -317,8 +313,7 @@ AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typeName2,
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the owner --- okay to scribble on tup because it's a
|
||||
* copy
|
||||
* Modify the owner --- okay to scribble on tup because it's a copy
|
||||
*/
|
||||
oprForm->oprowner = newOwnerId;
|
||||
|
||||
|
@@ -14,7 +14,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.42 2005/06/03 23:05:28 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/portalcmds.c,v 1.43 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -54,27 +54,26 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||
errmsg("invalid cursor name: must not be empty")));
|
||||
|
||||
/*
|
||||
* If this is a non-holdable cursor, we require that this statement
|
||||
* has been executed inside a transaction block (or else, it would
|
||||
* have no user-visible effect).
|
||||
* If this is a non-holdable cursor, we require that this statement has
|
||||
* been executed inside a transaction block (or else, it would have no
|
||||
* user-visible effect).
|
||||
*/
|
||||
if (!(stmt->options & CURSOR_OPT_HOLD))
|
||||
RequireTransactionChain((void *) stmt, "DECLARE CURSOR");
|
||||
|
||||
/*
|
||||
* Because the planner is not cool about not scribbling on its input,
|
||||
* we make a preliminary copy of the source querytree. This prevents
|
||||
* Because the planner is not cool about not scribbling on its input, we
|
||||
* make a preliminary copy of the source querytree. This prevents
|
||||
* problems in the case that the DECLARE CURSOR is in a portal and is
|
||||
* executed repeatedly. XXX the planner really shouldn't modify its
|
||||
* input ... FIXME someday.
|
||||
* executed repeatedly. XXX the planner really shouldn't modify its input
|
||||
* ... FIXME someday.
|
||||
*/
|
||||
query = copyObject(stmt->query);
|
||||
|
||||
/*
|
||||
* The query has been through parse analysis, but not rewriting or
|
||||
* planning as yet. Note that the grammar ensured we have a SELECT
|
||||
* query, so we are not expecting rule rewriting to do anything
|
||||
* strange.
|
||||
* planning as yet. Note that the grammar ensured we have a SELECT query,
|
||||
* so we are not expecting rule rewriting to do anything strange.
|
||||
*/
|
||||
AcquireRewriteLocks(query);
|
||||
rewritten = QueryRewrite(query);
|
||||
@@ -91,14 +90,13 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||
if (query->rowMarks != NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
|
||||
errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"),
|
||||
errdetail("Cursors must be READ ONLY.")));
|
||||
|
||||
plan = planner(query, true, stmt->options, NULL);
|
||||
|
||||
/*
|
||||
* Create a portal and copy the query and plan into its memory
|
||||
* context.
|
||||
* Create a portal and copy the query and plan into its memory context.
|
||||
*/
|
||||
portal = CreatePortal(stmt->portalname, false, false);
|
||||
|
||||
@@ -116,11 +114,10 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||
|
||||
/*
|
||||
* Also copy the outer portal's parameter list into the inner portal's
|
||||
* memory context. We want to pass down the parameter values in case
|
||||
* we had a command like DECLARE c CURSOR FOR SELECT ... WHERE foo =
|
||||
* $1 This will have been parsed using the outer parameter set and the
|
||||
* parameter value needs to be preserved for use when the cursor is
|
||||
* executed.
|
||||
* memory context. We want to pass down the parameter values in case we
|
||||
* had a command like DECLARE c CURSOR FOR SELECT ... WHERE foo = $1 This
|
||||
* will have been parsed using the outer parameter set and the parameter
|
||||
* value needs to be preserved for use when the cursor is executed.
|
||||
*/
|
||||
params = copyParamList(params);
|
||||
|
||||
@@ -130,8 +127,8 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||
* Set up options for portal.
|
||||
*
|
||||
* If the user didn't specify a SCROLL type, allow or disallow scrolling
|
||||
* based on whether it would require any additional runtime overhead
|
||||
* to do so.
|
||||
* based on whether it would require any additional runtime overhead to do
|
||||
* so.
|
||||
*/
|
||||
portal->cursorOptions = stmt->options;
|
||||
if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
|
||||
@@ -150,8 +147,8 @@ PerformCursorOpen(DeclareCursorStmt *stmt, ParamListInfo params)
|
||||
Assert(portal->strategy == PORTAL_ONE_SELECT);
|
||||
|
||||
/*
|
||||
* We're done; the query won't actually be run until
|
||||
* PerformPortalFetch is called.
|
||||
* We're done; the query won't actually be run until PerformPortalFetch is
|
||||
* called.
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -189,7 +186,7 @@ PerformPortalFetch(FetchStmt *stmt,
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_CURSOR),
|
||||
errmsg("cursor \"%s\" does not exist", stmt->portalname)));
|
||||
errmsg("cursor \"%s\" does not exist", stmt->portalname)));
|
||||
return; /* keep compiler happy */
|
||||
}
|
||||
|
||||
@@ -264,10 +261,9 @@ PortalCleanup(Portal portal)
|
||||
AssertArg(portal->cleanup == PortalCleanup);
|
||||
|
||||
/*
|
||||
* Shut down executor, if still running. We skip this during error
|
||||
* abort, since other mechanisms will take care of releasing executor
|
||||
* resources, and we can't be sure that ExecutorEnd itself wouldn't
|
||||
* fail.
|
||||
* Shut down executor, if still running. We skip this during error abort,
|
||||
* since other mechanisms will take care of releasing executor resources,
|
||||
* and we can't be sure that ExecutorEnd itself wouldn't fail.
|
||||
*/
|
||||
queryDesc = PortalGetQueryDesc(portal);
|
||||
if (queryDesc)
|
||||
@@ -367,9 +363,8 @@ PersistHoldablePortal(Portal portal)
|
||||
MemoryContextSwitchTo(PortalContext);
|
||||
|
||||
/*
|
||||
* Rewind the executor: we need to store the entire result set in
|
||||
* the tuplestore, so that subsequent backward FETCHs can be
|
||||
* processed.
|
||||
* Rewind the executor: we need to store the entire result set in the
|
||||
* tuplestore, so that subsequent backward FETCHs can be processed.
|
||||
*/
|
||||
ExecutorRewind(queryDesc);
|
||||
|
||||
@@ -391,10 +386,10 @@ PersistHoldablePortal(Portal portal)
|
||||
|
||||
/*
|
||||
* Reset the position in the result set: ideally, this could be
|
||||
* implemented by just skipping straight to the tuple # that we
|
||||
* need to be at, but the tuplestore API doesn't support that. So
|
||||
* we start at the beginning of the tuplestore and iterate through
|
||||
* it until we reach where we need to be. FIXME someday?
|
||||
* implemented by just skipping straight to the tuple # that we need
|
||||
* to be at, but the tuplestore API doesn't support that. So we start
|
||||
* at the beginning of the tuplestore and iterate through it until we
|
||||
* reach where we need to be. FIXME someday?
|
||||
*/
|
||||
MemoryContextSwitchTo(portal->holdContext);
|
||||
|
||||
@@ -404,8 +399,8 @@ PersistHoldablePortal(Portal portal)
|
||||
|
||||
if (portal->posOverflow) /* oops, cannot trust portalPos */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("could not reposition held cursor")));
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("could not reposition held cursor")));
|
||||
|
||||
tuplestore_rescan(portal->holdStore);
|
||||
|
||||
@@ -453,10 +448,10 @@ PersistHoldablePortal(Portal portal)
|
||||
QueryContext = saveQueryContext;
|
||||
|
||||
/*
|
||||
* We can now release any subsidiary memory of the portal's heap
|
||||
* context; we'll never use it again. The executor already dropped
|
||||
* its context, but this will clean up anything that glommed onto the
|
||||
* portal's heap via PortalContext.
|
||||
* We can now release any subsidiary memory of the portal's heap context;
|
||||
* we'll never use it again. The executor already dropped its context,
|
||||
* but this will clean up anything that glommed onto the portal's heap via
|
||||
* PortalContext.
|
||||
*/
|
||||
MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@
|
||||
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.40 2005/06/22 17:45:45 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.41 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -84,17 +84,17 @@ PrepareQuery(PrepareStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse analysis is already done, but we must still rewrite and plan
|
||||
* the query.
|
||||
* Parse analysis is already done, but we must still rewrite and plan the
|
||||
* query.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Because the planner is not cool about not scribbling on its input,
|
||||
* we make a preliminary copy of the source querytree. This prevents
|
||||
* Because the planner is not cool about not scribbling on its input, we
|
||||
* make a preliminary copy of the source querytree. This prevents
|
||||
* problems in the case that the PREPARE is in a portal or plpgsql
|
||||
* function and is executed repeatedly. (See also the same hack in
|
||||
* DECLARE CURSOR and EXPLAIN.) XXX the planner really shouldn't
|
||||
* modify its input ... FIXME someday.
|
||||
* DECLARE CURSOR and EXPLAIN.) XXX the planner really shouldn't modify
|
||||
* its input ... FIXME someday.
|
||||
*/
|
||||
query = copyObject(stmt->query);
|
||||
|
||||
@@ -106,8 +106,8 @@ PrepareQuery(PrepareStmt *stmt)
|
||||
plan_list = pg_plan_queries(query_list, NULL, false);
|
||||
|
||||
/*
|
||||
* Save the results. We don't have the query string for this PREPARE,
|
||||
* but we do have the string we got from the client, so use that.
|
||||
* Save the results. We don't have the query string for this PREPARE, but
|
||||
* we do have the string we got from the client, so use that.
|
||||
*/
|
||||
StorePreparedStatement(stmt->name,
|
||||
debug_query_string,
|
||||
@@ -146,8 +146,8 @@ ExecuteQuery(ExecuteStmt *stmt, DestReceiver *dest, char *completionTag)
|
||||
if (entry->argtype_list != NIL)
|
||||
{
|
||||
/*
|
||||
* Need an EState to evaluate parameters; must not delete it till
|
||||
* end of query, in case parameters are pass-by-reference.
|
||||
* Need an EState to evaluate parameters; must not delete it till end
|
||||
* of query, in case parameters are pass-by-reference.
|
||||
*/
|
||||
estate = CreateExecutorState();
|
||||
paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
|
||||
@@ -159,10 +159,10 @@ ExecuteQuery(ExecuteStmt *stmt, DestReceiver *dest, char *completionTag)
|
||||
portal = CreateNewPortal();
|
||||
|
||||
/*
|
||||
* For CREATE TABLE / AS EXECUTE, make a copy of the stored query so
|
||||
* that we can modify its destination (yech, but this has always been
|
||||
* ugly). For regular EXECUTE we can just use the stored query where
|
||||
* it sits, since the executor is read-only.
|
||||
* For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that
|
||||
* we can modify its destination (yech, but this has always been ugly).
|
||||
* For regular EXECUTE we can just use the stored query where it sits,
|
||||
* since the executor is read-only.
|
||||
*/
|
||||
if (stmt->into)
|
||||
{
|
||||
@@ -245,7 +245,7 @@ EvaluateParams(EState *estate, List *params, List *argtypes)
|
||||
bool isNull;
|
||||
|
||||
paramLI[i].value = ExecEvalExprSwitchContext(n,
|
||||
GetPerTupleExprContext(estate),
|
||||
GetPerTupleExprContext(estate),
|
||||
&isNull,
|
||||
NULL);
|
||||
paramLI[i].kind = PARAM_NUM;
|
||||
@@ -333,8 +333,8 @@ StorePreparedStatement(const char *stmt_name,
|
||||
/*
|
||||
* We need to copy the data so that it is stored in the correct memory
|
||||
* context. Do this before making hashtable entry, so that an
|
||||
* out-of-memory failure only wastes memory and doesn't leave us with
|
||||
* an incomplete (ie corrupt) hashtable entry.
|
||||
* out-of-memory failure only wastes memory and doesn't leave us with an
|
||||
* incomplete (ie corrupt) hashtable entry.
|
||||
*/
|
||||
qstring = query_string ? pstrdup(query_string) : NULL;
|
||||
query_list = (List *) copyObject(query_list);
|
||||
@@ -380,9 +380,9 @@ FetchPreparedStatement(const char *stmt_name, bool throwError)
|
||||
if (prepared_queries)
|
||||
{
|
||||
/*
|
||||
* We can't just use the statement name as supplied by the user:
|
||||
* the hash package is picky enough that it needs to be
|
||||
* NULL-padded out to the appropriate length to work correctly.
|
||||
* We can't just use the statement name as supplied by the user: the
|
||||
* hash package is picky enough that it needs to be NULL-padded out to
|
||||
* the appropriate length to work correctly.
|
||||
*/
|
||||
StrNCpy(key, stmt_name, sizeof(key));
|
||||
|
||||
@@ -447,7 +447,7 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
|
||||
|
||||
/*
|
||||
* Given a prepared statement that returns tuples, extract the query
|
||||
* targetlist. Returns NIL if the statement doesn't have a determinable
|
||||
* targetlist. Returns NIL if the statement doesn't have a determinable
|
||||
* targetlist.
|
||||
*
|
||||
* Note: do not modify the result.
|
||||
@@ -464,31 +464,31 @@ FetchPreparedStatementTargetList(PreparedStatement *stmt)
|
||||
return ((Query *) linitial(stmt->query_list))->targetList;
|
||||
if (strategy == PORTAL_UTIL_SELECT)
|
||||
{
|
||||
Node *utilityStmt;
|
||||
Node *utilityStmt;
|
||||
|
||||
utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt;
|
||||
switch (nodeTag(utilityStmt))
|
||||
{
|
||||
case T_FetchStmt:
|
||||
{
|
||||
FetchStmt *substmt = (FetchStmt *) utilityStmt;
|
||||
Portal subportal;
|
||||
{
|
||||
FetchStmt *substmt = (FetchStmt *) utilityStmt;
|
||||
Portal subportal;
|
||||
|
||||
Assert(!substmt->ismove);
|
||||
subportal = GetPortalByName(substmt->portalname);
|
||||
Assert(PortalIsValid(subportal));
|
||||
return FetchPortalTargetList(subportal);
|
||||
}
|
||||
Assert(!substmt->ismove);
|
||||
subportal = GetPortalByName(substmt->portalname);
|
||||
Assert(PortalIsValid(subportal));
|
||||
return FetchPortalTargetList(subportal);
|
||||
}
|
||||
|
||||
case T_ExecuteStmt:
|
||||
{
|
||||
ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
|
||||
PreparedStatement *entry;
|
||||
{
|
||||
ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
|
||||
PreparedStatement *entry;
|
||||
|
||||
Assert(!substmt->into);
|
||||
entry = FetchPreparedStatement(substmt->name, true);
|
||||
return FetchPreparedStatementTargetList(entry);
|
||||
}
|
||||
Assert(!substmt->into);
|
||||
entry = FetchPreparedStatement(substmt->name, true);
|
||||
return FetchPreparedStatementTargetList(entry);
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
@@ -564,8 +564,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
|
||||
if (entry->argtype_list != NIL)
|
||||
{
|
||||
/*
|
||||
* Need an EState to evaluate parameters; must not delete it till
|
||||
* end of query, in case parameters are pass-by-reference.
|
||||
* Need an EState to evaluate parameters; must not delete it till end
|
||||
* of query, in case parameters are pass-by-reference.
|
||||
*/
|
||||
estate = CreateExecutorState();
|
||||
paramLI = EvaluateParams(estate, execstmt->params,
|
||||
@@ -597,7 +597,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
|
||||
if (query->commandType != CMD_SELECT)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("prepared statement is not a SELECT")));
|
||||
errmsg("prepared statement is not a SELECT")));
|
||||
|
||||
/* Copy the query so we can modify it */
|
||||
query = copyObject(query);
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.62 2005/09/08 20:07:42 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.63 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -44,7 +44,7 @@ typedef struct
|
||||
} PLTemplate;
|
||||
|
||||
static void create_proc_lang(const char *languageName,
|
||||
Oid handlerOid, Oid valOid, bool trusted);
|
||||
Oid handlerOid, Oid valOid, bool trusted);
|
||||
static PLTemplate *find_language_template(const char *languageName);
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to create procedural language")));
|
||||
errmsg("must be superuser to create procedural language")));
|
||||
|
||||
/*
|
||||
* Translate the language name and check that this language doesn't
|
||||
@@ -89,7 +89,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
*/
|
||||
if ((pltemplate = find_language_template(languageName)) != NULL)
|
||||
{
|
||||
List *funcname;
|
||||
List *funcname;
|
||||
|
||||
/*
|
||||
* Give a notice if we are ignoring supplied parameters.
|
||||
@@ -99,9 +99,9 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
(errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
|
||||
|
||||
/*
|
||||
* Find or create the handler function, which we force to be in
|
||||
* the pg_catalog schema. If already present, it must have the
|
||||
* correct return type.
|
||||
* Find or create the handler function, which we force to be in the
|
||||
* pg_catalog schema. If already present, it must have the correct
|
||||
* return type.
|
||||
*/
|
||||
funcname = SystemFuncName(pltemplate->tmplhandler);
|
||||
handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
|
||||
@@ -111,23 +111,23 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
if (funcrettype != LANGUAGE_HANDLEROID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("function %s must return type \"language_handler\"",
|
||||
NameListToString(funcname))));
|
||||
errmsg("function %s must return type \"language_handler\"",
|
||||
NameListToString(funcname))));
|
||||
}
|
||||
else
|
||||
{
|
||||
handlerOid = ProcedureCreate(pltemplate->tmplhandler,
|
||||
PG_CATALOG_NAMESPACE,
|
||||
false, /* replace */
|
||||
false, /* returnsSet */
|
||||
false, /* replace */
|
||||
false, /* returnsSet */
|
||||
LANGUAGE_HANDLEROID,
|
||||
ClanguageId,
|
||||
F_FMGR_C_VALIDATOR,
|
||||
pltemplate->tmplhandler,
|
||||
pltemplate->tmpllibrary,
|
||||
false, /* isAgg */
|
||||
false, /* security_definer */
|
||||
false, /* isStrict */
|
||||
false, /* isAgg */
|
||||
false, /* security_definer */
|
||||
false, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 0),
|
||||
PointerGetDatum(NULL),
|
||||
@@ -148,16 +148,16 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
{
|
||||
valOid = ProcedureCreate(pltemplate->tmplvalidator,
|
||||
PG_CATALOG_NAMESPACE,
|
||||
false, /* replace */
|
||||
false, /* returnsSet */
|
||||
false, /* replace */
|
||||
false, /* returnsSet */
|
||||
VOIDOID,
|
||||
ClanguageId,
|
||||
F_FMGR_C_VALIDATOR,
|
||||
pltemplate->tmplvalidator,
|
||||
pltemplate->tmpllibrary,
|
||||
false, /* isAgg */
|
||||
false, /* security_definer */
|
||||
false, /* isStrict */
|
||||
false, /* isAgg */
|
||||
false, /* security_definer */
|
||||
false, /* isStrict */
|
||||
PROVOLATILE_VOLATILE,
|
||||
buildoidvector(funcargtypes, 1),
|
||||
PointerGetDatum(NULL),
|
||||
@@ -175,9 +175,9 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
else
|
||||
{
|
||||
/*
|
||||
* No template, so use the provided information. If there's
|
||||
* no handler clause, the user is trying to rely on a template
|
||||
* that we don't have, so complain accordingly.
|
||||
* No template, so use the provided information. If there's no
|
||||
* handler clause, the user is trying to rely on a template that we
|
||||
* don't have, so complain accordingly.
|
||||
*/
|
||||
if (!stmt->plhandler)
|
||||
ereport(ERROR,
|
||||
@@ -210,8 +210,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("function %s must return type \"language_handler\"",
|
||||
NameListToString(stmt->plhandler))));
|
||||
errmsg("function %s must return type \"language_handler\"",
|
||||
NameListToString(stmt->plhandler))));
|
||||
}
|
||||
|
||||
/* validate the validator function */
|
||||
@@ -385,7 +385,7 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to drop procedural language")));
|
||||
errmsg("must be superuser to drop procedural language")));
|
||||
|
||||
/*
|
||||
* Translate the language name, check that the language exists
|
||||
@@ -471,7 +471,7 @@ RenameLanguage(const char *oldname, const char *newname)
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to rename procedural language")));
|
||||
errmsg("must be superuser to rename procedural language")));
|
||||
|
||||
/* rename */
|
||||
namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.34 2005/08/22 17:38:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/schemacmds.c,v 1.35 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -42,8 +42,8 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
|
||||
Oid namespaceId;
|
||||
List *parsetree_list;
|
||||
ListCell *parsetree_item;
|
||||
Oid owner_uid;
|
||||
Oid saved_uid;
|
||||
Oid owner_uid;
|
||||
Oid saved_uid;
|
||||
AclResult aclresult;
|
||||
|
||||
saved_uid = GetUserId();
|
||||
@@ -60,8 +60,8 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
|
||||
* To create a schema, must have schema-create privilege on the current
|
||||
* database and must be able to become the target role (this does not
|
||||
* imply that the target role itself must have create-schema privilege).
|
||||
* The latter provision guards against "giveaway" attacks. Note that
|
||||
* a superuser will always have both of these privileges a fortiori.
|
||||
* The latter provision guards against "giveaway" attacks. Note that a
|
||||
* superuser will always have both of these privileges a fortiori.
|
||||
*/
|
||||
aclresult = pg_database_aclcheck(MyDatabaseId, saved_uid, ACL_CREATE);
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
@@ -75,15 +75,15 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_RESERVED_NAME),
|
||||
errmsg("unacceptable schema name \"%s\"", schemaName),
|
||||
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
|
||||
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
|
||||
|
||||
/*
|
||||
* If the requested authorization is different from the current user,
|
||||
* temporarily set the current user so that the object(s) will be
|
||||
* created with the correct ownership.
|
||||
* temporarily set the current user so that the object(s) will be created
|
||||
* with the correct ownership.
|
||||
*
|
||||
* (The setting will revert to session user on error or at the end of
|
||||
* this routine.)
|
||||
* (The setting will revert to session user on error or at the end of this
|
||||
* routine.)
|
||||
*/
|
||||
if (saved_uid != owner_uid)
|
||||
SetUserId(owner_uid);
|
||||
@@ -95,19 +95,18 @@ CreateSchemaCommand(CreateSchemaStmt *stmt)
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* Temporarily make the new namespace be the front of the search path,
|
||||
* as well as the default creation target namespace. This will be
|
||||
* undone at the end of this routine, or upon error.
|
||||
* Temporarily make the new namespace be the front of the search path, as
|
||||
* well as the default creation target namespace. This will be undone at
|
||||
* the end of this routine, or upon error.
|
||||
*/
|
||||
PushSpecialNamespace(namespaceId);
|
||||
|
||||
/*
|
||||
* Examine the list of commands embedded in the CREATE SCHEMA command,
|
||||
* and reorganize them into a sequentially executable order with no
|
||||
* forward references. Note that the result is still a list of raw
|
||||
* parsetrees in need of parse analysis --- we cannot, in general, run
|
||||
* analyze.c on one statement until we have actually executed the
|
||||
* prior ones.
|
||||
* Examine the list of commands embedded in the CREATE SCHEMA command, and
|
||||
* reorganize them into a sequentially executable order with no forward
|
||||
* references. Note that the result is still a list of raw parsetrees in
|
||||
* need of parse analysis --- we cannot, in general, run analyze.c on one
|
||||
* statement until we have actually executed the prior ones.
|
||||
*/
|
||||
parsetree_list = analyzeCreateSchemaStmt(stmt);
|
||||
|
||||
@@ -174,8 +173,8 @@ RemoveSchema(List *names, DropBehavior behavior)
|
||||
namespaceName);
|
||||
|
||||
/*
|
||||
* Do the deletion. Objects contained in the schema are removed by
|
||||
* means of their dependency links to the schema.
|
||||
* Do the deletion. Objects contained in the schema are removed by means
|
||||
* of their dependency links to the schema.
|
||||
*/
|
||||
object.classId = NamespaceRelationId;
|
||||
object.objectId = namespaceId;
|
||||
@@ -254,7 +253,7 @@ RenameSchema(const char *oldname, const char *newname)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_RESERVED_NAME),
|
||||
errmsg("unacceptable schema name \"%s\"", newname),
|
||||
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
|
||||
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
|
||||
|
||||
/* rename */
|
||||
namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
|
||||
@@ -302,21 +301,21 @@ AlterSchemaOwner(const char *name, Oid newOwnerId)
|
||||
AclResult aclresult;
|
||||
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_namespace_ownercheck(HeapTupleGetOid(tup),GetUserId()))
|
||||
if (!pg_namespace_ownercheck(HeapTupleGetOid(tup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
|
||||
name);
|
||||
|
||||
/* Must be able to become new owner */
|
||||
check_is_member_of_role(GetUserId(),newOwnerId);
|
||||
check_is_member_of_role(GetUserId(), newOwnerId);
|
||||
|
||||
/*
|
||||
* must have create-schema rights
|
||||
*
|
||||
* NOTE: This is different from other alter-owner checks in
|
||||
* that the current user is checked for create privileges
|
||||
* instead of the destination owner. This is consistent
|
||||
* with the CREATE case for schemas. Because superusers
|
||||
* will always have this right, we need no special case for them.
|
||||
* NOTE: This is different from other alter-owner checks in that the
|
||||
* current user is checked for create privileges instead of the
|
||||
* destination owner. This is consistent with the CREATE case for
|
||||
* schemas. Because superusers will always have this right, we need
|
||||
* no special case for them.
|
||||
*/
|
||||
aclresult = pg_database_aclcheck(MyDatabaseId, GetUserId(),
|
||||
ACL_CREATE);
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.124 2005/10/02 23:50:08 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.125 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -219,17 +219,17 @@ DefineSequence(CreateSeqStmt *seq)
|
||||
/*
|
||||
* Two special hacks here:
|
||||
*
|
||||
* 1. Since VACUUM does not process sequences, we have to force the tuple
|
||||
* to have xmin = FrozenTransactionId now. Otherwise it would become
|
||||
* 1. Since VACUUM does not process sequences, we have to force the tuple to
|
||||
* have xmin = FrozenTransactionId now. Otherwise it would become
|
||||
* invisible to SELECTs after 2G transactions. It is okay to do this
|
||||
* because if the current transaction aborts, no other xact will ever
|
||||
* examine the sequence tuple anyway.
|
||||
*
|
||||
* 2. Even though heap_insert emitted a WAL log record, we have to emit
|
||||
* an XLOG_SEQ_LOG record too, since (a) the heap_insert record will
|
||||
* not have the right xmin, and (b) REDO of the heap_insert record
|
||||
* would re-init page and sequence magic number would be lost. This
|
||||
* means two log records instead of one :-(
|
||||
* 2. Even though heap_insert emitted a WAL log record, we have to emit an
|
||||
* XLOG_SEQ_LOG record too, since (a) the heap_insert record will not have
|
||||
* the right xmin, and (b) REDO of the heap_insert record would re-init
|
||||
* page and sequence magic number would be lost. This means two log
|
||||
* records instead of one :-(
|
||||
*/
|
||||
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
|
||||
|
||||
@@ -237,12 +237,11 @@ DefineSequence(CreateSeqStmt *seq)
|
||||
|
||||
{
|
||||
/*
|
||||
* Note that the "tuple" structure is still just a local tuple
|
||||
* record created by heap_formtuple; its t_data pointer doesn't
|
||||
* point at the disk buffer. To scribble on the disk buffer we
|
||||
* need to fetch the item pointer. But do the same to the local
|
||||
* tuple, since that will be the source for the WAL log record,
|
||||
* below.
|
||||
* Note that the "tuple" structure is still just a local tuple record
|
||||
* created by heap_formtuple; its t_data pointer doesn't point at the
|
||||
* disk buffer. To scribble on the disk buffer we need to fetch the
|
||||
* item pointer. But do the same to the local tuple, since that will
|
||||
* be the source for the WAL log record, below.
|
||||
*/
|
||||
ItemId itemId;
|
||||
Item item;
|
||||
@@ -334,8 +333,8 @@ AlterSequence(AlterSeqStmt *stmt)
|
||||
|
||||
/* Clear local cache so that we don't think we have cached numbers */
|
||||
elm->last = new.last_value; /* last returned number */
|
||||
elm->cached = new.last_value; /* last cached number (forget
|
||||
* cached values) */
|
||||
elm->cached = new.last_value; /* last cached number (forget cached
|
||||
* values) */
|
||||
|
||||
START_CRIT_SECTION();
|
||||
|
||||
@@ -456,14 +455,14 @@ nextval_internal(Oid relid)
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide whether we should emit a WAL log record. If so, force up
|
||||
* the fetch count to grab SEQ_LOG_VALS more values than we actually
|
||||
* need to cache. (These will then be usable without logging.)
|
||||
* Decide whether we should emit a WAL log record. If so, force up the
|
||||
* fetch count to grab SEQ_LOG_VALS more values than we actually need to
|
||||
* cache. (These will then be usable without logging.)
|
||||
*
|
||||
* If this is the first nextval after a checkpoint, we must force a new
|
||||
* WAL record to be written anyway, else replay starting from the
|
||||
* checkpoint would fail to advance the sequence past the logged
|
||||
* values. In this case we may as well fetch extra values.
|
||||
* If this is the first nextval after a checkpoint, we must force a new WAL
|
||||
* record to be written anyway, else replay starting from the checkpoint
|
||||
* would fail to advance the sequence past the logged values. In this
|
||||
* case we may as well fetch extra values.
|
||||
*/
|
||||
if (log < fetch)
|
||||
{
|
||||
@@ -486,8 +485,8 @@ nextval_internal(Oid relid)
|
||||
while (fetch) /* try to fetch cache [+ log ] numbers */
|
||||
{
|
||||
/*
|
||||
* Check MAXVALUE for ascending sequences and MINVALUE for
|
||||
* descending sequences
|
||||
* Check MAXVALUE for ascending sequences and MINVALUE for descending
|
||||
* sequences
|
||||
*/
|
||||
if (incby > 0)
|
||||
{
|
||||
@@ -503,9 +502,9 @@ nextval_internal(Oid relid)
|
||||
|
||||
snprintf(buf, sizeof(buf), INT64_FORMAT, maxv);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
|
||||
RelationGetRelationName(seqrel), buf)));
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("nextval: reached maximum value of sequence \"%s\" (%s)",
|
||||
RelationGetRelationName(seqrel), buf)));
|
||||
}
|
||||
next = minv;
|
||||
}
|
||||
@@ -526,9 +525,9 @@ nextval_internal(Oid relid)
|
||||
|
||||
snprintf(buf, sizeof(buf), INT64_FORMAT, minv);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
|
||||
RelationGetRelationName(seqrel), buf)));
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("nextval: reached minimum value of sequence \"%s\" (%s)",
|
||||
RelationGetRelationName(seqrel), buf)));
|
||||
}
|
||||
next = maxv;
|
||||
}
|
||||
@@ -721,8 +720,7 @@ do_setval(Oid relid, int64 next, bool iscalled)
|
||||
|
||||
/* save info in local cache */
|
||||
elm->last = next; /* last returned number */
|
||||
elm->cached = next; /* last cached number (forget cached
|
||||
* values) */
|
||||
elm->cached = next; /* last cached number (forget cached values) */
|
||||
|
||||
START_CRIT_SECTION();
|
||||
|
||||
@@ -805,7 +803,7 @@ setval3_oid(PG_FUNCTION_ARGS)
|
||||
|
||||
/*
|
||||
* If we haven't touched the sequence already in this transaction,
|
||||
* we need to acquire AccessShareLock. We arrange for the lock to
|
||||
* we need to acquire AccessShareLock. We arrange for the lock to
|
||||
* be owned by the top transaction, so that we don't need to do it
|
||||
* more than once per xact.
|
||||
*/
|
||||
@@ -869,15 +867,15 @@ init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel)
|
||||
/*
|
||||
* Allocate new seqtable entry if we didn't find one.
|
||||
*
|
||||
* NOTE: seqtable entries remain in the list for the life of a backend.
|
||||
* If the sequence itself is deleted then the entry becomes wasted
|
||||
* memory, but it's small enough that this should not matter.
|
||||
* NOTE: seqtable entries remain in the list for the life of a backend. If
|
||||
* the sequence itself is deleted then the entry becomes wasted memory,
|
||||
* but it's small enough that this should not matter.
|
||||
*/
|
||||
if (elm == NULL)
|
||||
{
|
||||
/*
|
||||
* Time to make a new seqtable entry. These entries live as long
|
||||
* as the backend does, so we use plain malloc for them.
|
||||
* Time to make a new seqtable entry. These entries live as long as
|
||||
* the backend does, so we use plain malloc for them.
|
||||
*/
|
||||
elm = (SeqTable) malloc(sizeof(SeqTableData));
|
||||
if (elm == NULL)
|
||||
@@ -1094,8 +1092,8 @@ init_params(List *options, Form_pg_sequence new, bool isInit)
|
||||
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("START value (%s) can't be less than MINVALUE (%s)",
|
||||
bufs, bufm)));
|
||||
errmsg("START value (%s) can't be less than MINVALUE (%s)",
|
||||
bufs, bufm)));
|
||||
}
|
||||
if (new->last_value > new->max_value)
|
||||
{
|
||||
@@ -1106,8 +1104,8 @@ init_params(List *options, Form_pg_sequence new, bool isInit)
|
||||
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
|
||||
bufs, bufm)));
|
||||
errmsg("START value (%s) can't be greater than MAXVALUE (%s)",
|
||||
bufs, bufm)));
|
||||
}
|
||||
|
||||
/* CACHE */
|
||||
@@ -1152,7 +1150,7 @@ seq_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
buffer = XLogReadBuffer(true, reln, 0);
|
||||
if (!BufferIsValid(buffer))
|
||||
elog(PANIC, "seq_redo: can't read block 0 of rel %u/%u/%u",
|
||||
xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
|
||||
xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode);
|
||||
|
||||
page = (Page) BufferGetPage(buffer);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -37,7 +37,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.27 2005/08/30 01:08:47 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.28 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
|
||||
/* GUC variable */
|
||||
char *default_tablespace = NULL;
|
||||
char *default_tablespace = NULL;
|
||||
|
||||
|
||||
static bool remove_tablespace_directories(Oid tablespaceoid, bool redo);
|
||||
@@ -118,9 +118,9 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
/*
|
||||
* Acquire ExclusiveLock on pg_tablespace to ensure that no
|
||||
* DROP TABLESPACE or TablespaceCreateDbspace is running
|
||||
* concurrently. Simple reads from pg_tablespace are OK.
|
||||
* Acquire ExclusiveLock on pg_tablespace to ensure that no DROP
|
||||
* TABLESPACE or TablespaceCreateDbspace is running concurrently.
|
||||
* Simple reads from pg_tablespace are OK.
|
||||
*/
|
||||
Relation rel;
|
||||
|
||||
@@ -130,8 +130,8 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
|
||||
rel = NULL;
|
||||
|
||||
/*
|
||||
* Recheck to see if someone created the directory while we
|
||||
* were waiting for lock.
|
||||
* Recheck to see if someone created the directory while we were
|
||||
* waiting for lock.
|
||||
*/
|
||||
if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
{
|
||||
@@ -147,22 +147,22 @@ TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
|
||||
if (errno != ENOENT || !isRedo)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create directory \"%s\": %m",
|
||||
dir)));
|
||||
errmsg("could not create directory \"%s\": %m",
|
||||
dir)));
|
||||
/* Try to make parent directory too */
|
||||
parentdir = pstrdup(dir);
|
||||
get_parent_directory(parentdir);
|
||||
if (mkdir(parentdir, S_IRWXU) < 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create directory \"%s\": %m",
|
||||
parentdir)));
|
||||
errmsg("could not create directory \"%s\": %m",
|
||||
parentdir)));
|
||||
pfree(parentdir);
|
||||
if (mkdir(dir, S_IRWXU) < 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create directory \"%s\": %m",
|
||||
dir)));
|
||||
errmsg("could not create directory \"%s\": %m",
|
||||
dir)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
Oid tablespaceoid;
|
||||
char *location;
|
||||
char *linkloc;
|
||||
Oid ownerId;
|
||||
Oid ownerId;
|
||||
|
||||
/* validate */
|
||||
|
||||
@@ -238,7 +238,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
if (strchr(location, '\''))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("tablespace location may not contain single quotes")));
|
||||
errmsg("tablespace location may not contain single quotes")));
|
||||
|
||||
/*
|
||||
* Allowing relative paths seems risky
|
||||
@@ -251,9 +251,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
errmsg("tablespace location must be an absolute path")));
|
||||
|
||||
/*
|
||||
* Check that location isn't too long. Remember that we're going to
|
||||
* append '/<dboid>/<relid>.<nnn>' (XXX but do we ever form the whole
|
||||
* path explicitly? This may be overly conservative.)
|
||||
* Check that location isn't too long. Remember that we're going to append
|
||||
* '/<dboid>/<relid>.<nnn>' (XXX but do we ever form the whole path
|
||||
* explicitly? This may be overly conservative.)
|
||||
*/
|
||||
if (strlen(location) >= (MAXPGPATH - 1 - 10 - 1 - 10 - 1 - 10))
|
||||
ereport(ERROR,
|
||||
@@ -270,7 +270,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
(errcode(ERRCODE_RESERVED_NAME),
|
||||
errmsg("unacceptable tablespace name \"%s\"",
|
||||
stmt->tablespacename),
|
||||
errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
|
||||
errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
|
||||
|
||||
/*
|
||||
* Check that there is no other tablespace by this name. (The unique
|
||||
@@ -284,9 +284,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
stmt->tablespacename)));
|
||||
|
||||
/*
|
||||
* Insert tuple into pg_tablespace. The purpose of doing this first
|
||||
* is to lock the proposed tablename against other would-be creators.
|
||||
* The insertion will roll back if we find problems below.
|
||||
* Insert tuple into pg_tablespace. The purpose of doing this first is to
|
||||
* lock the proposed tablename against other would-be creators. The
|
||||
* insertion will roll back if we find problems below.
|
||||
*/
|
||||
rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
|
||||
|
||||
@@ -312,14 +312,14 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
recordDependencyOnOwner(TableSpaceRelationId, tablespaceoid, ownerId);
|
||||
|
||||
/*
|
||||
* Attempt to coerce target directory to safe permissions. If this
|
||||
* fails, it doesn't exist or has the wrong owner.
|
||||
* Attempt to coerce target directory to safe permissions. If this fails,
|
||||
* it doesn't exist or has the wrong owner.
|
||||
*/
|
||||
if (chmod(location, 0700) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not set permissions on directory \"%s\": %m",
|
||||
location)));
|
||||
errmsg("could not set permissions on directory \"%s\": %m",
|
||||
location)));
|
||||
|
||||
/*
|
||||
* Check the target directory is empty.
|
||||
@@ -331,11 +331,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
location)));
|
||||
|
||||
/*
|
||||
* Create the PG_VERSION file in the target directory. This has
|
||||
* several purposes: to make sure we can write in the directory, to
|
||||
* prevent someone from creating another tablespace pointing at the
|
||||
* same directory (the emptiness check above will fail), and to label
|
||||
* tablespace directories by PG version.
|
||||
* Create the PG_VERSION file in the target directory. This has several
|
||||
* purposes: to make sure we can write in the directory, to prevent
|
||||
* someone from creating another tablespace pointing at the same directory
|
||||
* (the emptiness check above will fail), and to label tablespace
|
||||
* directories by PG version.
|
||||
*/
|
||||
set_short_version(location);
|
||||
|
||||
@@ -375,7 +375,6 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
|
||||
|
||||
/* We keep the lock on pg_tablespace until commit */
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
#else /* !HAVE_SYMLINK */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
@@ -403,9 +402,8 @@ DropTableSpace(DropTableSpaceStmt *stmt)
|
||||
PreventTransactionChain((void *) stmt, "DROP TABLESPACE");
|
||||
|
||||
/*
|
||||
* Acquire ExclusiveLock on pg_tablespace to ensure that no one else
|
||||
* is trying to do DROP TABLESPACE or TablespaceCreateDbspace
|
||||
* concurrently.
|
||||
* Acquire ExclusiveLock on pg_tablespace to ensure that no one else is
|
||||
* trying to do DROP TABLESPACE or TablespaceCreateDbspace concurrently.
|
||||
*/
|
||||
rel = heap_open(TableSpaceRelationId, ExclusiveLock);
|
||||
|
||||
@@ -439,8 +437,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
|
||||
tablespacename);
|
||||
|
||||
/*
|
||||
* Remove the pg_tablespace tuple (this will roll back if we fail
|
||||
* below)
|
||||
* Remove the pg_tablespace tuple (this will roll back if we fail below)
|
||||
*/
|
||||
simple_heap_delete(rel, &tuple->t_self);
|
||||
|
||||
@@ -476,7 +473,6 @@ DropTableSpace(DropTableSpaceStmt *stmt)
|
||||
|
||||
/* We keep the lock on pg_tablespace until commit */
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
#else /* !HAVE_SYMLINK */
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
@@ -504,17 +500,17 @@ remove_tablespace_directories(Oid tablespaceoid, bool redo)
|
||||
sprintf(location, "pg_tblspc/%u", tablespaceoid);
|
||||
|
||||
/*
|
||||
* Check if the tablespace still contains any files. We try to rmdir
|
||||
* each per-database directory we find in it. rmdir failure implies
|
||||
* there are still files in that subdirectory, so give up. (We do not
|
||||
* have to worry about undoing any already completed rmdirs, since the
|
||||
* next attempt to use the tablespace from that database will simply
|
||||
* recreate the subdirectory via TablespaceCreateDbspace.)
|
||||
* Check if the tablespace still contains any files. We try to rmdir each
|
||||
* per-database directory we find in it. rmdir failure implies there are
|
||||
* still files in that subdirectory, so give up. (We do not have to worry
|
||||
* about undoing any already completed rmdirs, since the next attempt to
|
||||
* use the tablespace from that database will simply recreate the
|
||||
* subdirectory via TablespaceCreateDbspace.)
|
||||
*
|
||||
* Since we hold exclusive lock, no one else should be creating any fresh
|
||||
* subdirectories in parallel. It is possible that new files are
|
||||
* being created within subdirectories, though, so the rmdir call
|
||||
* could fail. Worst consequence is a less friendly error message.
|
||||
* subdirectories in parallel. It is possible that new files are being
|
||||
* created within subdirectories, though, so the rmdir call could fail.
|
||||
* Worst consequence is a less friendly error message.
|
||||
*/
|
||||
dirdesc = AllocateDir(location);
|
||||
if (dirdesc == NULL)
|
||||
@@ -558,8 +554,8 @@ remove_tablespace_directories(Oid tablespaceoid, bool redo)
|
||||
FreeDir(dirdesc);
|
||||
|
||||
/*
|
||||
* Okay, try to unlink PG_VERSION (we allow it to not be there, even
|
||||
* in non-REDO case, for robustness).
|
||||
* Okay, try to unlink PG_VERSION (we allow it to not be there, even in
|
||||
* non-REDO case, for robustness).
|
||||
*/
|
||||
subfile = palloc(strlen(location) + 11 + 1);
|
||||
sprintf(subfile, "%s/PG_VERSION", location);
|
||||
@@ -577,9 +573,9 @@ remove_tablespace_directories(Oid tablespaceoid, bool redo)
|
||||
|
||||
/*
|
||||
* Okay, try to remove the symlink. We must however deal with the
|
||||
* possibility that it's a directory instead of a symlink --- this
|
||||
* could happen during WAL replay (see TablespaceCreateDbspace), and
|
||||
* it is also the normal case on Windows.
|
||||
* possibility that it's a directory instead of a symlink --- this could
|
||||
* happen during WAL replay (see TablespaceCreateDbspace), and it is also
|
||||
* the normal case on Windows.
|
||||
*/
|
||||
if (lstat(location, &st) == 0 && S_ISDIR(st.st_mode))
|
||||
{
|
||||
@@ -725,7 +721,7 @@ RenameTableSpace(const char *oldname, const char *newname)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_RESERVED_NAME),
|
||||
errmsg("unacceptable tablespace name \"%s\"", newname),
|
||||
errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
|
||||
errdetail("The prefix \"pg_\" is reserved for system tablespaces.")));
|
||||
|
||||
/* Make sure the new name doesn't exist */
|
||||
ScanKeyInit(&entry[0],
|
||||
@@ -802,13 +798,13 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId)
|
||||
check_is_member_of_role(GetUserId(), newOwnerId);
|
||||
|
||||
/*
|
||||
* Normally we would also check for create permissions here,
|
||||
* but there are none for tablespaces so we follow what rename
|
||||
* tablespace does and omit the create permissions check.
|
||||
* Normally we would also check for create permissions here, but there
|
||||
* are none for tablespaces so we follow what rename tablespace does
|
||||
* and omit the create permissions check.
|
||||
*
|
||||
* NOTE: Only superusers may create tablespaces to begin with and
|
||||
* so initially only a superuser would be able to change its
|
||||
* ownership anyway.
|
||||
* NOTE: Only superusers may create tablespaces to begin with and so
|
||||
* initially only a superuser would be able to change its ownership
|
||||
* anyway.
|
||||
*/
|
||||
|
||||
memset(repl_null, ' ', sizeof(repl_null));
|
||||
@@ -860,7 +856,7 @@ assign_default_tablespace(const char *newval, bool doit, GucSource source)
|
||||
{
|
||||
/*
|
||||
* If we aren't inside a transaction, we cannot do database access so
|
||||
* cannot verify the name. Must accept the value on faith.
|
||||
* cannot verify the name. Must accept the value on faith.
|
||||
*/
|
||||
if (IsTransactionState())
|
||||
{
|
||||
@@ -895,15 +891,16 @@ GetDefaultTablespace(void)
|
||||
/* Fast path for default_tablespace == "" */
|
||||
if (default_tablespace == NULL || default_tablespace[0] == '\0')
|
||||
return InvalidOid;
|
||||
|
||||
/*
|
||||
* It is tempting to cache this lookup for more speed, but then we would
|
||||
* fail to detect the case where the tablespace was dropped since the
|
||||
* GUC variable was set. Note also that we don't complain if the value
|
||||
* fails to refer to an existing tablespace; we just silently return
|
||||
* InvalidOid, causing the new object to be created in the database's
|
||||
* tablespace.
|
||||
* fail to detect the case where the tablespace was dropped since the GUC
|
||||
* variable was set. Note also that we don't complain if the value fails
|
||||
* to refer to an existing tablespace; we just silently return InvalidOid,
|
||||
* causing the new object to be created in the database's tablespace.
|
||||
*/
|
||||
result = get_tablespace_oid(default_tablespace);
|
||||
|
||||
/*
|
||||
* Allow explicit specification of database's default tablespace in
|
||||
* default_tablespace without triggering permissions checks.
|
||||
@@ -1001,14 +998,14 @@ tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
char *linkloc;
|
||||
|
||||
/*
|
||||
* Attempt to coerce target directory to safe permissions. If
|
||||
* this fails, it doesn't exist or has the wrong owner.
|
||||
* Attempt to coerce target directory to safe permissions. If this
|
||||
* fails, it doesn't exist or has the wrong owner.
|
||||
*/
|
||||
if (chmod(location, 0700) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not set permissions on directory \"%s\": %m",
|
||||
location)));
|
||||
errmsg("could not set permissions on directory \"%s\": %m",
|
||||
location)));
|
||||
|
||||
/* Create or re-create the PG_VERSION file in the target directory */
|
||||
set_short_version(location);
|
||||
@@ -1022,8 +1019,8 @@ tblspc_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||
if (errno != EEXIST)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create symbolic link \"%s\": %m",
|
||||
linkloc)));
|
||||
errmsg("could not create symbolic link \"%s\": %m",
|
||||
linkloc)));
|
||||
}
|
||||
|
||||
pfree(linkloc);
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.194 2005/08/24 17:38:35 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.195 2005/10/15 02:49:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -52,7 +52,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
Instrumentation *instr,
|
||||
MemoryContext per_tuple_context);
|
||||
static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,
|
||||
bool row_trigger, HeapTuple oldtup, HeapTuple newtup);
|
||||
bool row_trigger, HeapTuple oldtup, HeapTuple newtup);
|
||||
|
||||
|
||||
/*
|
||||
@@ -98,15 +98,14 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
{
|
||||
/*
|
||||
* If this trigger is a constraint (and a foreign key one) then we
|
||||
* really need a constrrelid. Since we don't have one, we'll try
|
||||
* to generate one from the argument information.
|
||||
* really need a constrrelid. Since we don't have one, we'll try to
|
||||
* generate one from the argument information.
|
||||
*
|
||||
* This is really just a workaround for a long-ago pg_dump bug that
|
||||
* omitted the FROM clause in dumped CREATE CONSTRAINT TRIGGER
|
||||
* commands. We don't want to bomb out completely here if we
|
||||
* can't determine the correct relation, because that would
|
||||
* prevent loading the dump file. Instead, NOTICE here and ERROR
|
||||
* in the trigger.
|
||||
* commands. We don't want to bomb out completely here if we can't
|
||||
* determine the correct relation, because that would prevent loading
|
||||
* the dump file. Instead, NOTICE here and ERROR in the trigger.
|
||||
*/
|
||||
bool needconstrrelid = false;
|
||||
void *elem = NULL;
|
||||
@@ -181,8 +180,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the trigger's OID now, so that we can use it in the name
|
||||
* if needed.
|
||||
* Generate the trigger's OID now, so that we can use it in the name if
|
||||
* needed.
|
||||
*/
|
||||
tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
|
||||
|
||||
@@ -190,9 +189,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
|
||||
/*
|
||||
* If trigger is an RI constraint, use specified trigger name as
|
||||
* constraint name and build a unique trigger name instead. This is
|
||||
* mainly for backwards compatibility with CREATE CONSTRAINT TRIGGER
|
||||
* commands.
|
||||
* constraint name and build a unique trigger name instead. This is mainly
|
||||
* for backwards compatibility with CREATE CONSTRAINT TRIGGER commands.
|
||||
*/
|
||||
if (stmt->isconstraint)
|
||||
{
|
||||
@@ -246,10 +244,10 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan pg_trigger for existing triggers on relation. We do this
|
||||
* mainly because we must count them; a secondary benefit is to give a
|
||||
* nice error message if there's already a trigger of the same name.
|
||||
* (The unique index on tgrelid/tgname would complain anyway.)
|
||||
* Scan pg_trigger for existing triggers on relation. We do this mainly
|
||||
* because we must count them; a secondary benefit is to give a nice error
|
||||
* message if there's already a trigger of the same name. (The unique
|
||||
* index on tgrelid/tgname would complain anyway.)
|
||||
*
|
||||
* NOTE that this is cool only because we have AccessExclusiveLock on the
|
||||
* relation, so the trigger set won't be changing underneath us.
|
||||
@@ -267,8 +265,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("trigger \"%s\" for relation \"%s\" already exists",
|
||||
trigname, stmt->relation->relname)));
|
||||
errmsg("trigger \"%s\" for relation \"%s\" already exists",
|
||||
trigname, stmt->relation->relname)));
|
||||
found++;
|
||||
}
|
||||
systable_endscan(tgscan);
|
||||
@@ -281,8 +279,8 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
if (funcrettype != TRIGGEROID)
|
||||
{
|
||||
/*
|
||||
* We allow OPAQUE just so we can load old dump files. When we
|
||||
* see a trigger function declared OPAQUE, change it to TRIGGER.
|
||||
* We allow OPAQUE just so we can load old dump files. When we see a
|
||||
* trigger function declared OPAQUE, change it to TRIGGER.
|
||||
*/
|
||||
if (funcrettype == OPAQUEOID)
|
||||
{
|
||||
@@ -305,13 +303,13 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
|
||||
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
|
||||
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
|
||||
CStringGetDatum(trigname));
|
||||
CStringGetDatum(trigname));
|
||||
values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
|
||||
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
|
||||
values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
|
||||
values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
|
||||
values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
|
||||
CStringGetDatum(constrname));
|
||||
CStringGetDatum(constrname));
|
||||
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
|
||||
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
|
||||
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
|
||||
@@ -351,13 +349,13 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
}
|
||||
values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);
|
||||
values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
|
||||
CStringGetDatum(args));
|
||||
CStringGetDatum(args));
|
||||
}
|
||||
else
|
||||
{
|
||||
values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);
|
||||
values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,
|
||||
CStringGetDatum(""));
|
||||
CStringGetDatum(""));
|
||||
}
|
||||
/* tgattr is currently always a zero-length array */
|
||||
tgattr = buildint2vector(NULL, 0);
|
||||
@@ -386,9 +384,9 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));
|
||||
|
||||
/*
|
||||
* Update relation's pg_class entry. Crucial side-effect: other
|
||||
* backends (and this one too!) are sent SI message to make them
|
||||
* rebuild relcache entries.
|
||||
* Update relation's pg_class entry. Crucial side-effect: other backends
|
||||
* (and this one too!) are sent SI message to make them rebuild relcache
|
||||
* entries.
|
||||
*/
|
||||
pgrel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
tuple = SearchSysCacheCopy(RELOID,
|
||||
@@ -409,19 +407,18 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
|
||||
/*
|
||||
* We used to try to update the rel's relcache entry here, but that's
|
||||
* fairly pointless since it will happen as a byproduct of the
|
||||
* upcoming CommandCounterIncrement...
|
||||
* fairly pointless since it will happen as a byproduct of the upcoming
|
||||
* CommandCounterIncrement...
|
||||
*/
|
||||
|
||||
/*
|
||||
* Record dependencies for trigger. Always place a normal dependency
|
||||
* on the function. If we are doing this in response to an explicit
|
||||
* CREATE TRIGGER command, also make trigger be auto-dropped if its
|
||||
* relation is dropped or if the FK relation is dropped. (Auto drop
|
||||
* is compatible with our pre-7.3 behavior.) If the trigger is being
|
||||
* made for a constraint, we can skip the relation links; the
|
||||
* dependency on the constraint will indirectly depend on the
|
||||
* relations.
|
||||
* Record dependencies for trigger. Always place a normal dependency on
|
||||
* the function. If we are doing this in response to an explicit CREATE
|
||||
* TRIGGER command, also make trigger be auto-dropped if its relation is
|
||||
* dropped or if the FK relation is dropped. (Auto drop is compatible
|
||||
* with our pre-7.3 behavior.) If the trigger is being made for a
|
||||
* constraint, we can skip the relation links; the dependency on the
|
||||
* constraint will indirectly depend on the relations.
|
||||
*/
|
||||
referenced.classId = ProcedureRelationId;
|
||||
referenced.objectId = funcoid;
|
||||
@@ -565,13 +562,12 @@ RemoveTriggerById(Oid trigOid)
|
||||
heap_close(tgrel, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Update relation's pg_class entry. Crucial side-effect: other
|
||||
* backends (and this one too!) are sent SI message to make them
|
||||
* rebuild relcache entries.
|
||||
* Update relation's pg_class entry. Crucial side-effect: other backends
|
||||
* (and this one too!) are sent SI message to make them rebuild relcache
|
||||
* entries.
|
||||
*
|
||||
* Note this is OK only because we have AccessExclusiveLock on the rel,
|
||||
* so no one else is creating/deleting triggers on this rel at the
|
||||
* same time.
|
||||
* Note this is OK only because we have AccessExclusiveLock on the rel, so no
|
||||
* one else is creating/deleting triggers on this rel at the same time.
|
||||
*/
|
||||
pgrel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
tuple = SearchSysCacheCopy(RELOID,
|
||||
@@ -623,16 +619,16 @@ renametrig(Oid relid,
|
||||
ScanKeyData key[2];
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the target table, which we will NOT
|
||||
* release until end of transaction.
|
||||
* Grab an exclusive lock on the target table, which we will NOT release
|
||||
* until end of transaction.
|
||||
*/
|
||||
targetrel = heap_open(relid, AccessExclusiveLock);
|
||||
|
||||
/*
|
||||
* Scan pg_trigger twice for existing triggers on relation. We do
|
||||
* this in order to ensure a trigger does not exist with newname (The
|
||||
* unique index on tgrelid/tgname would complain anyway) and to ensure
|
||||
* a trigger does exist with oldname.
|
||||
* Scan pg_trigger twice for existing triggers on relation. We do this in
|
||||
* order to ensure a trigger does not exist with newname (The unique index
|
||||
* on tgrelid/tgname would complain anyway) and to ensure a trigger does
|
||||
* exist with oldname.
|
||||
*
|
||||
* NOTE that this is cool only because we have AccessExclusiveLock on the
|
||||
* relation, so the trigger set won't be changing underneath us.
|
||||
@@ -655,8 +651,8 @@ renametrig(Oid relid,
|
||||
if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("trigger \"%s\" for relation \"%s\" already exists",
|
||||
newname, RelationGetRelationName(targetrel))));
|
||||
errmsg("trigger \"%s\" for relation \"%s\" already exists",
|
||||
newname, RelationGetRelationName(targetrel))));
|
||||
systable_endscan(tgscan);
|
||||
|
||||
/*
|
||||
@@ -687,10 +683,9 @@ renametrig(Oid relid,
|
||||
CatalogUpdateIndexes(tgrel, tuple);
|
||||
|
||||
/*
|
||||
* Invalidate relation's relcache entry so that other backends
|
||||
* (and this one too!) are sent SI message to make them rebuild
|
||||
* relcache entries. (Ideally this should happen
|
||||
* automatically...)
|
||||
* Invalidate relation's relcache entry so that other backends (and
|
||||
* this one too!) are sent SI message to make them rebuild relcache
|
||||
* entries. (Ideally this should happen automatically...)
|
||||
*/
|
||||
CacheInvalidateRelcache(targetrel);
|
||||
}
|
||||
@@ -732,13 +727,13 @@ void
|
||||
EnableDisableTrigger(Relation rel, const char *tgname,
|
||||
bool enable, bool skip_system)
|
||||
{
|
||||
Relation tgrel;
|
||||
int nkeys;
|
||||
Relation tgrel;
|
||||
int nkeys;
|
||||
ScanKeyData keys[2];
|
||||
SysScanDesc tgscan;
|
||||
HeapTuple tuple;
|
||||
bool found;
|
||||
bool changed;
|
||||
HeapTuple tuple;
|
||||
bool found;
|
||||
bool changed;
|
||||
|
||||
/* Scan the relevant entries in pg_triggers */
|
||||
tgrel = heap_open(TriggerRelationId, RowExclusiveLock);
|
||||
@@ -775,8 +770,8 @@ EnableDisableTrigger(Relation rel, const char *tgname,
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("permission denied: \"%s\" is a system trigger",
|
||||
NameStr(oldtrig->tgname))));
|
||||
errmsg("permission denied: \"%s\" is a system trigger",
|
||||
NameStr(oldtrig->tgname))));
|
||||
}
|
||||
|
||||
found = true;
|
||||
@@ -784,7 +779,7 @@ EnableDisableTrigger(Relation rel, const char *tgname,
|
||||
if (oldtrig->tgenabled != enable)
|
||||
{
|
||||
/* need to change this one ... make a copy to scribble on */
|
||||
HeapTuple newtup = heap_copytuple(tuple);
|
||||
HeapTuple newtup = heap_copytuple(tuple);
|
||||
Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup);
|
||||
|
||||
newtrig->tgenabled = enable;
|
||||
@@ -848,10 +843,10 @@ RelationBuildTriggers(Relation relation)
|
||||
triggers = (Trigger *) palloc(ntrigs * sizeof(Trigger));
|
||||
|
||||
/*
|
||||
* Note: since we scan the triggers using TriggerRelidNameIndexId, we
|
||||
* will be reading the triggers in name order, except possibly during
|
||||
* emergency-recovery operations (ie, IsIgnoringSystemIndexes). This
|
||||
* in turn ensures that triggers will be fired in name order.
|
||||
* Note: since we scan the triggers using TriggerRelidNameIndexId, we will
|
||||
* be reading the triggers in name order, except possibly during
|
||||
* emergency-recovery operations (ie, IsIgnoringSystemIndexes). This in
|
||||
* turn ensures that triggers will be fired in name order.
|
||||
*/
|
||||
ScanKeyInit(&skey,
|
||||
Anum_pg_trigger_tgrelid,
|
||||
@@ -874,7 +869,7 @@ RelationBuildTriggers(Relation relation)
|
||||
|
||||
build->tgoid = HeapTupleGetOid(htup);
|
||||
build->tgname = DatumGetCString(DirectFunctionCall1(nameout,
|
||||
NameGetDatum(&pg_trigger->tgname)));
|
||||
NameGetDatum(&pg_trigger->tgname)));
|
||||
build->tgfoid = pg_trigger->tgfoid;
|
||||
build->tgtype = pg_trigger->tgtype;
|
||||
build->tgenabled = pg_trigger->tgenabled;
|
||||
@@ -1183,12 +1178,12 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
|
||||
j;
|
||||
|
||||
/*
|
||||
* We need not examine the "index" data, just the trigger array
|
||||
* itself; if we have the same triggers with the same types, the
|
||||
* derived index data should match.
|
||||
* We need not examine the "index" data, just the trigger array itself; if
|
||||
* we have the same triggers with the same types, the derived index data
|
||||
* should match.
|
||||
*
|
||||
* As of 7.3 we assume trigger set ordering is significant in the
|
||||
* comparison; so we just compare corresponding slots of the two sets.
|
||||
* As of 7.3 we assume trigger set ordering is significant in the comparison;
|
||||
* so we just compare corresponding slots of the two sets.
|
||||
*/
|
||||
if (trigdesc1 != NULL)
|
||||
{
|
||||
@@ -1279,9 +1274,9 @@ ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
|
||||
/*
|
||||
* Do the function evaluation in the per-tuple memory context, so that
|
||||
* leaked memory will be reclaimed once per tuple. Note in particular
|
||||
* that any new tuple created by the trigger function will live till
|
||||
* the end of the tuple cycle.
|
||||
* leaked memory will be reclaimed once per tuple. Note in particular that
|
||||
* any new tuple created by the trigger function will live till the end of
|
||||
* the tuple cycle.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(per_tuple_context);
|
||||
|
||||
@@ -1295,8 +1290,8 @@ ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
/*
|
||||
* Trigger protocol allows function to return a null pointer, but NOT
|
||||
* to set the isnull result flag.
|
||||
* Trigger protocol allows function to return a null pointer, but NOT to
|
||||
* set the isnull result flag.
|
||||
*/
|
||||
if (fcinfo.isnull)
|
||||
ereport(ERROR,
|
||||
@@ -1305,8 +1300,8 @@ ExecCallTriggerFunc(TriggerData *trigdata,
|
||||
fcinfo.flinfo->fn_oid)));
|
||||
|
||||
/*
|
||||
* If doing EXPLAIN ANALYZE, stop charging time to this trigger,
|
||||
* and count one "tuple returned" (really the number of firings).
|
||||
* If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
|
||||
* one "tuple returned" (really the number of firings).
|
||||
*/
|
||||
if (instr)
|
||||
InstrStopNode(instr + tgindx, true);
|
||||
@@ -1359,7 +1354,7 @@ ExecBSInsertTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
if (newtuple)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
|
||||
errmsg("BEFORE STATEMENT trigger cannot return a value")));
|
||||
errmsg("BEFORE STATEMENT trigger cannot return a value")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1470,7 +1465,7 @@ ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
if (newtuple)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
|
||||
errmsg("BEFORE STATEMENT trigger cannot return a value")));
|
||||
errmsg("BEFORE STATEMENT trigger cannot return a value")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1601,7 +1596,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
|
||||
if (newtuple)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
|
||||
errmsg("BEFORE STATEMENT trigger cannot return a value")));
|
||||
errmsg("BEFORE STATEMENT trigger cannot return a value")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1703,7 +1698,7 @@ GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo,
|
||||
|
||||
if (newSlot != NULL)
|
||||
{
|
||||
HTSU_Result test;
|
||||
HTSU_Result test;
|
||||
ItemPointerData update_ctid;
|
||||
TransactionId update_xmax;
|
||||
|
||||
@@ -1751,8 +1746,8 @@ ltrmark:;
|
||||
}
|
||||
|
||||
/*
|
||||
* if tuple was deleted or PlanQual failed for updated
|
||||
* tuple - we have not process this tuple!
|
||||
* if tuple was deleted or PlanQual failed for updated tuple -
|
||||
* we have not process this tuple!
|
||||
*/
|
||||
return NULL;
|
||||
|
||||
@@ -1799,7 +1794,7 @@ ltrmark:;
|
||||
* they will easily go away during subtransaction abort.
|
||||
*
|
||||
* Because the list of pending events can grow large, we go to some effort
|
||||
* to minimize memory consumption. We do not use the generic List mechanism
|
||||
* to minimize memory consumption. We do not use the generic List mechanism
|
||||
* but thread the events manually.
|
||||
*
|
||||
* XXX We need to be able to save the per-event data in a file if it grows too
|
||||
@@ -1832,7 +1827,7 @@ typedef struct SetConstraintStateData
|
||||
bool all_isdeferred;
|
||||
int numstates; /* number of trigstates[] entries in use */
|
||||
int numalloc; /* allocated size of trigstates[] */
|
||||
SetConstraintTriggerData trigstates[1]; /* VARIABLE LENGTH ARRAY */
|
||||
SetConstraintTriggerData trigstates[1]; /* VARIABLE LENGTH ARRAY */
|
||||
} SetConstraintStateData;
|
||||
|
||||
typedef SetConstraintStateData *SetConstraintState;
|
||||
@@ -1849,12 +1844,12 @@ typedef struct AfterTriggerEventData *AfterTriggerEvent;
|
||||
|
||||
typedef struct AfterTriggerEventData
|
||||
{
|
||||
AfterTriggerEvent ate_next; /* list link */
|
||||
TriggerEvent ate_event; /* event type and status bits */
|
||||
CommandId ate_firing_id; /* ID for firing cycle */
|
||||
Oid ate_tgoid; /* the trigger's ID */
|
||||
Oid ate_relid; /* the relation it's on */
|
||||
ItemPointerData ate_oldctid; /* specific tuple(s) involved */
|
||||
AfterTriggerEvent ate_next; /* list link */
|
||||
TriggerEvent ate_event; /* event type and status bits */
|
||||
CommandId ate_firing_id; /* ID for firing cycle */
|
||||
Oid ate_tgoid; /* the trigger's ID */
|
||||
Oid ate_relid; /* the relation it's on */
|
||||
ItemPointerData ate_oldctid; /* specific tuple(s) involved */
|
||||
ItemPointerData ate_newctid;
|
||||
} AfterTriggerEventData;
|
||||
|
||||
@@ -1873,7 +1868,7 @@ typedef struct AfterTriggerEventList
|
||||
*
|
||||
* firing_counter is incremented for each call of afterTriggerInvokeEvents.
|
||||
* We mark firable events with the current firing cycle's ID so that we can
|
||||
* tell which ones to work on. This ensures sane behavior if a trigger
|
||||
* tell which ones to work on. This ensures sane behavior if a trigger
|
||||
* function chooses to do SET CONSTRAINTS: the inner SET CONSTRAINTS will
|
||||
* only fire those events that weren't already scheduled for firing.
|
||||
*
|
||||
@@ -1881,7 +1876,7 @@ typedef struct AfterTriggerEventList
|
||||
* This is saved and restored across failed subtransactions.
|
||||
*
|
||||
* events is the current list of deferred events. This is global across
|
||||
* all subtransactions of the current transaction. In a subtransaction
|
||||
* all subtransactions of the current transaction. In a subtransaction
|
||||
* abort, we know that the events added by the subtransaction are at the
|
||||
* end of the list, so it is relatively easy to discard them.
|
||||
*
|
||||
@@ -1908,31 +1903,31 @@ typedef struct AfterTriggerEventList
|
||||
* which we similarly use to clean up at subtransaction abort.
|
||||
*
|
||||
* firing_stack is a stack of copies of subtransaction-start-time
|
||||
* firing_counter. We use this to recognize which deferred triggers were
|
||||
* firing_counter. We use this to recognize which deferred triggers were
|
||||
* fired (or marked for firing) within an aborted subtransaction.
|
||||
*
|
||||
* We use GetCurrentTransactionNestLevel() to determine the correct array
|
||||
* index in these stacks. maxtransdepth is the number of allocated entries in
|
||||
* each stack. (By not keeping our own stack pointer, we can avoid trouble
|
||||
* each stack. (By not keeping our own stack pointer, we can avoid trouble
|
||||
* in cases where errors during subxact abort cause multiple invocations
|
||||
* of AfterTriggerEndSubXact() at the same nesting depth.)
|
||||
*/
|
||||
typedef struct AfterTriggersData
|
||||
{
|
||||
CommandId firing_counter; /* next firing ID to assign */
|
||||
SetConstraintState state; /* the active S C state */
|
||||
CommandId firing_counter; /* next firing ID to assign */
|
||||
SetConstraintState state; /* the active S C state */
|
||||
AfterTriggerEventList events; /* deferred-event list */
|
||||
int query_depth; /* current query list index */
|
||||
AfterTriggerEventList *query_stack; /* events pending from each query */
|
||||
int maxquerydepth; /* allocated len of above array */
|
||||
int query_depth; /* current query list index */
|
||||
AfterTriggerEventList *query_stack; /* events pending from each query */
|
||||
int maxquerydepth; /* allocated len of above array */
|
||||
|
||||
/* these fields are just for resetting at subtrans abort: */
|
||||
|
||||
SetConstraintState *state_stack; /* stacked S C states */
|
||||
AfterTriggerEventList *events_stack; /* stacked list pointers */
|
||||
int *depth_stack; /* stacked query_depths */
|
||||
CommandId *firing_stack; /* stacked firing_counters */
|
||||
int maxtransdepth; /* allocated len of above arrays */
|
||||
AfterTriggerEventList *events_stack; /* stacked list pointers */
|
||||
int *depth_stack; /* stacked query_depths */
|
||||
CommandId *firing_stack; /* stacked firing_counters */
|
||||
int maxtransdepth; /* allocated len of above arrays */
|
||||
} AfterTriggersData;
|
||||
|
||||
typedef AfterTriggersData *AfterTriggers;
|
||||
@@ -1941,14 +1936,14 @@ static AfterTriggers afterTriggers;
|
||||
|
||||
|
||||
static void AfterTriggerExecute(AfterTriggerEvent event,
|
||||
Relation rel, TriggerDesc *trigdesc,
|
||||
FmgrInfo *finfo,
|
||||
Instrumentation *instr,
|
||||
MemoryContext per_tuple_context);
|
||||
Relation rel, TriggerDesc *trigdesc,
|
||||
FmgrInfo *finfo,
|
||||
Instrumentation *instr,
|
||||
MemoryContext per_tuple_context);
|
||||
static SetConstraintState SetConstraintStateCreate(int numalloc);
|
||||
static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
|
||||
static SetConstraintState SetConstraintStateAddItem(SetConstraintState state,
|
||||
Oid tgoid, bool tgisdeferred);
|
||||
Oid tgoid, bool tgisdeferred);
|
||||
|
||||
|
||||
/* ----------
|
||||
@@ -2075,8 +2070,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
|
||||
elog(ERROR, "could not find trigger %u", tgoid);
|
||||
|
||||
/*
|
||||
* If doing EXPLAIN ANALYZE, start charging time to this trigger.
|
||||
* We want to include time spent re-fetching tuples in the trigger cost.
|
||||
* If doing EXPLAIN ANALYZE, start charging time to this trigger. We want
|
||||
* to include time spent re-fetching tuples in the trigger cost.
|
||||
*/
|
||||
if (instr)
|
||||
InstrStartNode(instr + tgindx);
|
||||
@@ -2133,8 +2128,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
|
||||
MemoryContextReset(per_tuple_context);
|
||||
|
||||
/*
|
||||
* Call the trigger and throw away any possibly returned updated
|
||||
* tuple. (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
|
||||
* Call the trigger and throw away any possibly returned updated tuple.
|
||||
* (Don't let ExecCallTriggerFunc measure EXPLAIN time.)
|
||||
*/
|
||||
rettuple = ExecCallTriggerFunc(&LocTriggerData,
|
||||
tgindx,
|
||||
@@ -2153,8 +2148,8 @@ AfterTriggerExecute(AfterTriggerEvent event,
|
||||
ReleaseBuffer(newbuffer);
|
||||
|
||||
/*
|
||||
* If doing EXPLAIN ANALYZE, stop charging time to this trigger,
|
||||
* and count one "tuple returned" (really the number of firings).
|
||||
* If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count
|
||||
* one "tuple returned" (really the number of firings).
|
||||
*/
|
||||
if (instr)
|
||||
InstrStopNode(instr + tgindx, true);
|
||||
@@ -2264,7 +2259,7 @@ afterTriggerMarkEvents(AfterTriggerEventList *events,
|
||||
*
|
||||
* If estate isn't NULL, then we expect that all the firable events are
|
||||
* for triggers of the relations included in the estate's result relation
|
||||
* array. This allows us to re-use the estate's open relations and
|
||||
* array. This allows us to re-use the estate's open relations and
|
||||
* trigger cache info. When estate is NULL, we have to find the relations
|
||||
* the hard way.
|
||||
*
|
||||
@@ -2308,8 +2303,8 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
event->ate_firing_id == firing_id)
|
||||
{
|
||||
/*
|
||||
* So let's fire it... but first, open the correct
|
||||
* relation if this is not the same relation as before.
|
||||
* So let's fire it... but first, open the correct relation if
|
||||
* this is not the same relation as before.
|
||||
*/
|
||||
if (rel == NULL || rel->rd_id != event->ate_relid)
|
||||
{
|
||||
@@ -2317,7 +2312,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
{
|
||||
/* Find target relation among estate's result rels */
|
||||
ResultRelInfo *rInfo;
|
||||
int nr;
|
||||
int nr;
|
||||
|
||||
rInfo = estate->es_result_relations;
|
||||
nr = estate->es_num_result_relations;
|
||||
@@ -2328,7 +2323,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
rInfo++;
|
||||
nr--;
|
||||
}
|
||||
if (nr <= 0) /* should not happen */
|
||||
if (nr <= 0) /* should not happen */
|
||||
elog(ERROR, "could not find relation %u among query result relations",
|
||||
event->ate_relid);
|
||||
rel = rInfo->ri_RelationDesc;
|
||||
@@ -2345,17 +2340,17 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
FreeTriggerDesc(trigdesc);
|
||||
if (finfo)
|
||||
pfree(finfo);
|
||||
Assert(instr == NULL); /* never used in this case */
|
||||
Assert(instr == NULL); /* never used in this case */
|
||||
|
||||
/*
|
||||
* We assume that an appropriate lock is still held by
|
||||
* the executor, so grab no new lock here.
|
||||
* We assume that an appropriate lock is still held by the
|
||||
* executor, so grab no new lock here.
|
||||
*/
|
||||
rel = heap_open(event->ate_relid, NoLock);
|
||||
|
||||
/*
|
||||
* Copy relation's trigger info so that we have a
|
||||
* stable copy no matter what the called triggers do.
|
||||
* Copy relation's trigger info so that we have a stable
|
||||
* copy no matter what the called triggers do.
|
||||
*/
|
||||
trigdesc = CopyTriggerDesc(rel->trigdesc);
|
||||
|
||||
@@ -2364,8 +2359,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
event->ate_relid);
|
||||
|
||||
/*
|
||||
* Allocate space to cache fmgr lookup info for
|
||||
* triggers.
|
||||
* Allocate space to cache fmgr lookup info for triggers.
|
||||
*/
|
||||
finfo = (FmgrInfo *)
|
||||
palloc0(trigdesc->numtriggers * sizeof(FmgrInfo));
|
||||
@@ -2376,8 +2370,8 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
|
||||
/*
|
||||
* Fire it. Note that the AFTER_TRIGGER_IN_PROGRESS flag is still
|
||||
* set, so recursive examinations of the event list won't try
|
||||
* to re-fire it.
|
||||
* set, so recursive examinations of the event list won't try to
|
||||
* re-fire it.
|
||||
*/
|
||||
AfterTriggerExecute(event, rel, trigdesc, finfo, instr,
|
||||
per_tuple_context);
|
||||
@@ -2393,9 +2387,9 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events,
|
||||
* If it's now done, throw it away, if allowed.
|
||||
*
|
||||
* NB: it's possible the trigger call above added more events to the
|
||||
* queue, or that calls we will do later will want to add more, so
|
||||
* we have to be careful about maintaining list validity at all
|
||||
* points here.
|
||||
* queue, or that calls we will do later will want to add more, so we
|
||||
* have to be careful about maintaining list validity at all points
|
||||
* here.
|
||||
*/
|
||||
next_event = event->ate_next;
|
||||
|
||||
@@ -2499,7 +2493,7 @@ AfterTriggerBeginQuery(void)
|
||||
if (afterTriggers->query_depth >= afterTriggers->maxquerydepth)
|
||||
{
|
||||
/* repalloc will keep the stack in the same context */
|
||||
int new_alloc = afterTriggers->maxquerydepth * 2;
|
||||
int new_alloc = afterTriggers->maxquerydepth * 2;
|
||||
|
||||
afterTriggers->query_stack = (AfterTriggerEventList *)
|
||||
repalloc(afterTriggers->query_stack,
|
||||
@@ -2537,21 +2531,21 @@ AfterTriggerEndQuery(EState *estate)
|
||||
Assert(afterTriggers->query_depth >= 0);
|
||||
|
||||
/*
|
||||
* Process all immediate-mode triggers queued by the query, and move
|
||||
* the deferred ones to the main list of deferred events.
|
||||
* Process all immediate-mode triggers queued by the query, and move the
|
||||
* deferred ones to the main list of deferred events.
|
||||
*
|
||||
* Notice that we decide which ones will be fired, and put the deferred
|
||||
* ones on the main list, before anything is actually fired. This
|
||||
* ensures reasonably sane behavior if a trigger function does
|
||||
* SET CONSTRAINTS ... IMMEDIATE: all events we have decided to defer
|
||||
* will be available for it to fire.
|
||||
* Notice that we decide which ones will be fired, and put the deferred ones
|
||||
* on the main list, before anything is actually fired. This ensures
|
||||
* reasonably sane behavior if a trigger function does SET CONSTRAINTS ...
|
||||
* IMMEDIATE: all events we have decided to defer will be available for it
|
||||
* to fire.
|
||||
*
|
||||
* If we find no firable events, we don't have to increment firing_counter.
|
||||
*/
|
||||
events = &afterTriggers->query_stack[afterTriggers->query_depth];
|
||||
if (afterTriggerMarkEvents(events, &afterTriggers->events, true))
|
||||
{
|
||||
CommandId firing_id = afterTriggers->firing_counter++;
|
||||
CommandId firing_id = afterTriggers->firing_counter++;
|
||||
|
||||
/* OK to delete the immediate events after processing them */
|
||||
afterTriggerInvokeEvents(events, firing_id, estate, true);
|
||||
@@ -2584,21 +2578,21 @@ AfterTriggerFireDeferred(void)
|
||||
Assert(afterTriggers->query_depth == -1);
|
||||
|
||||
/*
|
||||
* If there are any triggers to fire, make sure we have set a snapshot
|
||||
* for them to use. (Since PortalRunUtility doesn't set a snap for
|
||||
* COMMIT, we can't assume ActiveSnapshot is valid on entry.)
|
||||
* If there are any triggers to fire, make sure we have set a snapshot for
|
||||
* them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we
|
||||
* can't assume ActiveSnapshot is valid on entry.)
|
||||
*/
|
||||
events = &afterTriggers->events;
|
||||
if (events->head != NULL)
|
||||
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
|
||||
|
||||
/*
|
||||
* Run all the remaining triggers. Loop until they are all gone,
|
||||
* just in case some trigger queues more for us to do.
|
||||
* Run all the remaining triggers. Loop until they are all gone, just in
|
||||
* case some trigger queues more for us to do.
|
||||
*/
|
||||
while (afterTriggerMarkEvents(events, NULL, false))
|
||||
{
|
||||
CommandId firing_id = afterTriggers->firing_counter++;
|
||||
CommandId firing_id = afterTriggers->firing_counter++;
|
||||
|
||||
afterTriggerInvokeEvents(events, firing_id, NULL, true);
|
||||
}
|
||||
@@ -2643,7 +2637,7 @@ AfterTriggerBeginSubXact(void)
|
||||
int my_level = GetCurrentTransactionNestLevel();
|
||||
|
||||
/*
|
||||
* Ignore call if the transaction is in aborted state. (Probably
|
||||
* Ignore call if the transaction is in aborted state. (Probably
|
||||
* shouldn't happen?)
|
||||
*/
|
||||
if (afterTriggers == NULL)
|
||||
@@ -2676,7 +2670,7 @@ AfterTriggerBeginSubXact(void)
|
||||
else
|
||||
{
|
||||
/* repalloc will keep the stacks in the same context */
|
||||
int new_alloc = afterTriggers->maxtransdepth * 2;
|
||||
int new_alloc = afterTriggers->maxtransdepth * 2;
|
||||
|
||||
afterTriggers->state_stack = (SetConstraintState *)
|
||||
repalloc(afterTriggers->state_stack,
|
||||
@@ -2695,8 +2689,8 @@ AfterTriggerBeginSubXact(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Push the current information into the stack. The SET CONSTRAINTS
|
||||
* state is not saved until/unless changed.
|
||||
* Push the current information into the stack. The SET CONSTRAINTS state
|
||||
* is not saved until/unless changed.
|
||||
*/
|
||||
afterTriggers->state_stack[my_level] = NULL;
|
||||
afterTriggers->events_stack[my_level] = afterTriggers->events;
|
||||
@@ -2718,7 +2712,8 @@ AfterTriggerEndSubXact(bool isCommit)
|
||||
CommandId subxact_firing_id;
|
||||
|
||||
/*
|
||||
* Ignore call if the transaction is in aborted state. (Probably unneeded)
|
||||
* Ignore call if the transaction is in aborted state. (Probably
|
||||
* unneeded)
|
||||
*/
|
||||
if (afterTriggers == NULL)
|
||||
return;
|
||||
@@ -2759,8 +2754,8 @@ AfterTriggerEndSubXact(bool isCommit)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Restore the trigger state. If the saved state is NULL, then
|
||||
* this subxact didn't save it, so it doesn't need restoring.
|
||||
* Restore the trigger state. If the saved state is NULL, then this
|
||||
* subxact didn't save it, so it doesn't need restoring.
|
||||
*/
|
||||
state = afterTriggers->state_stack[my_level];
|
||||
if (state != NULL)
|
||||
@@ -2772,12 +2767,12 @@ AfterTriggerEndSubXact(bool isCommit)
|
||||
afterTriggers->state_stack[my_level] = NULL;
|
||||
|
||||
/*
|
||||
* Scan for any remaining deferred events that were marked DONE
|
||||
* or IN PROGRESS by this subxact or a child, and un-mark them.
|
||||
* We can recognize such events because they have a firing ID
|
||||
* greater than or equal to the firing_counter value we saved at
|
||||
* subtransaction start. (This essentially assumes that the
|
||||
* current subxact includes all subxacts started after it.)
|
||||
* Scan for any remaining deferred events that were marked DONE or IN
|
||||
* PROGRESS by this subxact or a child, and un-mark them. We can
|
||||
* recognize such events because they have a firing ID greater than or
|
||||
* equal to the firing_counter value we saved at subtransaction start.
|
||||
* (This essentially assumes that the current subxact includes all
|
||||
* subxacts started after it.)
|
||||
*/
|
||||
subxact_firing_id = afterTriggers->firing_stack[my_level];
|
||||
for (event = afterTriggers->events.head;
|
||||
@@ -2813,7 +2808,7 @@ SetConstraintStateCreate(int numalloc)
|
||||
state = (SetConstraintState)
|
||||
MemoryContextAllocZero(TopTransactionContext,
|
||||
sizeof(SetConstraintStateData) +
|
||||
(numalloc - 1) *sizeof(SetConstraintTriggerData));
|
||||
(numalloc - 1) *sizeof(SetConstraintTriggerData));
|
||||
|
||||
state->numalloc = numalloc;
|
||||
|
||||
@@ -2840,7 +2835,7 @@ SetConstraintStateCopy(SetConstraintState origstate)
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a per-trigger item to a SetConstraintState. Returns possibly-changed
|
||||
* Add a per-trigger item to a SetConstraintState. Returns possibly-changed
|
||||
* pointer to the state object (it will change if we have to repalloc).
|
||||
*/
|
||||
static SetConstraintState
|
||||
@@ -2885,9 +2880,8 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If in a subtransaction, and we didn't save the current state
|
||||
* already, save it so it can be restored if the subtransaction
|
||||
* aborts.
|
||||
* If in a subtransaction, and we didn't save the current state already,
|
||||
* save it so it can be restored if the subtransaction aborts.
|
||||
*/
|
||||
if (my_level > 1 &&
|
||||
afterTriggers->state_stack[my_level] == NULL)
|
||||
@@ -2939,7 +2933,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
if (strlen(cname) == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("unnamed constraints cannot be set explicitly")));
|
||||
errmsg("unnamed constraints cannot be set explicitly")));
|
||||
|
||||
/*
|
||||
* Setup to scan pg_trigger by tgconstrname ...
|
||||
@@ -2962,9 +2956,9 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
|
||||
|
||||
/*
|
||||
* If we found some, check that they fit the deferrability
|
||||
* but skip referential action ones, since they are
|
||||
* silently never deferrable.
|
||||
* If we found some, check that they fit the deferrability but
|
||||
* skip referential action ones, since they are silently never
|
||||
* deferrable.
|
||||
*/
|
||||
if (pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
|
||||
pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL &&
|
||||
@@ -3026,15 +3020,15 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* SQL99 requires that when a constraint is set to IMMEDIATE, any
|
||||
* deferred checks against that constraint must be made when the SET
|
||||
* CONSTRAINTS command is executed -- i.e. the effects of the SET
|
||||
* CONSTRAINTS command apply retroactively. We've updated the
|
||||
* constraints state, so scan the list of previously deferred events
|
||||
* to fire any that have now become immediate.
|
||||
* SQL99 requires that when a constraint is set to IMMEDIATE, any deferred
|
||||
* checks against that constraint must be made when the SET CONSTRAINTS
|
||||
* command is executed -- i.e. the effects of the SET CONSTRAINTS command
|
||||
* apply retroactively. We've updated the constraints state, so scan the
|
||||
* list of previously deferred events to fire any that have now become
|
||||
* immediate.
|
||||
*
|
||||
* Obviously, if this was SET ... DEFERRED then it can't have converted
|
||||
* any unfired events to immediate, so we need do nothing in that case.
|
||||
* Obviously, if this was SET ... DEFERRED then it can't have converted any
|
||||
* unfired events to immediate, so we need do nothing in that case.
|
||||
*/
|
||||
if (!stmt->deferred)
|
||||
{
|
||||
@@ -3042,12 +3036,12 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
|
||||
|
||||
if (afterTriggerMarkEvents(events, NULL, true))
|
||||
{
|
||||
CommandId firing_id = afterTriggers->firing_counter++;
|
||||
CommandId firing_id = afterTriggers->firing_counter++;
|
||||
|
||||
/*
|
||||
* We can delete fired events if we are at top transaction
|
||||
* level, but we'd better not if inside a subtransaction, since
|
||||
* the subtransaction could later get rolled back.
|
||||
* We can delete fired events if we are at top transaction level,
|
||||
* but we'd better not if inside a subtransaction, since the
|
||||
* subtransaction could later get rolled back.
|
||||
*/
|
||||
afterTriggerInvokeEvents(events, firing_id, NULL,
|
||||
!IsSubTransaction());
|
||||
@@ -3116,9 +3110,9 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If this is an UPDATE of a PK table or FK table that does
|
||||
* not change the PK or FK respectively, we can skip queuing
|
||||
* the event: there is no need to fire the trigger.
|
||||
* If this is an UPDATE of a PK table or FK table that does not change
|
||||
* the PK or FK respectively, we can skip queuing the event: there is
|
||||
* no need to fire the trigger.
|
||||
*/
|
||||
if ((event & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE)
|
||||
{
|
||||
@@ -3134,17 +3128,17 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
|
||||
break;
|
||||
|
||||
case RI_TRIGGER_FK:
|
||||
|
||||
/*
|
||||
* Update on FK table
|
||||
*
|
||||
* There is one exception when updating FK tables:
|
||||
* if the updated row was inserted by our own
|
||||
* transaction and the FK is deferred, we still
|
||||
* need to fire the trigger. This is because our
|
||||
* UPDATE will invalidate the INSERT so the
|
||||
* end-of-transaction INSERT RI trigger will not
|
||||
* do anything, so we have to do the check for the
|
||||
* UPDATE anyway.
|
||||
* There is one exception when updating FK tables: if the
|
||||
* updated row was inserted by our own transaction and the
|
||||
* FK is deferred, we still need to fire the trigger. This
|
||||
* is because our UPDATE will invalidate the INSERT so the
|
||||
* end-of-transaction INSERT RI trigger will not do
|
||||
* anything, so we have to do the check for the UPDATE
|
||||
* anyway.
|
||||
*/
|
||||
if (HeapTupleHeaderGetXmin(oldtup->t_data) !=
|
||||
GetCurrentTransactionId() &&
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.80 2005/08/22 17:38:20 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.81 2005/10/15 02:49:16 momjian Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -130,8 +130,7 @@ DefineType(List *names, List *parameters)
|
||||
|
||||
/*
|
||||
* Type names must be one character shorter than other names, allowing
|
||||
* room to create the corresponding array type name with prepended
|
||||
* "_".
|
||||
* room to create the corresponding array type name with prepended "_".
|
||||
*/
|
||||
if (strlen(typeName) > (NAMEDATALEN - 2))
|
||||
ereport(ERROR,
|
||||
@@ -183,10 +182,9 @@ DefineType(List *names, List *parameters)
|
||||
char *a = defGetString(defel);
|
||||
|
||||
/*
|
||||
* Note: if argument was an unquoted identifier, parser will
|
||||
* have applied translations to it, so be prepared to
|
||||
* recognize translated type names as well as the nominal
|
||||
* form.
|
||||
* Note: if argument was an unquoted identifier, parser will have
|
||||
* applied translations to it, so be prepared to recognize
|
||||
* translated type names as well as the nominal form.
|
||||
*/
|
||||
if (pg_strcasecmp(a, "double") == 0 ||
|
||||
pg_strcasecmp(a, "float8") == 0 ||
|
||||
@@ -303,8 +301,8 @@ DefineType(List *names, List *parameters)
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("type output function %s must return type \"cstring\"",
|
||||
NameListToString(outputName))));
|
||||
errmsg("type output function %s must return type \"cstring\"",
|
||||
NameListToString(outputName))));
|
||||
}
|
||||
if (receiveOid)
|
||||
{
|
||||
@@ -312,8 +310,8 @@ DefineType(List *names, List *parameters)
|
||||
if (resulttype != typoid)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("type receive function %s must return type %s",
|
||||
NameListToString(receiveName), typeName)));
|
||||
errmsg("type receive function %s must return type %s",
|
||||
NameListToString(receiveName), typeName)));
|
||||
}
|
||||
if (sendOid)
|
||||
{
|
||||
@@ -321,14 +319,13 @@ DefineType(List *names, List *parameters)
|
||||
if (resulttype != BYTEAOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("type send function %s must return type \"bytea\"",
|
||||
NameListToString(sendName))));
|
||||
errmsg("type send function %s must return type \"bytea\"",
|
||||
NameListToString(sendName))));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert analysis function proc name to an OID. If no analysis
|
||||
* function is specified, we'll use zero to select the built-in
|
||||
* default algorithm.
|
||||
* Convert analysis function proc name to an OID. If no analysis function
|
||||
* is specified, we'll use zero to select the built-in default algorithm.
|
||||
*/
|
||||
if (analyzeName)
|
||||
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
|
||||
@@ -361,8 +358,8 @@ DefineType(List *names, List *parameters)
|
||||
false); /* Type NOT NULL */
|
||||
|
||||
/*
|
||||
* When we create a base type (as opposed to a complex type) we need
|
||||
* to have an array entry for it in pg_type as well.
|
||||
* When we create a base type (as opposed to a complex type) we need to
|
||||
* have an array entry for it in pg_type as well.
|
||||
*/
|
||||
shadow_type = makeArrayTypeName(typeName);
|
||||
|
||||
@@ -430,8 +427,8 @@ RemoveType(List *names, DropBehavior behavior)
|
||||
|
||||
/* Permission check: must own type or its namespace */
|
||||
if (!pg_type_ownercheck(typeoid, GetUserId()) &&
|
||||
!pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
|
||||
GetUserId()))
|
||||
!pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
|
||||
TypeNameToString(typename));
|
||||
|
||||
@@ -522,12 +519,11 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
get_namespace_name(domainNamespace));
|
||||
|
||||
/*
|
||||
* Domainnames, unlike typenames don't need to account for the '_'
|
||||
* prefix. So they can be one character longer. (This test is
|
||||
* presently useless since the parser will have truncated the name to
|
||||
* fit. But leave it here since we may someday support arrays of
|
||||
* domains, in which case we'll be back to needing to enforce
|
||||
* NAMEDATALEN-2.)
|
||||
* Domainnames, unlike typenames don't need to account for the '_' prefix.
|
||||
* So they can be one character longer. (This test is presently useless
|
||||
* since the parser will have truncated the name to fit. But leave it
|
||||
* here since we may someday support arrays of domains, in which case
|
||||
* we'll be back to needing to enforce NAMEDATALEN-2.)
|
||||
*/
|
||||
if (strlen(domainName) > (NAMEDATALEN - 1))
|
||||
ereport(ERROR,
|
||||
@@ -544,10 +540,9 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
basetypeoid = HeapTupleGetOid(typeTup);
|
||||
|
||||
/*
|
||||
* Base type must be a plain base type. Domains over pseudo types
|
||||
* would create a security hole. Domains of domains might be made to
|
||||
* work in the future, but not today. Ditto for domains over complex
|
||||
* types.
|
||||
* Base type must be a plain base type. Domains over pseudo types would
|
||||
* create a security hole. Domains of domains might be made to work in
|
||||
* the future, but not today. Ditto for domains over complex types.
|
||||
*/
|
||||
typtype = baseType->typtype;
|
||||
if (typtype != 'b')
|
||||
@@ -613,7 +608,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
if (IsA(newConstraint, FkConstraint))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("foreign key constraints not possible for domains")));
|
||||
errmsg("foreign key constraints not possible for domains")));
|
||||
|
||||
/* otherwise it should be a plain Constraint */
|
||||
if (!IsA(newConstraint, Constraint))
|
||||
@@ -627,8 +622,8 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
case CONSTR_DEFAULT:
|
||||
|
||||
/*
|
||||
* The inherited default value may be overridden by the
|
||||
* user with the DEFAULT <expr> statement.
|
||||
* The inherited default value may be overridden by the user
|
||||
* with the DEFAULT <expr> statement.
|
||||
*/
|
||||
if (defaultExpr)
|
||||
ereport(ERROR,
|
||||
@@ -639,8 +634,8 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
/*
|
||||
* Cook the constr->raw_expr into an expression. Note:
|
||||
* Name is strictly for error message
|
||||
* Cook the constr->raw_expr into an expression. Note: Name is
|
||||
* strictly for error message
|
||||
*/
|
||||
defaultExpr = cookDefault(pstate, constr->raw_expr,
|
||||
basetypeoid,
|
||||
@@ -648,13 +643,13 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
domainName);
|
||||
|
||||
/*
|
||||
* Expression must be stored as a nodeToString result, but
|
||||
* we also require a valid textual representation (mainly
|
||||
* to make life easier for pg_dump).
|
||||
* Expression must be stored as a nodeToString result, but we
|
||||
* also require a valid textual representation (mainly to make
|
||||
* life easier for pg_dump).
|
||||
*/
|
||||
defaultValue = deparse_expression(defaultExpr,
|
||||
deparse_context_for(domainName,
|
||||
InvalidOid),
|
||||
deparse_context_for(domainName,
|
||||
InvalidOid),
|
||||
false, false);
|
||||
defaultValueBin = nodeToString(defaultExpr);
|
||||
break;
|
||||
@@ -663,7 +658,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
if (nullDefined && !typNotNull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting NULL/NOT NULL constraints")));
|
||||
errmsg("conflicting NULL/NOT NULL constraints")));
|
||||
typNotNull = true;
|
||||
nullDefined = true;
|
||||
break;
|
||||
@@ -672,7 +667,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
if (nullDefined && typNotNull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("conflicting NULL/NOT NULL constraints")));
|
||||
errmsg("conflicting NULL/NOT NULL constraints")));
|
||||
typNotNull = false;
|
||||
nullDefined = true;
|
||||
break;
|
||||
@@ -691,13 +686,13 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
case CONSTR_UNIQUE:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unique constraints not possible for domains")));
|
||||
errmsg("unique constraints not possible for domains")));
|
||||
break;
|
||||
|
||||
case CONSTR_PRIMARY:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("primary key constraints not possible for domains")));
|
||||
errmsg("primary key constraints not possible for domains")));
|
||||
break;
|
||||
|
||||
case CONSTR_ATTR_DEFERRABLE:
|
||||
@@ -744,8 +739,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
typNotNull); /* Type NOT NULL */
|
||||
|
||||
/*
|
||||
* Process constraints which refer to the domain ID returned by
|
||||
* TypeCreate
|
||||
* Process constraints which refer to the domain ID returned by TypeCreate
|
||||
*/
|
||||
foreach(listptr, schema)
|
||||
{
|
||||
@@ -815,8 +809,8 @@ RemoveDomain(List *names, DropBehavior behavior)
|
||||
|
||||
/* Permission check: must own type or its namespace */
|
||||
if (!pg_type_ownercheck(typeoid, GetUserId()) &&
|
||||
!pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
|
||||
GetUserId()))
|
||||
!pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
|
||||
TypeNameToString(typename));
|
||||
|
||||
@@ -856,11 +850,11 @@ findTypeInputFunction(List *procname, Oid typeOid)
|
||||
Oid procOid;
|
||||
|
||||
/*
|
||||
* Input functions can take a single argument of type CSTRING, or
|
||||
* three arguments (string, typioparam OID, typmod).
|
||||
* Input functions can take a single argument of type CSTRING, or three
|
||||
* arguments (string, typioparam OID, typmod).
|
||||
*
|
||||
* For backwards compatibility we allow OPAQUE in place of CSTRING; if we
|
||||
* see this, we issue a warning and fix up the pg_proc entry.
|
||||
* For backwards compatibility we allow OPAQUE in place of CSTRING; if we see
|
||||
* this, we issue a warning and fix up the pg_proc entry.
|
||||
*/
|
||||
argList[0] = CSTRINGOID;
|
||||
|
||||
@@ -897,8 +891,8 @@ findTypeInputFunction(List *procname, Oid typeOid)
|
||||
SetFunctionArgType(procOid, 0, CSTRINGOID);
|
||||
|
||||
/*
|
||||
* Need CommandCounterIncrement since DefineType will likely try
|
||||
* to alter the pg_proc tuple again.
|
||||
* Need CommandCounterIncrement since DefineType will likely try to
|
||||
* alter the pg_proc tuple again.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
@@ -925,9 +919,8 @@ findTypeOutputFunction(List *procname, Oid typeOid)
|
||||
/*
|
||||
* Output functions can take a single argument of the type.
|
||||
*
|
||||
* For backwards compatibility we allow OPAQUE in place of the actual
|
||||
* type name; if we see this, we issue a warning and fix up the
|
||||
* pg_proc entry.
|
||||
* For backwards compatibility we allow OPAQUE in place of the actual type
|
||||
* name; if we see this, we issue a warning and fix up the pg_proc entry.
|
||||
*/
|
||||
argList[0] = typeOid;
|
||||
|
||||
@@ -944,13 +937,13 @@ findTypeOutputFunction(List *procname, Oid typeOid)
|
||||
{
|
||||
/* Found, but must complain and fix the pg_proc entry */
|
||||
ereport(WARNING,
|
||||
(errmsg("changing argument type of function %s from \"opaque\" to %s",
|
||||
NameListToString(procname), format_type_be(typeOid))));
|
||||
(errmsg("changing argument type of function %s from \"opaque\" to %s",
|
||||
NameListToString(procname), format_type_be(typeOid))));
|
||||
SetFunctionArgType(procOid, 0, typeOid);
|
||||
|
||||
/*
|
||||
* Need CommandCounterIncrement since DefineType will likely try
|
||||
* to alter the pg_proc tuple again.
|
||||
* Need CommandCounterIncrement since DefineType will likely try to
|
||||
* alter the pg_proc tuple again.
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
@@ -975,8 +968,8 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
|
||||
Oid procOid;
|
||||
|
||||
/*
|
||||
* Receive functions can take a single argument of type INTERNAL, or
|
||||
* three arguments (internal, typioparam OID, typmod).
|
||||
* Receive functions can take a single argument of type INTERNAL, or three
|
||||
* arguments (internal, typioparam OID, typmod).
|
||||
*/
|
||||
argList[0] = INTERNALOID;
|
||||
|
||||
@@ -1029,8 +1022,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
|
||||
Oid procOid;
|
||||
|
||||
/*
|
||||
* Analyze functions always take one INTERNAL argument and return
|
||||
* bool.
|
||||
* Analyze functions always take one INTERNAL argument and return bool.
|
||||
*/
|
||||
argList[0] = INTERNALOID;
|
||||
|
||||
@@ -1044,8 +1036,8 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
|
||||
if (get_func_rettype(procOid) != BOOLOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("type analyze function %s must return type \"boolean\"",
|
||||
NameListToString(procname))));
|
||||
errmsg("type analyze function %s must return type \"boolean\"",
|
||||
NameListToString(procname))));
|
||||
|
||||
return procOid;
|
||||
}
|
||||
@@ -1073,7 +1065,7 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
|
||||
if (coldeflist == NIL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||
errmsg("composite type must have at least one attribute")));
|
||||
errmsg("composite type must have at least one attribute")));
|
||||
|
||||
/*
|
||||
* now set the parameters for keys/inheritance etc. All of these are
|
||||
@@ -1165,28 +1157,28 @@ AlterDomainDefault(List *names, Node *defaultRaw)
|
||||
|
||||
/*
|
||||
* Expression must be stored as a nodeToString result, but we also
|
||||
* require a valid textual representation (mainly to make life
|
||||
* easier for pg_dump).
|
||||
* require a valid textual representation (mainly to make life easier
|
||||
* for pg_dump).
|
||||
*/
|
||||
defaultValue = deparse_expression(defaultExpr,
|
||||
deparse_context_for(NameStr(typTup->typname),
|
||||
InvalidOid),
|
||||
deparse_context_for(NameStr(typTup->typname),
|
||||
InvalidOid),
|
||||
false, false);
|
||||
|
||||
/*
|
||||
* Form an updated tuple with the new default and write it back.
|
||||
*/
|
||||
new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(
|
||||
nodeToString(defaultExpr)));
|
||||
CStringGetDatum(
|
||||
nodeToString(defaultExpr)));
|
||||
|
||||
new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
|
||||
new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(defaultValue));
|
||||
CStringGetDatum(defaultValue));
|
||||
new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
|
||||
}
|
||||
else
|
||||
/* Default is NULL, drop it */
|
||||
/* Default is NULL, drop it */
|
||||
{
|
||||
new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
|
||||
new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
|
||||
@@ -1305,8 +1297,8 @@ AlterDomainNotNull(List *names, bool notNull)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_NOT_NULL_VIOLATION),
|
||||
errmsg("column \"%s\" of table \"%s\" contains null values",
|
||||
NameStr(tupdesc->attrs[attnum - 1]->attname),
|
||||
RelationGetRelationName(testrel))));
|
||||
NameStr(tupdesc->attrs[attnum - 1]->attname),
|
||||
RelationGetRelationName(testrel))));
|
||||
}
|
||||
}
|
||||
heap_endscan(scan);
|
||||
@@ -1317,8 +1309,8 @@ AlterDomainNotNull(List *names, bool notNull)
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay to update pg_type row. We can scribble on typTup because it's
|
||||
* a copy.
|
||||
* Okay to update pg_type row. We can scribble on typTup because it's a
|
||||
* copy.
|
||||
*/
|
||||
typTup->typnotnull = notNull;
|
||||
|
||||
@@ -1467,7 +1459,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
|
||||
if (IsA(newConstraint, FkConstraint))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("foreign key constraints not possible for domains")));
|
||||
errmsg("foreign key constraints not possible for domains")));
|
||||
|
||||
/* otherwise it should be a plain Constraint */
|
||||
if (!IsA(newConstraint, Constraint))
|
||||
@@ -1485,13 +1477,13 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
|
||||
case CONSTR_UNIQUE:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unique constraints not possible for domains")));
|
||||
errmsg("unique constraints not possible for domains")));
|
||||
break;
|
||||
|
||||
case CONSTR_PRIMARY:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("primary key constraints not possible for domains")));
|
||||
errmsg("primary key constraints not possible for domains")));
|
||||
break;
|
||||
|
||||
case CONSTR_ATTR_DEFERRABLE:
|
||||
@@ -1511,8 +1503,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
|
||||
|
||||
/*
|
||||
* Since all other constraint types throw errors, this must be a check
|
||||
* constraint. First, process the constraint expression and add an
|
||||
* entry to pg_constraint.
|
||||
* constraint. First, process the constraint expression and add an entry
|
||||
* to pg_constraint.
|
||||
*/
|
||||
|
||||
ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
|
||||
@@ -1572,7 +1564,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_CHECK_VIOLATION),
|
||||
errmsg("column \"%s\" of table \"%s\" contains values that violate the new constraint",
|
||||
NameStr(tupdesc->attrs[attnum - 1]->attname),
|
||||
NameStr(tupdesc->attrs[attnum - 1]->attname),
|
||||
RelationGetRelationName(testrel))));
|
||||
}
|
||||
|
||||
@@ -1626,8 +1618,8 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
|
||||
HeapTuple depTup;
|
||||
|
||||
/*
|
||||
* We scan pg_depend to find those things that depend on the domain.
|
||||
* (We assume we can ignore refobjsubid for a domain.)
|
||||
* We scan pg_depend to find those things that depend on the domain. (We
|
||||
* assume we can ignore refobjsubid for a domain.)
|
||||
*/
|
||||
depRel = heap_open(DependRelationId, AccessShareLock);
|
||||
|
||||
@@ -1693,10 +1685,10 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
|
||||
}
|
||||
|
||||
/*
|
||||
* Confirm column has not been dropped, and is of the expected
|
||||
* type. This defends against an ALTER DROP COLUMN occuring just
|
||||
* before we acquired lock ... but if the whole table were
|
||||
* dropped, we'd still have a problem.
|
||||
* Confirm column has not been dropped, and is of the expected type.
|
||||
* This defends against an ALTER DROP COLUMN occuring just before we
|
||||
* acquired lock ... but if the whole table were dropped, we'd still
|
||||
* have a problem.
|
||||
*/
|
||||
if (pg_depend->objsubid > RelationGetNumberOfAttributes(rtc->rel))
|
||||
continue;
|
||||
@@ -1705,9 +1697,9 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Okay, add column to result. We store the columns in
|
||||
* column-number order; this is just a hack to improve
|
||||
* predictability of regression test output ...
|
||||
* Okay, add column to result. We store the columns in column-number
|
||||
* order; this is just a hack to improve predictability of regression
|
||||
* test output ...
|
||||
*/
|
||||
Assert(rtc->natts < RelationGetNumberOfAttributes(rtc->rel));
|
||||
|
||||
@@ -1777,8 +1769,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
constr->name))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("constraint \"%s\" for domain \"%s\" already exists",
|
||||
constr->name, domainName)));
|
||||
errmsg("constraint \"%s\" for domain \"%s\" already exists",
|
||||
constr->name, domainName)));
|
||||
}
|
||||
else
|
||||
constr->name = ChooseConstraintName(domainName,
|
||||
@@ -1793,11 +1785,11 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
/*
|
||||
* Set up a CoerceToDomainValue to represent the occurrence of VALUE
|
||||
* in the expression. Note that it will appear to have the type of
|
||||
* the base type, not the domain. This seems correct since within the
|
||||
* check expression, we should not assume the input value can be
|
||||
* considered a member of the domain.
|
||||
* Set up a CoerceToDomainValue to represent the occurrence of VALUE in
|
||||
* the expression. Note that it will appear to have the type of the base
|
||||
* type, not the domain. This seems correct since within the check
|
||||
* expression, we should not assume the input value can be considered a
|
||||
* member of the domain.
|
||||
*/
|
||||
domVal = makeNode(CoerceToDomainValue);
|
||||
domVal->typeId = baseTypeOid;
|
||||
@@ -1818,7 +1810,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
if (list_length(pstate->p_rtable) != 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||
errmsg("cannot use table references in domain check constraint")));
|
||||
errmsg("cannot use table references in domain check constraint")));
|
||||
|
||||
/*
|
||||
* Domains don't allow var clauses (this should be redundant with the
|
||||
@@ -1827,7 +1819,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
if (contain_var_clause(expr))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||
errmsg("cannot use table references in domain check constraint")));
|
||||
errmsg("cannot use table references in domain check constraint")));
|
||||
|
||||
/*
|
||||
* No subplans or aggregates, either...
|
||||
@@ -1849,8 +1841,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
/*
|
||||
* Deparse it to produce text for consrc.
|
||||
*
|
||||
* Since VARNOs aren't allowed in domain constraints, relation context
|
||||
* isn't required as anything other than a shell.
|
||||
* Since VARNOs aren't allowed in domain constraints, relation context isn't
|
||||
* required as anything other than a shell.
|
||||
*/
|
||||
ccsrc = deparse_expression(expr,
|
||||
deparse_context_for(domainName,
|
||||
@@ -1881,8 +1873,8 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
ccsrc); /* Source form check constraint */
|
||||
|
||||
/*
|
||||
* Return the compiled constraint expression so the calling routine
|
||||
* can perform any additional required tests.
|
||||
* Return the compiled constraint expression so the calling routine can
|
||||
* perform any additional required tests.
|
||||
*/
|
||||
return ccbin;
|
||||
}
|
||||
@@ -1956,8 +1948,7 @@ GetDomainConstraints(Oid typeOid)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Not expecting conbin to be NULL, but we'll test for it
|
||||
* anyway
|
||||
* Not expecting conbin to be NULL, but we'll test for it anyway
|
||||
*/
|
||||
val = fastgetattr(conTup, Anum_pg_constraint_conbin,
|
||||
conRel->rd_att, &isNull);
|
||||
@@ -1978,8 +1969,8 @@ GetDomainConstraints(Oid typeOid)
|
||||
r->check_expr = ExecInitExpr(check_expr, NULL);
|
||||
|
||||
/*
|
||||
* use lcons() here because constraints of lower domains
|
||||
* should be applied earlier.
|
||||
* use lcons() here because constraints of lower domains should be
|
||||
* applied earlier.
|
||||
*/
|
||||
result = lcons(r, result);
|
||||
}
|
||||
@@ -1994,8 +1985,8 @@ GetDomainConstraints(Oid typeOid)
|
||||
heap_close(conRel, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Only need to add one NOT NULL check regardless of how many domains
|
||||
* in the stack request it.
|
||||
* Only need to add one NOT NULL check regardless of how many domains in
|
||||
* the stack request it.
|
||||
*/
|
||||
if (notNull)
|
||||
{
|
||||
@@ -2071,7 +2062,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
|
||||
if (!superuser())
|
||||
{
|
||||
/* Otherwise, must be owner of the existing object */
|
||||
if (!pg_type_ownercheck(HeapTupleGetOid(tup),GetUserId()))
|
||||
if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
|
||||
TypeNameToString(typename));
|
||||
|
||||
@@ -2088,8 +2079,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
|
||||
}
|
||||
|
||||
/*
|
||||
* Modify the owner --- okay to scribble on typTup because it's a
|
||||
* copy
|
||||
* Modify the owner --- okay to scribble on typTup because it's a copy
|
||||
*/
|
||||
typTup->typowner = newOwnerId;
|
||||
|
||||
@@ -2128,8 +2118,7 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
|
||||
typTup = (Form_pg_type) GETSTRUCT(tup);
|
||||
|
||||
/*
|
||||
* Modify the owner --- okay to scribble on typTup because it's a
|
||||
* copy
|
||||
* Modify the owner --- okay to scribble on typTup because it's a copy
|
||||
*/
|
||||
typTup->typowner = newOwnerId;
|
||||
|
||||
@@ -2150,9 +2139,9 @@ AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
|
||||
void
|
||||
AlterTypeNamespace(List *names, const char *newschema)
|
||||
{
|
||||
TypeName *typename;
|
||||
Oid typeOid;
|
||||
Oid nspOid;
|
||||
TypeName *typename;
|
||||
Oid typeOid;
|
||||
Oid nspOid;
|
||||
|
||||
/* get type OID */
|
||||
typename = makeNode(TypeName);
|
||||
@@ -2221,7 +2210,7 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
||||
if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot move objects into or out of temporary schemas")));
|
||||
errmsg("cannot move objects into or out of temporary schemas")));
|
||||
|
||||
/* same for TOAST schema */
|
||||
if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
|
||||
@@ -2264,18 +2253,18 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
||||
/*
|
||||
* Composite types have pg_class entries.
|
||||
*
|
||||
* We need to modify the pg_class tuple as well to
|
||||
* reflect the change of schema.
|
||||
* We need to modify the pg_class tuple as well to reflect the change of
|
||||
* schema.
|
||||
*/
|
||||
if (isCompositeType)
|
||||
{
|
||||
Relation classRel;
|
||||
Relation classRel;
|
||||
|
||||
classRel = heap_open(RelationRelationId, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* The dependency on the schema is listed under the pg_class entry,
|
||||
* so tell AlterRelationNamespaceInternal to fix it.
|
||||
* The dependency on the schema is listed under the pg_class entry, so
|
||||
* tell AlterRelationNamespaceInternal to fix it.
|
||||
*/
|
||||
AlterRelationNamespaceInternal(classRel, typform->typrelid,
|
||||
oldNspOid, nspOid,
|
||||
@@ -2284,8 +2273,8 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
||||
heap_close(classRel, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Check for constraints associated with the composite type
|
||||
* (we don't currently support this, but probably will someday).
|
||||
* Check for constraints associated with the composite type (we don't
|
||||
* currently support this, but probably will someday).
|
||||
*/
|
||||
AlterConstraintNamespaces(typform->typrelid, oldNspOid,
|
||||
nspOid, false);
|
||||
@@ -2297,12 +2286,12 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
|
||||
AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
|
||||
|
||||
/*
|
||||
* Update dependency on schema, if any --- a table rowtype has not
|
||||
* got one.
|
||||
* Update dependency on schema, if any --- a table rowtype has not got
|
||||
* one.
|
||||
*/
|
||||
if (typform->typtype != 'c')
|
||||
if (changeDependencyFor(TypeRelationId, typeOid,
|
||||
NamespaceRelationId, oldNspOid, nspOid) != 1)
|
||||
NamespaceRelationId, oldNspOid, nspOid) != 1)
|
||||
elog(ERROR, "failed to change schema dependency for type %s",
|
||||
format_type_be(typeOid));
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.160 2005/07/31 17:19:17 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/user.c,v 1.161 2005/10/15 02:49:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -34,11 +34,11 @@ extern bool Password_encryption;
|
||||
|
||||
static List *roleNamesToIds(List *memberNames);
|
||||
static void AddRoleMems(const char *rolename, Oid roleid,
|
||||
List *memberNames, List *memberIds,
|
||||
Oid grantorId, bool admin_opt);
|
||||
List *memberNames, List *memberIds,
|
||||
Oid grantorId, bool admin_opt);
|
||||
static void DelRoleMems(const char *rolename, Oid roleid,
|
||||
List *memberNames, List *memberIds,
|
||||
bool admin_opt);
|
||||
List *memberNames, List *memberIds,
|
||||
bool admin_opt);
|
||||
|
||||
|
||||
/* Check if current user has createrole privileges */
|
||||
@@ -78,16 +78,16 @@ CreateRole(CreateRoleStmt *stmt)
|
||||
Oid roleid;
|
||||
ListCell *item;
|
||||
ListCell *option;
|
||||
char *password = NULL; /* user password */
|
||||
char *password = NULL; /* user password */
|
||||
bool encrypt_password = Password_encryption; /* encrypt password? */
|
||||
char encrypted_password[MD5_PASSWD_LEN + 1];
|
||||
bool issuper = false; /* Make the user a superuser? */
|
||||
bool inherit = true; /* Auto inherit privileges? */
|
||||
bool issuper = false; /* Make the user a superuser? */
|
||||
bool inherit = true; /* Auto inherit privileges? */
|
||||
bool createrole = false; /* Can this user create roles? */
|
||||
bool createdb = false; /* Can the user create databases? */
|
||||
bool canlogin = false; /* Can this user login? */
|
||||
int connlimit = -1; /* maximum connections allowed */
|
||||
List *addroleto = NIL; /* roles to make this a member of */
|
||||
int connlimit = -1; /* maximum connections allowed */
|
||||
List *addroleto = NIL; /* roles to make this a member of */
|
||||
List *rolemembers = NIL; /* roles to be members of this role */
|
||||
List *adminmembers = NIL; /* roles to be admins of this role */
|
||||
char *validUntil = NULL; /* time the login is valid until */
|
||||
@@ -272,9 +272,9 @@ CreateRole(CreateRoleStmt *stmt)
|
||||
stmt->role)));
|
||||
|
||||
/*
|
||||
* Check the pg_authid relation to be certain the role doesn't
|
||||
* already exist. Note we secure exclusive lock because
|
||||
* we need to protect our eventual update of the flat auth file.
|
||||
* Check the pg_authid relation to be certain the role doesn't already
|
||||
* exist. Note we secure exclusive lock because we need to protect our
|
||||
* eventual update of the flat auth file.
|
||||
*/
|
||||
pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
|
||||
pg_authid_dsc = RelationGetDescr(pg_authid_rel);
|
||||
@@ -344,8 +344,8 @@ CreateRole(CreateRoleStmt *stmt)
|
||||
CatalogUpdateIndexes(pg_authid_rel, tuple);
|
||||
|
||||
/*
|
||||
* Advance command counter so we can see new record; else tests
|
||||
* in AddRoleMems may fail.
|
||||
* Advance command counter so we can see new record; else tests in
|
||||
* AddRoleMems may fail.
|
||||
*/
|
||||
if (addroleto || adminmembers || rolemembers)
|
||||
CommandCounterIncrement();
|
||||
@@ -355,8 +355,8 @@ CreateRole(CreateRoleStmt *stmt)
|
||||
*/
|
||||
foreach(item, addroleto)
|
||||
{
|
||||
char *oldrolename = strVal(lfirst(item));
|
||||
Oid oldroleid = get_roleid_checked(oldrolename);
|
||||
char *oldrolename = strVal(lfirst(item));
|
||||
Oid oldroleid = get_roleid_checked(oldrolename);
|
||||
|
||||
AddRoleMems(oldrolename, oldroleid,
|
||||
list_make1(makeString(stmt->role)),
|
||||
@@ -365,8 +365,8 @@ CreateRole(CreateRoleStmt *stmt)
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the specified members to this new role. adminmembers get the
|
||||
* admin option, rolemembers don't.
|
||||
* Add the specified members to this new role. adminmembers get the admin
|
||||
* option, rolemembers don't.
|
||||
*/
|
||||
AddRoleMems(stmt->role, roleid,
|
||||
adminmembers, roleNamesToIds(adminmembers),
|
||||
@@ -406,15 +406,15 @@ AlterRole(AlterRoleStmt *stmt)
|
||||
HeapTuple tuple,
|
||||
new_tuple;
|
||||
ListCell *option;
|
||||
char *password = NULL; /* user password */
|
||||
char *password = NULL; /* user password */
|
||||
bool encrypt_password = Password_encryption; /* encrypt password? */
|
||||
char encrypted_password[MD5_PASSWD_LEN + 1];
|
||||
int issuper = -1; /* Make the user a superuser? */
|
||||
int inherit = -1; /* Auto inherit privileges? */
|
||||
int createrole = -1; /* Can this user create roles? */
|
||||
int createdb = -1; /* Can the user create databases? */
|
||||
int canlogin = -1; /* Can this user login? */
|
||||
int connlimit = -1; /* maximum connections allowed */
|
||||
int issuper = -1; /* Make the user a superuser? */
|
||||
int inherit = -1; /* Auto inherit privileges? */
|
||||
int createrole = -1; /* Can this user create roles? */
|
||||
int createdb = -1; /* Can the user create databases? */
|
||||
int canlogin = -1; /* Can this user login? */
|
||||
int connlimit = -1; /* maximum connections allowed */
|
||||
List *rolemembers = NIL; /* roles to be added/removed */
|
||||
char *validUntil = NULL; /* time the login is valid until */
|
||||
DefElem *dpassword = NULL;
|
||||
@@ -591,9 +591,9 @@ AlterRole(AlterRoleStmt *stmt)
|
||||
* issuper/createrole/catupdate/etc
|
||||
*
|
||||
* XXX It's rather unclear how to handle catupdate. It's probably best to
|
||||
* keep it equal to the superuser status, otherwise you could end up
|
||||
* with a situation where no existing superuser can alter the
|
||||
* catalogs, including pg_authid!
|
||||
* keep it equal to the superuser status, otherwise you could end up with
|
||||
* a situation where no existing superuser can alter the catalogs,
|
||||
* including pg_authid!
|
||||
*/
|
||||
if (issuper >= 0)
|
||||
{
|
||||
@@ -673,8 +673,8 @@ AlterRole(AlterRoleStmt *stmt)
|
||||
heap_freetuple(new_tuple);
|
||||
|
||||
/*
|
||||
* Advance command counter so we can see new record; else tests
|
||||
* in AddRoleMems may fail.
|
||||
* Advance command counter so we can see new record; else tests in
|
||||
* AddRoleMems may fail.
|
||||
*/
|
||||
if (rolemembers)
|
||||
CommandCounterIncrement();
|
||||
@@ -801,7 +801,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
|
||||
void
|
||||
DropRole(DropRoleStmt *stmt)
|
||||
{
|
||||
Relation pg_authid_rel, pg_auth_members_rel;
|
||||
Relation pg_authid_rel,
|
||||
pg_auth_members_rel;
|
||||
ListCell *item;
|
||||
|
||||
if (!have_createrole_privilege())
|
||||
@@ -811,9 +812,9 @@ DropRole(DropRoleStmt *stmt)
|
||||
|
||||
/*
|
||||
* Scan the pg_authid relation to find the Oid of the role(s) to be
|
||||
* deleted. Note we secure exclusive lock on pg_authid, because we
|
||||
* need to protect our update of the flat auth file. A regular
|
||||
* writer's lock on pg_auth_members is sufficient though.
|
||||
* deleted. Note we secure exclusive lock on pg_authid, because we need
|
||||
* to protect our update of the flat auth file. A regular writer's lock
|
||||
* on pg_auth_members is sufficient though.
|
||||
*/
|
||||
pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
|
||||
pg_auth_members_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
|
||||
@@ -823,7 +824,7 @@ DropRole(DropRoleStmt *stmt)
|
||||
const char *role = strVal(lfirst(item));
|
||||
HeapTuple tuple,
|
||||
tmp_tuple;
|
||||
ScanKeyData scankey;
|
||||
ScanKeyData scankey;
|
||||
char *detail;
|
||||
SysScanDesc sscan;
|
||||
Oid roleid;
|
||||
@@ -865,7 +866,7 @@ DropRole(DropRoleStmt *stmt)
|
||||
/*
|
||||
* Lock the role, so nobody can add dependencies to her while we drop
|
||||
* her. We keep the lock until the end of transaction.
|
||||
*/
|
||||
*/
|
||||
LockSharedObject(AuthIdRelationId, roleid, 0, AccessExclusiveLock);
|
||||
|
||||
/* Check for pg_shdepend entries depending on this role */
|
||||
@@ -873,7 +874,7 @@ DropRole(DropRoleStmt *stmt)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
|
||||
errmsg("role \"%s\" cannot be dropped because some objects depend on it",
|
||||
role),
|
||||
role),
|
||||
errdetail("%s", detail)));
|
||||
|
||||
/*
|
||||
@@ -884,10 +885,10 @@ DropRole(DropRoleStmt *stmt)
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* Remove role from the pg_auth_members table. We have to remove
|
||||
* all tuples that show it as either a role or a member.
|
||||
* Remove role from the pg_auth_members table. We have to remove all
|
||||
* tuples that show it as either a role or a member.
|
||||
*
|
||||
* XXX what about grantor entries? Maybe we should do one heap scan.
|
||||
* XXX what about grantor entries? Maybe we should do one heap scan.
|
||||
*/
|
||||
ScanKeyInit(&scankey,
|
||||
Anum_pg_auth_members_roleid,
|
||||
@@ -920,13 +921,13 @@ DropRole(DropRoleStmt *stmt)
|
||||
systable_endscan(sscan);
|
||||
|
||||
/*
|
||||
* Advance command counter so that later iterations of this loop
|
||||
* will see the changes already made. This is essential if, for
|
||||
* example, we are trying to drop both a role and one of its
|
||||
* direct members --- we'll get an error if we try to delete the
|
||||
* linking pg_auth_members tuple twice. (We do not need a CCI
|
||||
* between the two delete loops above, because it's not allowed
|
||||
* for a role to directly contain itself.)
|
||||
* Advance command counter so that later iterations of this loop will
|
||||
* see the changes already made. This is essential if, for example,
|
||||
* we are trying to drop both a role and one of its direct members ---
|
||||
* we'll get an error if we try to delete the linking pg_auth_members
|
||||
* tuple twice. (We do not need a CCI between the two delete loops
|
||||
* above, because it's not allowed for a role to directly contain
|
||||
* itself.)
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
@@ -975,11 +976,11 @@ RenameRole(const char *oldname, const char *newname)
|
||||
errmsg("role \"%s\" does not exist", oldname)));
|
||||
|
||||
/*
|
||||
* XXX Client applications probably store the session user somewhere,
|
||||
* so renaming it could cause confusion. On the other hand, there may
|
||||
* not be an actual problem besides a little confusion, so think about
|
||||
* this and decide. Same for SET ROLE ... we don't restrict renaming
|
||||
* the current effective userid, though.
|
||||
* XXX Client applications probably store the session user somewhere, so
|
||||
* renaming it could cause confusion. On the other hand, there may not be
|
||||
* an actual problem besides a little confusion, so think about this and
|
||||
* decide. Same for SET ROLE ... we don't restrict renaming the current
|
||||
* effective userid, though.
|
||||
*/
|
||||
|
||||
roleid = HeapTupleGetOid(oldtuple);
|
||||
@@ -1032,7 +1033,7 @@ RenameRole(const char *oldname, const char *newname)
|
||||
|
||||
repl_repl[Anum_pg_authid_rolname - 1] = 'r';
|
||||
repl_val[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein,
|
||||
CStringGetDatum(newname));
|
||||
CStringGetDatum(newname));
|
||||
repl_null[Anum_pg_authid_rolname - 1] = ' ';
|
||||
|
||||
datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
|
||||
@@ -1082,23 +1083,22 @@ GrantRole(GrantRoleStmt *stmt)
|
||||
grantee_ids = roleNamesToIds(stmt->grantee_roles);
|
||||
|
||||
/*
|
||||
* Even though this operation doesn't change pg_authid, we must
|
||||
* secure exclusive lock on it to protect our update of the flat
|
||||
* auth file.
|
||||
* Even though this operation doesn't change pg_authid, we must secure
|
||||
* exclusive lock on it to protect our update of the flat auth file.
|
||||
*/
|
||||
pg_authid_rel = heap_open(AuthIdRelationId, ExclusiveLock);
|
||||
|
||||
/*
|
||||
* Step through all of the granted roles and add/remove
|
||||
* entries for the grantees, or, if admin_opt is set, then
|
||||
* just add/remove the admin option.
|
||||
* Step through all of the granted roles and add/remove entries for the
|
||||
* grantees, or, if admin_opt is set, then just add/remove the admin
|
||||
* option.
|
||||
*
|
||||
* Note: Permissions checking is done by AddRoleMems/DelRoleMems
|
||||
*/
|
||||
foreach(item, stmt->granted_roles)
|
||||
{
|
||||
char *rolename = strVal(lfirst(item));
|
||||
Oid roleid = get_roleid_checked(rolename);
|
||||
char *rolename = strVal(lfirst(item));
|
||||
Oid roleid = get_roleid_checked(rolename);
|
||||
|
||||
if (stmt->is_grant)
|
||||
AddRoleMems(rolename, roleid,
|
||||
@@ -1132,8 +1132,8 @@ roleNamesToIds(List *memberNames)
|
||||
|
||||
foreach(l, memberNames)
|
||||
{
|
||||
char *rolename = strVal(lfirst(l));
|
||||
Oid roleid = get_roleid_checked(rolename);
|
||||
char *rolename = strVal(lfirst(l));
|
||||
Oid roleid = get_roleid_checked(rolename);
|
||||
|
||||
result = lappend_oid(result, roleid);
|
||||
}
|
||||
@@ -1160,8 +1160,8 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||
{
|
||||
Relation pg_authmem_rel;
|
||||
TupleDesc pg_authmem_dsc;
|
||||
ListCell *nameitem;
|
||||
ListCell *iditem;
|
||||
ListCell *nameitem;
|
||||
ListCell *iditem;
|
||||
|
||||
Assert(list_length(memberNames) == list_length(memberIds));
|
||||
|
||||
@@ -1170,9 +1170,8 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check permissions: must have createrole or admin option on the
|
||||
* role to be changed. To mess with a superuser role, you gotta
|
||||
* be superuser.
|
||||
* Check permissions: must have createrole or admin option on the role to
|
||||
* be changed. To mess with a superuser role, you gotta be superuser.
|
||||
*/
|
||||
if (superuser_arg(roleid))
|
||||
{
|
||||
@@ -1207,32 +1206,32 @@ AddRoleMems(const char *rolename, Oid roleid,
|
||||
Oid memberid = lfirst_oid(iditem);
|
||||
HeapTuple authmem_tuple;
|
||||
HeapTuple tuple;
|
||||
Datum new_record[Natts_pg_auth_members];
|
||||
char new_record_nulls[Natts_pg_auth_members];
|
||||
char new_record_repl[Natts_pg_auth_members];
|
||||
Datum new_record[Natts_pg_auth_members];
|
||||
char new_record_nulls[Natts_pg_auth_members];
|
||||
char new_record_repl[Natts_pg_auth_members];
|
||||
|
||||
/*
|
||||
* Refuse creation of membership loops, including the trivial case
|
||||
* where a role is made a member of itself. We do this by checking
|
||||
* to see if the target role is already a member of the proposed
|
||||
* member role.
|
||||
* where a role is made a member of itself. We do this by checking to
|
||||
* see if the target role is already a member of the proposed member
|
||||
* role.
|
||||
*/
|
||||
if (is_member_of_role(roleid, memberid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_GRANT_OPERATION),
|
||||
(errmsg("role \"%s\" is a member of role \"%s\"",
|
||||
rolename, membername))));
|
||||
(errmsg("role \"%s\" is a member of role \"%s\"",
|
||||
rolename, membername))));
|
||||
|
||||
/*
|
||||
* Check if entry for this role/member already exists;
|
||||
* if so, give warning unless we are adding admin option.
|
||||
* Check if entry for this role/member already exists; if so, give
|
||||
* warning unless we are adding admin option.
|
||||
*/
|
||||
authmem_tuple = SearchSysCache(AUTHMEMROLEMEM,
|
||||
ObjectIdGetDatum(roleid),
|
||||
ObjectIdGetDatum(memberid),
|
||||
0, 0);
|
||||
if (HeapTupleIsValid(authmem_tuple) &&
|
||||
(!admin_opt ||
|
||||
(!admin_opt ||
|
||||
((Form_pg_auth_members) GETSTRUCT(authmem_tuple))->admin_option))
|
||||
{
|
||||
ereport(NOTICE,
|
||||
@@ -1301,8 +1300,8 @@ DelRoleMems(const char *rolename, Oid roleid,
|
||||
{
|
||||
Relation pg_authmem_rel;
|
||||
TupleDesc pg_authmem_dsc;
|
||||
ListCell *nameitem;
|
||||
ListCell *iditem;
|
||||
ListCell *nameitem;
|
||||
ListCell *iditem;
|
||||
|
||||
Assert(list_length(memberNames) == list_length(memberIds));
|
||||
|
||||
@@ -1311,9 +1310,8 @@ DelRoleMems(const char *rolename, Oid roleid,
|
||||
return;
|
||||
|
||||
/*
|
||||
* Check permissions: must have createrole or admin option on the
|
||||
* role to be changed. To mess with a superuser role, you gotta
|
||||
* be superuser.
|
||||
* Check permissions: must have createrole or admin option on the role to
|
||||
* be changed. To mess with a superuser role, you gotta be superuser.
|
||||
*/
|
||||
if (superuser_arg(roleid))
|
||||
{
|
||||
@@ -1366,9 +1364,9 @@ DelRoleMems(const char *rolename, Oid roleid,
|
||||
{
|
||||
/* Just turn off the admin option */
|
||||
HeapTuple tuple;
|
||||
Datum new_record[Natts_pg_auth_members];
|
||||
char new_record_nulls[Natts_pg_auth_members];
|
||||
char new_record_repl[Natts_pg_auth_members];
|
||||
Datum new_record[Natts_pg_auth_members];
|
||||
char new_record_nulls[Natts_pg_auth_members];
|
||||
char new_record_repl[Natts_pg_auth_members];
|
||||
|
||||
/* Build a tuple to update with */
|
||||
MemSet(new_record, 0, sizeof(new_record));
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.60 2005/10/03 22:52:22 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.61 2005/10/15 02:49:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -67,7 +67,7 @@ typedef struct LVRelStats
|
||||
/* Overall statistics about rel */
|
||||
BlockNumber rel_pages;
|
||||
double rel_tuples;
|
||||
BlockNumber pages_removed;
|
||||
BlockNumber pages_removed;
|
||||
double tuples_deleted;
|
||||
BlockNumber nonempty_pages; /* actually, last nonempty page + 1 */
|
||||
Size threshold; /* minimum interesting free space */
|
||||
@@ -97,9 +97,9 @@ static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats);
|
||||
static void lazy_scan_index(Relation indrel, LVRelStats *vacrelstats);
|
||||
static void lazy_vacuum_index(Relation indrel,
|
||||
double *index_tups_vacuumed,
|
||||
BlockNumber *index_pages_removed,
|
||||
LVRelStats *vacrelstats);
|
||||
double *index_tups_vacuumed,
|
||||
BlockNumber *index_pages_removed,
|
||||
LVRelStats *vacrelstats);
|
||||
static int lazy_vacuum_page(Relation onerel, BlockNumber blkno, Buffer buffer,
|
||||
int tupindex, LVRelStats *vacrelstats);
|
||||
static void lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats);
|
||||
@@ -167,7 +167,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
||||
*/
|
||||
possibly_freeable = vacrelstats->rel_pages - vacrelstats->nonempty_pages;
|
||||
if (possibly_freeable >= REL_TRUNCATE_MINIMUM ||
|
||||
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
|
||||
possibly_freeable >= vacrelstats->rel_pages / REL_TRUNCATE_FRACTION)
|
||||
lazy_truncate_heap(onerel, vacrelstats);
|
||||
|
||||
/* Update shared free space map with final free space info */
|
||||
@@ -181,7 +181,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt)
|
||||
|
||||
/* report results to the stats collector, too */
|
||||
pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared,
|
||||
vacstmt->analyze, vacrelstats->rel_tuples);
|
||||
vacstmt->analyze, vacrelstats->rel_tuples);
|
||||
}
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
* track of the total number of rows and pages removed from each index.
|
||||
* index_tups_vacuumed[i] is the number removed so far from the i'th
|
||||
* index. (For partial indexes this could well be different from
|
||||
* tups_vacuumed.) Likewise for index_pages_removed[i].
|
||||
* tups_vacuumed.) Likewise for index_pages_removed[i].
|
||||
*/
|
||||
index_tups_vacuumed = (double *) palloc0(nindexes * sizeof(double));
|
||||
index_pages_removed = (BlockNumber *) palloc0(nindexes * sizeof(BlockNumber));
|
||||
@@ -253,9 +253,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
vacuum_delay_point();
|
||||
|
||||
/*
|
||||
* If we are close to overrunning the available space for
|
||||
* dead-tuple TIDs, pause and do a cycle of vacuuming before we
|
||||
* tackle this page.
|
||||
* If we are close to overrunning the available space for dead-tuple
|
||||
* TIDs, pause and do a cycle of vacuuming before we tackle this page.
|
||||
*/
|
||||
if ((vacrelstats->max_dead_tuples - vacrelstats->num_dead_tuples) < MaxHeapTuplesPerPage &&
|
||||
vacrelstats->num_dead_tuples > 0)
|
||||
@@ -283,25 +282,25 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
if (PageIsNew(page))
|
||||
{
|
||||
/*
|
||||
* An all-zeroes page could be left over if a backend extends
|
||||
* the relation but crashes before initializing the page.
|
||||
* Reclaim such pages for use.
|
||||
* An all-zeroes page could be left over if a backend extends the
|
||||
* relation but crashes before initializing the page. Reclaim such
|
||||
* pages for use.
|
||||
*
|
||||
* We have to be careful here because we could be looking at
|
||||
* a page that someone has just added to the relation and not
|
||||
* yet been able to initialize (see RelationGetBufferForTuple).
|
||||
* To interlock against that, release the buffer read lock
|
||||
* (which we must do anyway) and grab the relation extension
|
||||
* lock before re-locking in exclusive mode. If the page is
|
||||
* still uninitialized by then, it must be left over from a
|
||||
* crashed backend, and we can initialize it.
|
||||
* We have to be careful here because we could be looking at a page
|
||||
* that someone has just added to the relation and not yet been
|
||||
* able to initialize (see RelationGetBufferForTuple). To
|
||||
* interlock against that, release the buffer read lock (which we
|
||||
* must do anyway) and grab the relation extension lock before
|
||||
* re-locking in exclusive mode. If the page is still
|
||||
* uninitialized by then, it must be left over from a crashed
|
||||
* backend, and we can initialize it.
|
||||
*
|
||||
* We don't really need the relation lock when this is a new
|
||||
* or temp relation, but it's probably not worth the code space
|
||||
* to check that, since this surely isn't a critical path.
|
||||
* We don't really need the relation lock when this is a new or temp
|
||||
* relation, but it's probably not worth the code space to check
|
||||
* that, since this surely isn't a critical path.
|
||||
*
|
||||
* Note: the comparable code in vacuum.c need not worry
|
||||
* because it's got exclusive lock on the whole relation.
|
||||
* Note: the comparable code in vacuum.c need not worry because it's
|
||||
* got exclusive lock on the whole relation.
|
||||
*/
|
||||
LockBuffer(buf, BUFFER_LOCK_UNLOCK);
|
||||
LockRelationForExtension(onerel, ExclusiveLock);
|
||||
@@ -310,8 +309,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
if (PageIsNew(page))
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errmsg("relation \"%s\" page %u is uninitialized --- fixing",
|
||||
relname, blkno)));
|
||||
(errmsg("relation \"%s\" page %u is uninitialized --- fixing",
|
||||
relname, blkno)));
|
||||
PageInit(page, BufferGetPageSize(buf), 0);
|
||||
empty_pages++;
|
||||
lazy_record_free_space(vacrelstats, blkno,
|
||||
@@ -365,15 +364,15 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
case HEAPTUPLE_LIVE:
|
||||
|
||||
/*
|
||||
* Tuple is good. Consider whether to replace its
|
||||
* xmin value with FrozenTransactionId.
|
||||
* Tuple is good. Consider whether to replace its xmin
|
||||
* value with FrozenTransactionId.
|
||||
*
|
||||
* NB: Since we hold only a shared buffer lock here, we
|
||||
* are assuming that TransactionId read/write is
|
||||
* atomic. This is not the only place that makes such
|
||||
* an assumption. It'd be possible to avoid the
|
||||
* assumption by momentarily acquiring exclusive lock,
|
||||
* but for the moment I see no need to.
|
||||
* NB: Since we hold only a shared buffer lock here, we are
|
||||
* assuming that TransactionId read/write is atomic. This
|
||||
* is not the only place that makes such an assumption.
|
||||
* It'd be possible to avoid the assumption by momentarily
|
||||
* acquiring exclusive lock, but for the moment I see no
|
||||
* need to.
|
||||
*/
|
||||
if (TransactionIdIsNormal(HeapTupleHeaderGetXmin(tuple.t_data)) &&
|
||||
TransactionIdPrecedes(HeapTupleHeaderGetXmin(tuple.t_data),
|
||||
@@ -396,8 +395,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
case HEAPTUPLE_RECENTLY_DEAD:
|
||||
|
||||
/*
|
||||
* If tuple is recently deleted then we must not
|
||||
* remove it from relation.
|
||||
* If tuple is recently deleted then we must not remove it
|
||||
* from relation.
|
||||
*/
|
||||
nkeep += 1;
|
||||
break;
|
||||
@@ -426,9 +425,9 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
|
||||
/*
|
||||
* If we remembered any tuples for deletion, then the page will be
|
||||
* visited again by lazy_vacuum_heap, which will compute and
|
||||
* record its post-compaction free space. If not, then we're done
|
||||
* with this page, so remember its free space as-is.
|
||||
* visited again by lazy_vacuum_heap, which will compute and record
|
||||
* its post-compaction free space. If not, then we're done with this
|
||||
* page, so remember its free space as-is.
|
||||
*/
|
||||
if (vacrelstats->num_dead_tuples == prev_dead_count)
|
||||
{
|
||||
@@ -608,8 +607,8 @@ lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
|
||||
pg_rusage_init(&ru0);
|
||||
|
||||
/*
|
||||
* Acquire appropriate type of lock on index: must be exclusive if
|
||||
* index AM isn't concurrent-safe.
|
||||
* Acquire appropriate type of lock on index: must be exclusive if index
|
||||
* AM isn't concurrent-safe.
|
||||
*/
|
||||
if (indrel->rd_am->amconcurrent)
|
||||
LockRelation(indrel, RowExclusiveLock);
|
||||
@@ -618,9 +617,9 @@ lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
|
||||
|
||||
/*
|
||||
* Even though we're not planning to delete anything, we use the
|
||||
* ambulkdelete call, because (a) the scan happens within the index AM
|
||||
* for more speed, and (b) it may want to pass private statistics to
|
||||
* the amvacuumcleanup call.
|
||||
* ambulkdelete call, because (a) the scan happens within the index AM for
|
||||
* more speed, and (b) it may want to pass private statistics to the
|
||||
* amvacuumcleanup call.
|
||||
*/
|
||||
stats = index_bulk_delete(indrel, dummy_tid_reaped, NULL);
|
||||
|
||||
@@ -648,14 +647,14 @@ lazy_scan_index(Relation indrel, LVRelStats *vacrelstats)
|
||||
false);
|
||||
|
||||
ereport(elevel,
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
RelationGetRelationName(indrel),
|
||||
stats->num_index_tuples,
|
||||
stats->num_pages),
|
||||
errdetail("%u index pages have been deleted, %u are currently reusable.\n"
|
||||
"%s.",
|
||||
stats->pages_deleted, stats->pages_free,
|
||||
pg_rusage_show(&ru0))));
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
RelationGetRelationName(indrel),
|
||||
stats->num_index_tuples,
|
||||
stats->num_pages),
|
||||
errdetail("%u index pages have been deleted, %u are currently reusable.\n"
|
||||
"%s.",
|
||||
stats->pages_deleted, stats->pages_free,
|
||||
pg_rusage_show(&ru0))));
|
||||
|
||||
pfree(stats);
|
||||
}
|
||||
@@ -685,8 +684,8 @@ lazy_vacuum_index(Relation indrel,
|
||||
pg_rusage_init(&ru0);
|
||||
|
||||
/*
|
||||
* Acquire appropriate type of lock on index: must be exclusive if
|
||||
* index AM isn't concurrent-safe.
|
||||
* Acquire appropriate type of lock on index: must be exclusive if index
|
||||
* AM isn't concurrent-safe.
|
||||
*/
|
||||
if (indrel->rd_am->amconcurrent)
|
||||
LockRelation(indrel, RowExclusiveLock);
|
||||
@@ -724,16 +723,16 @@ lazy_vacuum_index(Relation indrel,
|
||||
false);
|
||||
|
||||
ereport(elevel,
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
RelationGetRelationName(indrel),
|
||||
stats->num_index_tuples,
|
||||
stats->num_pages),
|
||||
errdetail("%.0f index row versions were removed.\n"
|
||||
"%u index pages have been deleted, %u are currently reusable.\n"
|
||||
"%s.",
|
||||
stats->tuples_removed,
|
||||
stats->pages_deleted, stats->pages_free,
|
||||
pg_rusage_show(&ru0))));
|
||||
(errmsg("index \"%s\" now contains %.0f row versions in %u pages",
|
||||
RelationGetRelationName(indrel),
|
||||
stats->num_index_tuples,
|
||||
stats->num_pages),
|
||||
errdetail("%.0f index row versions were removed.\n"
|
||||
"%u index pages have been deleted, %u are currently reusable.\n"
|
||||
"%s.",
|
||||
stats->tuples_removed,
|
||||
stats->pages_deleted, stats->pages_free,
|
||||
pg_rusage_show(&ru0))));
|
||||
|
||||
pfree(stats);
|
||||
}
|
||||
@@ -755,19 +754,18 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
|
||||
pg_rusage_init(&ru0);
|
||||
|
||||
/*
|
||||
* We need full exclusive lock on the relation in order to do
|
||||
* truncation. If we can't get it, give up rather than waiting --- we
|
||||
* don't want to block other backends, and we don't want to deadlock
|
||||
* (which is quite possible considering we already hold a lower-grade
|
||||
* lock).
|
||||
* We need full exclusive lock on the relation in order to do truncation.
|
||||
* If we can't get it, give up rather than waiting --- we don't want to
|
||||
* block other backends, and we don't want to deadlock (which is quite
|
||||
* possible considering we already hold a lower-grade lock).
|
||||
*/
|
||||
if (!ConditionalLockRelation(onerel, AccessExclusiveLock))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Now that we have exclusive lock, look to see if the rel has grown
|
||||
* whilst we were vacuuming with non-exclusive lock. If so, give up;
|
||||
* the newly added pages presumably contain non-deletable tuples.
|
||||
* whilst we were vacuuming with non-exclusive lock. If so, give up; the
|
||||
* newly added pages presumably contain non-deletable tuples.
|
||||
*/
|
||||
new_rel_pages = RelationGetNumberOfBlocks(onerel);
|
||||
if (new_rel_pages != old_rel_pages)
|
||||
@@ -780,9 +778,9 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats)
|
||||
|
||||
/*
|
||||
* Scan backwards from the end to verify that the end pages actually
|
||||
* contain nothing we need to keep. This is *necessary*, not
|
||||
* optional, because other backends could have added tuples to these
|
||||
* pages whilst we were vacuuming.
|
||||
* contain nothing we need to keep. This is *necessary*, not optional,
|
||||
* because other backends could have added tuples to these pages whilst we
|
||||
* were vacuuming.
|
||||
*/
|
||||
new_rel_pages = count_nondeletable_pages(onerel, vacrelstats);
|
||||
|
||||
@@ -905,8 +903,8 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
|
||||
case HEAPTUPLE_RECENTLY_DEAD:
|
||||
|
||||
/*
|
||||
* If tuple is recently deleted then we must not
|
||||
* remove it from relation.
|
||||
* If tuple is recently deleted then we must not remove it
|
||||
* from relation.
|
||||
*/
|
||||
break;
|
||||
case HEAPTUPLE_INSERT_IN_PROGRESS:
|
||||
@@ -938,8 +936,8 @@ count_nondeletable_pages(Relation onerel, LVRelStats *vacrelstats)
|
||||
|
||||
/*
|
||||
* If we fall out of the loop, all the previously-thought-to-be-empty
|
||||
* pages really are; we need not bother to look at the last
|
||||
* known-nonempty page.
|
||||
* pages really are; we need not bother to look at the last known-nonempty
|
||||
* page.
|
||||
*/
|
||||
return vacrelstats->nonempty_pages;
|
||||
}
|
||||
@@ -1010,18 +1008,16 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
||||
/*
|
||||
* A page with less than stats->threshold free space will be forgotten
|
||||
* immediately, and never passed to the free space map. Removing the
|
||||
* uselessly small entries early saves cycles, and in particular
|
||||
* reduces the amount of time we spend holding the FSM lock when we
|
||||
* finally call RecordRelationFreeSpace. Since the FSM will probably
|
||||
* drop pages with little free space anyway, there's no point in
|
||||
* making this really small.
|
||||
* uselessly small entries early saves cycles, and in particular reduces
|
||||
* the amount of time we spend holding the FSM lock when we finally call
|
||||
* RecordRelationFreeSpace. Since the FSM will probably drop pages with
|
||||
* little free space anyway, there's no point in making this really small.
|
||||
*
|
||||
* XXX Is it worth trying to measure average tuple size, and using that
|
||||
* to adjust the threshold? Would be worthwhile if FSM has no stats
|
||||
* yet for this relation. But changing the threshold as we scan the
|
||||
* rel might lead to bizarre behavior, too. Also, it's probably
|
||||
* better if vacuum.c has the same thresholding behavior as we do
|
||||
* here.
|
||||
* XXX Is it worth trying to measure average tuple size, and using that to
|
||||
* adjust the threshold? Would be worthwhile if FSM has no stats yet for
|
||||
* this relation. But changing the threshold as we scan the rel might
|
||||
* lead to bizarre behavior, too. Also, it's probably better if vacuum.c
|
||||
* has the same thresholding behavior as we do here.
|
||||
*/
|
||||
if (avail < vacrelstats->threshold)
|
||||
return;
|
||||
@@ -1055,8 +1051,8 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
||||
{
|
||||
/*
|
||||
* Scan backwards through the array, "sift-up" each value into its
|
||||
* correct position. We can start the scan at n/2-1 since each
|
||||
* entry above that position has no children to worry about.
|
||||
* correct position. We can start the scan at n/2-1 since each entry
|
||||
* above that position has no children to worry about.
|
||||
*/
|
||||
int l = n / 2;
|
||||
|
||||
@@ -1092,9 +1088,9 @@ lazy_record_free_space(LVRelStats *vacrelstats,
|
||||
{
|
||||
/*
|
||||
* Notionally, we replace the zero'th entry with the new data, and
|
||||
* then sift-up to maintain the heap property. Physically, the
|
||||
* new data doesn't get stored into the arrays until we find the
|
||||
* right location for it.
|
||||
* then sift-up to maintain the heap property. Physically, the new
|
||||
* data doesn't get stored into the arrays until we find the right
|
||||
* location for it.
|
||||
*/
|
||||
int i = 0; /* i is where the "hole" is */
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.113 2005/08/08 23:39:01 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.114 2005/10/15 02:49:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -63,7 +63,7 @@ assign_datestyle(const char *value, bool doit, GucSource source)
|
||||
if (source >= PGC_S_INTERACTIVE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("invalid list syntax for parameter \"datestyle\"")));
|
||||
errmsg("invalid list syntax for parameter \"datestyle\"")));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -131,11 +131,11 @@ assign_datestyle(const char *value, bool doit, GucSource source)
|
||||
else if (pg_strcasecmp(tok, "DEFAULT") == 0)
|
||||
{
|
||||
/*
|
||||
* Easiest way to get the current DEFAULT state is to fetch
|
||||
* the DEFAULT string from guc.c and recursively parse it.
|
||||
* Easiest way to get the current DEFAULT state is to fetch the
|
||||
* DEFAULT string from guc.c and recursively parse it.
|
||||
*
|
||||
* We can't simply "return assign_datestyle(...)" because we need
|
||||
* to handle constructs like "DEFAULT, ISO".
|
||||
* We can't simply "return assign_datestyle(...)" because we need to
|
||||
* handle constructs like "DEFAULT, ISO".
|
||||
*/
|
||||
int saveDateStyle = DateStyle;
|
||||
int saveDateOrder = DateOrder;
|
||||
@@ -163,8 +163,8 @@ assign_datestyle(const char *value, bool doit, GucSource source)
|
||||
if (source >= PGC_S_INTERACTIVE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("unrecognized \"datestyle\" key word: \"%s\"",
|
||||
tok)));
|
||||
errmsg("unrecognized \"datestyle\" key word: \"%s\"",
|
||||
tok)));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
@@ -224,8 +224,8 @@ assign_datestyle(const char *value, bool doit, GucSource source)
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, it's safe to assign to the global variables; the
|
||||
* assignment cannot fail now.
|
||||
* Finally, it's safe to assign to the global variables; the assignment
|
||||
* cannot fail now.
|
||||
*/
|
||||
DateStyle = newDateStyle;
|
||||
DateOrder = newDateOrder;
|
||||
@@ -274,14 +274,14 @@ assign_timezone(const char *value, bool doit, GucSource source)
|
||||
|
||||
/*
|
||||
* Try to parse it. XXX an invalid interval format will result in
|
||||
* ereport, which is not desirable for GUC. We did what we could
|
||||
* to guard against this in flatten_set_variable_args, but a
|
||||
* string coming in from postgresql.conf might contain anything.
|
||||
* ereport, which is not desirable for GUC. We did what we could to
|
||||
* guard against this in flatten_set_variable_args, but a string
|
||||
* coming in from postgresql.conf might contain anything.
|
||||
*/
|
||||
interval = DatumGetIntervalP(DirectFunctionCall3(interval_in,
|
||||
CStringGetDatum(val),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
Int32GetDatum(-1)));
|
||||
CStringGetDatum(val),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
Int32GetDatum(-1)));
|
||||
|
||||
pfree(val);
|
||||
if (interval->month != 0)
|
||||
@@ -336,15 +336,14 @@ assign_timezone(const char *value, bool doit, GucSource source)
|
||||
* UNKNOWN is the value shown as the "default" for TimeZone in
|
||||
* guc.c. We interpret it as being a complete no-op; we don't
|
||||
* change the timezone setting. Note that if there is a known
|
||||
* timezone setting, we will return that name rather than
|
||||
* UNKNOWN as the canonical spelling.
|
||||
* timezone setting, we will return that name rather than UNKNOWN
|
||||
* as the canonical spelling.
|
||||
*
|
||||
* During GUC initialization, since the timezone library isn't
|
||||
* set up yet, pg_get_timezone_name will return NULL and we
|
||||
* will leave the setting as UNKNOWN. If this isn't
|
||||
* overridden from the config file then
|
||||
* pg_timezone_initialize() will eventually select a default
|
||||
* value from the environment.
|
||||
* During GUC initialization, since the timezone library isn't set up
|
||||
* yet, pg_get_timezone_name will return NULL and we will leave
|
||||
* the setting as UNKNOWN. If this isn't overridden from the
|
||||
* config file then pg_timezone_initialize() will eventually
|
||||
* select a default value from the environment.
|
||||
*/
|
||||
if (doit)
|
||||
{
|
||||
@@ -359,7 +358,7 @@ assign_timezone(const char *value, bool doit, GucSource source)
|
||||
/*
|
||||
* Otherwise assume it is a timezone name, and try to load it.
|
||||
*/
|
||||
pg_tz *new_tz;
|
||||
pg_tz *new_tz;
|
||||
|
||||
new_tz = pg_tzset(value);
|
||||
|
||||
@@ -376,9 +375,9 @@ assign_timezone(const char *value, bool doit, GucSource source)
|
||||
{
|
||||
ereport((source >= PGC_S_INTERACTIVE) ? ERROR : LOG,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("time zone \"%s\" appears to use leap seconds",
|
||||
value),
|
||||
errdetail("PostgreSQL does not support leap seconds.")));
|
||||
errmsg("time zone \"%s\" appears to use leap seconds",
|
||||
value),
|
||||
errdetail("PostgreSQL does not support leap seconds.")));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -406,7 +405,7 @@ assign_timezone(const char *value, bool doit, GucSource source)
|
||||
if (!result)
|
||||
return NULL;
|
||||
snprintf(result, 64, "%.5f",
|
||||
(double) (-CTimeZone) / (double)SECS_PER_HOUR);
|
||||
(double) (-CTimeZone) / (double) SECS_PER_HOUR);
|
||||
}
|
||||
else
|
||||
result = strdup(value);
|
||||
@@ -424,7 +423,7 @@ show_timezone(void)
|
||||
|
||||
if (HasCTZSet)
|
||||
{
|
||||
Interval interval;
|
||||
Interval interval;
|
||||
|
||||
interval.month = 0;
|
||||
interval.day = 0;
|
||||
@@ -435,7 +434,7 @@ show_timezone(void)
|
||||
#endif
|
||||
|
||||
tzn = DatumGetCString(DirectFunctionCall1(interval_out,
|
||||
IntervalPGetDatum(&interval)));
|
||||
IntervalPGetDatum(&interval)));
|
||||
}
|
||||
else
|
||||
tzn = pg_get_timezone_name(global_timezone);
|
||||
@@ -559,18 +558,18 @@ assign_client_encoding(const char *value, bool doit, GucSource source)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Note: if we are in startup phase then SetClientEncoding may not be
|
||||
* able to really set the encoding. In this case we will assume that
|
||||
* the encoding is okay, and InitializeClientEncoding() will fix
|
||||
* things once initialization is complete.
|
||||
* Note: if we are in startup phase then SetClientEncoding may not be able
|
||||
* to really set the encoding. In this case we will assume that the
|
||||
* encoding is okay, and InitializeClientEncoding() will fix things once
|
||||
* initialization is complete.
|
||||
*/
|
||||
if (SetClientEncoding(encoding, doit) < 0)
|
||||
{
|
||||
if (source >= PGC_S_INTERACTIVE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("conversion between %s and %s is not supported",
|
||||
value, GetDatabaseEncodingName())));
|
||||
errmsg("conversion between %s and %s is not supported",
|
||||
value, GetDatabaseEncodingName())));
|
||||
return NULL;
|
||||
}
|
||||
return value;
|
||||
@@ -594,7 +593,7 @@ extern char *session_authorization_string; /* in guc.c */
|
||||
const char *
|
||||
assign_session_authorization(const char *value, bool doit, GucSource source)
|
||||
{
|
||||
Oid roleid = InvalidOid;
|
||||
Oid roleid = InvalidOid;
|
||||
bool is_superuser = false;
|
||||
const char *actual_rolename = NULL;
|
||||
char *result;
|
||||
@@ -603,7 +602,7 @@ assign_session_authorization(const char *value, bool doit, GucSource source)
|
||||
(value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
|
||||
{
|
||||
/* might be a saved userid string */
|
||||
Oid savedoid;
|
||||
Oid savedoid;
|
||||
char *endptr;
|
||||
|
||||
savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
|
||||
@@ -625,9 +624,9 @@ assign_session_authorization(const char *value, bool doit, GucSource source)
|
||||
if (!IsTransactionState())
|
||||
{
|
||||
/*
|
||||
* Can't do catalog lookups, so fail. The upshot of this is
|
||||
* that session_authorization cannot be set in
|
||||
* postgresql.conf, which seems like a good thing anyway.
|
||||
* Can't do catalog lookups, so fail. The upshot of this is that
|
||||
* session_authorization cannot be set in postgresql.conf, which
|
||||
* seems like a good thing anyway.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
@@ -676,7 +675,7 @@ show_session_authorization(void)
|
||||
* assign_session_authorization
|
||||
*/
|
||||
const char *value = session_authorization_string;
|
||||
Oid savedoid;
|
||||
Oid savedoid;
|
||||
char *endptr;
|
||||
|
||||
Assert(strspn(value, "x") == NAMEDATALEN &&
|
||||
@@ -706,7 +705,7 @@ extern char *role_string; /* in guc.c */
|
||||
const char *
|
||||
assign_role(const char *value, bool doit, GucSource source)
|
||||
{
|
||||
Oid roleid = InvalidOid;
|
||||
Oid roleid = InvalidOid;
|
||||
bool is_superuser = false;
|
||||
const char *actual_rolename = value;
|
||||
char *result;
|
||||
@@ -715,7 +714,7 @@ assign_role(const char *value, bool doit, GucSource source)
|
||||
(value[NAMEDATALEN] == 'T' || value[NAMEDATALEN] == 'F'))
|
||||
{
|
||||
/* might be a saved userid string */
|
||||
Oid savedoid;
|
||||
Oid savedoid;
|
||||
char *endptr;
|
||||
|
||||
savedoid = (Oid) strtoul(value + NAMEDATALEN + 1, &endptr, 10);
|
||||
@@ -738,9 +737,9 @@ assign_role(const char *value, bool doit, GucSource source)
|
||||
if (!IsTransactionState())
|
||||
{
|
||||
/*
|
||||
* Can't do catalog lookups, so fail. The upshot of this is
|
||||
* that role cannot be set in postgresql.conf, which seems
|
||||
* like a good thing anyway.
|
||||
* Can't do catalog lookups, so fail. The upshot of this is that
|
||||
* role cannot be set in postgresql.conf, which seems like a good
|
||||
* thing anyway.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
@@ -797,11 +796,10 @@ const char *
|
||||
show_role(void)
|
||||
{
|
||||
/*
|
||||
* Extract the role name from the stored string; see
|
||||
* assign_role
|
||||
* Extract the role name from the stored string; see assign_role
|
||||
*/
|
||||
const char *value = role_string;
|
||||
Oid savedoid;
|
||||
Oid savedoid;
|
||||
char *endptr;
|
||||
|
||||
/* This special case only applies if no SET ROLE has been done */
|
||||
@@ -816,11 +814,11 @@ show_role(void)
|
||||
Assert(endptr != value + NAMEDATALEN + 1 && *endptr == ',');
|
||||
|
||||
/*
|
||||
* Check that the stored string still matches the effective setting,
|
||||
* else return "none". This is a kluge to deal with the fact that
|
||||
* SET SESSION AUTHORIZATION logically resets SET ROLE to NONE, but
|
||||
* we cannot set the GUC role variable from assign_session_authorization
|
||||
* (because we haven't got enough info to call set_config_option).
|
||||
* Check that the stored string still matches the effective setting, else
|
||||
* return "none". This is a kluge to deal with the fact that SET SESSION
|
||||
* AUTHORIZATION logically resets SET ROLE to NONE, but we cannot set the
|
||||
* GUC role variable from assign_session_authorization (because we haven't
|
||||
* got enough info to call set_config_option).
|
||||
*/
|
||||
if (savedoid != GetCurrentRoleId())
|
||||
return "none";
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.90 2005/04/14 01:38:17 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.91 2005/10/15 02:49:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -55,16 +55,18 @@ isViewOnTempTable_walker(Node *node, void *context)
|
||||
|
||||
if (IsA(node, Query))
|
||||
{
|
||||
Query *query = (Query *) node;
|
||||
ListCell *rtable;
|
||||
Query *query = (Query *) node;
|
||||
ListCell *rtable;
|
||||
|
||||
foreach (rtable, query->rtable)
|
||||
foreach(rtable, query->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = lfirst(rtable);
|
||||
|
||||
if (rte->rtekind == RTE_RELATION)
|
||||
{
|
||||
Relation rel = heap_open(rte->relid, AccessShareLock);
|
||||
bool istemp = rel->rd_istemp;
|
||||
Relation rel = heap_open(rte->relid, AccessShareLock);
|
||||
bool istemp = rel->rd_istemp;
|
||||
|
||||
heap_close(rel, AccessShareLock);
|
||||
if (istemp)
|
||||
return true;
|
||||
@@ -101,8 +103,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
||||
ListCell *t;
|
||||
|
||||
/*
|
||||
* create a list of ColumnDef nodes based on the names and types of
|
||||
* the (non-junk) targetlist items from the view's SELECT list.
|
||||
* create a list of ColumnDef nodes based on the names and types of the
|
||||
* (non-junk) targetlist items from the view's SELECT list.
|
||||
*/
|
||||
attrList = NIL;
|
||||
foreach(t, tlist)
|
||||
@@ -167,15 +169,15 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
||||
RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* Due to the namespace visibility rules for temporary
|
||||
* objects, we should only end up replacing a temporary view
|
||||
* with another temporary view, and vice versa.
|
||||
* Due to the namespace visibility rules for temporary objects, we
|
||||
* should only end up replacing a temporary view with another
|
||||
* temporary view, and vice versa.
|
||||
*/
|
||||
Assert(relation->istemp == rel->rd_istemp);
|
||||
|
||||
/*
|
||||
* Create a tuple descriptor to compare against the existing view,
|
||||
* and verify it matches.
|
||||
* Create a tuple descriptor to compare against the existing view, and
|
||||
* verify it matches.
|
||||
*/
|
||||
descriptor = BuildDescForRelation(attrList);
|
||||
checkViewTupleDesc(descriptor, rel->rd_att);
|
||||
@@ -190,8 +192,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
||||
else
|
||||
{
|
||||
/*
|
||||
* now set the parameters for keys/inheritance etc. All of these
|
||||
* are uninteresting for views...
|
||||
* now set the parameters for keys/inheritance etc. All of these are
|
||||
* uninteresting for views...
|
||||
*/
|
||||
createStmt->relation = (RangeVar *) relation;
|
||||
createStmt->tableElts = attrList;
|
||||
@@ -203,8 +205,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
|
||||
|
||||
/*
|
||||
* finally create the relation (this will error out if there's an
|
||||
* existing view, so we don't need more code to complain if
|
||||
* "replace" is false).
|
||||
* existing view, so we don't need more code to complain if "replace"
|
||||
* is false).
|
||||
*/
|
||||
return DefineRelation(createStmt, RELKIND_VIEW);
|
||||
}
|
||||
@@ -247,8 +249,8 @@ checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc)
|
||||
newattr->atttypmod != oldattr->atttypmod)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||
errmsg("cannot change data type of view column \"%s\"",
|
||||
NameStr(oldattr->attname))));
|
||||
errmsg("cannot change data type of view column \"%s\"",
|
||||
NameStr(oldattr->attname))));
|
||||
/* We can ignore the remaining attributes of an attribute... */
|
||||
}
|
||||
|
||||
@@ -265,8 +267,8 @@ FormViewRetrieveRule(const RangeVar *view, Query *viewParse, bool replace)
|
||||
RuleStmt *rule;
|
||||
|
||||
/*
|
||||
* Create a RuleStmt that corresponds to the suitable rewrite rule
|
||||
* args for DefineQueryRewrite();
|
||||
* Create a RuleStmt that corresponds to the suitable rewrite rule args
|
||||
* for DefineQueryRewrite();
|
||||
*/
|
||||
rule = makeNode(RuleStmt);
|
||||
rule->relation = copyObject((RangeVar *) view);
|
||||
@@ -336,11 +338,11 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
|
||||
|
||||
/*
|
||||
* Make a copy of the given parsetree. It's not so much that we don't
|
||||
* want to scribble on our input, it's that the parser has a bad habit
|
||||
* of outputting multiple links to the same subtree for constructs
|
||||
* like BETWEEN, and we mustn't have OffsetVarNodes increment the
|
||||
* varno of a Var node twice. copyObject will expand any
|
||||
* multiply-referenced subtree into multiple copies.
|
||||
* want to scribble on our input, it's that the parser has a bad habit of
|
||||
* outputting multiple links to the same subtree for constructs like
|
||||
* BETWEEN, and we mustn't have OffsetVarNodes increment the varno of a
|
||||
* Var node twice. copyObject will expand any multiply-referenced subtree
|
||||
* into multiple copies.
|
||||
*/
|
||||
viewParse = (Query *) copyObject(viewParse);
|
||||
|
||||
@@ -348,8 +350,8 @@ UpdateRangeTableOfViewParse(Oid viewOid, Query *viewParse)
|
||||
viewRel = relation_open(viewOid, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Create the 2 new range table entries and form the new range
|
||||
* table... OLD first, then NEW....
|
||||
* Create the 2 new range table entries and form the new range table...
|
||||
* OLD first, then NEW....
|
||||
*/
|
||||
rt_entry1 = addRangeTableEntryForRelation(NULL, viewRel,
|
||||
makeAlias("*OLD*", NIL),
|
||||
@@ -393,8 +395,8 @@ DefineView(RangeVar *view, Query *viewParse, bool replace)
|
||||
Oid viewOid;
|
||||
|
||||
/*
|
||||
* If the user didn't explicitly ask for a temporary view, check
|
||||
* whether we need one implicitly.
|
||||
* If the user didn't explicitly ask for a temporary view, check whether
|
||||
* we need one implicitly.
|
||||
*/
|
||||
if (!view->istemp)
|
||||
{
|
||||
@@ -404,25 +406,24 @@ DefineView(RangeVar *view, Query *viewParse, bool replace)
|
||||
(errmsg("view \"%s\" will be a temporary view",
|
||||
view->relname)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create the view relation
|
||||
*
|
||||
* NOTE: if it already exists and replace is false, the xact will be
|
||||
* aborted.
|
||||
* NOTE: if it already exists and replace is false, the xact will be aborted.
|
||||
*/
|
||||
viewOid = DefineVirtualRelation(view, viewParse->targetList, replace);
|
||||
|
||||
/*
|
||||
* The relation we have just created is not visible to any other
|
||||
* commands running with the same transaction & command id. So,
|
||||
* increment the command id counter (but do NOT pfree any memory!!!!)
|
||||
* The relation we have just created is not visible to any other commands
|
||||
* running with the same transaction & command id. So, increment the
|
||||
* command id counter (but do NOT pfree any memory!!!!)
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* The range table of 'viewParse' does not contain entries for the
|
||||
* "OLD" and "NEW" relations. So... add them!
|
||||
* The range table of 'viewParse' does not contain entries for the "OLD"
|
||||
* and "NEW" relations. So... add them!
|
||||
*/
|
||||
viewParse = UpdateRangeTableOfViewParse(viewOid, viewParse);
|
||||
|
||||
|
Reference in New Issue
Block a user