mirror of
https://github.com/postgres/postgres.git
synced 2025-11-07 19:06:32 +03:00
Make OIDs optional, per discussions in pghackers. WITH OIDS is still the
default, but OIDS are removed from many system catalogs that don't need them. Some interesting side effects: TOAST pointers are 20 bytes not 32 now; pg_description has a three-column key instead of one. Bugs fixed in passing: BINARY cursors work again; pg_class.relhaspkey has some usefulness; pg_dump dumps comments on indexes, rules, and triggers in a valid order. initdb forced.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.67 2001/07/12 20:35:54 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.68 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -160,7 +160,9 @@ copy_heap(Oid OIDOldHeap, char *NewName, bool istemp)
|
||||
tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
|
||||
|
||||
OIDNewHeap = heap_create_with_catalog(NewName, tupdesc,
|
||||
RELKIND_RELATION, istemp,
|
||||
OldHeap->rd_rel->relkind,
|
||||
OldHeap->rd_rel->relhasoids,
|
||||
istemp,
|
||||
allowSystemTableMods);
|
||||
|
||||
/*
|
||||
@@ -227,7 +229,8 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, char *NewIndexName)
|
||||
Old_pg_index_Form->indisprimary,
|
||||
allowSystemTableMods);
|
||||
|
||||
setRelhasindex(OIDNewHeap, true);
|
||||
setRelhasindex(OIDNewHeap, true,
|
||||
Old_pg_index_Form->indisprimary, InvalidOid);
|
||||
|
||||
ReleaseSysCache(Old_pg_index_Tuple);
|
||||
ReleaseSysCache(Old_pg_index_relation_Tuple);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.139 2001/08/10 14:30:14 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.140 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* The PerformAddAttribute() code, like most of the relation
|
||||
@@ -102,7 +102,7 @@ PerformPortalFetch(char *name,
|
||||
QueryDesc *queryDesc;
|
||||
EState *estate;
|
||||
MemoryContext oldcontext;
|
||||
bool faked_desc = false;
|
||||
bool temp_desc = false;
|
||||
|
||||
/*
|
||||
* sanity checks
|
||||
@@ -130,24 +130,33 @@ PerformPortalFetch(char *name,
|
||||
oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
|
||||
/*
|
||||
* tell the destination to prepare to receive some tuples.
|
||||
* If the requested destination is not the same as the query's
|
||||
* original destination, make a temporary QueryDesc with the proper
|
||||
* destination. This supports MOVE, for example, which will pass in
|
||||
* dest = None.
|
||||
*
|
||||
* If we've been asked for a MOVE, make a temporary QueryDesc with the
|
||||
* appropriate dummy destination.
|
||||
* EXCEPTION: if the query's original dest is RemoteInternal (ie, it's
|
||||
* a binary cursor) and the request is Remote, we do NOT override the
|
||||
* original dest. This is necessary since a FETCH command will pass
|
||||
* dest = Remote, not knowing whether the cursor is binary or not.
|
||||
*/
|
||||
queryDesc = PortalGetQueryDesc(portal);
|
||||
estate = PortalGetState(portal);
|
||||
|
||||
if (dest != queryDesc->dest) /* MOVE */
|
||||
if (dest != queryDesc->dest &&
|
||||
!(queryDesc->dest == RemoteInternal && dest == Remote))
|
||||
{
|
||||
QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc));
|
||||
|
||||
memcpy(qdesc, queryDesc, sizeof(QueryDesc));
|
||||
qdesc->dest = dest;
|
||||
queryDesc = qdesc;
|
||||
faked_desc = true;
|
||||
temp_desc = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* tell the destination to prepare to receive some tuples.
|
||||
*/
|
||||
BeginCommand(name,
|
||||
queryDesc->operation,
|
||||
PortalGetTupleDesc(portal),
|
||||
@@ -156,7 +165,7 @@ PerformPortalFetch(char *name,
|
||||
false, /* this is a portal fetch, not a "retrieve
|
||||
* portal" */
|
||||
tag,
|
||||
dest);
|
||||
queryDesc->dest);
|
||||
|
||||
/*
|
||||
* Determine which direction to go in, and check to see if we're
|
||||
@@ -205,7 +214,7 @@ PerformPortalFetch(char *name,
|
||||
/*
|
||||
* Clean up and switch back to old context.
|
||||
*/
|
||||
if (faked_desc) /* MOVE */
|
||||
if (temp_desc)
|
||||
pfree(queryDesc);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
@@ -1004,8 +1013,7 @@ AlterTableDropColumn(const char *relationName,
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
Relation rel,
|
||||
attrdesc;
|
||||
Oid myrelid,
|
||||
attoid;
|
||||
Oid myrelid;
|
||||
HeapTuple reltup;
|
||||
HeapTupleData classtuple;
|
||||
Buffer buffer;
|
||||
@@ -1094,7 +1102,6 @@ AlterTableDropColumn(const char *relationName,
|
||||
if (attnum <= 0)
|
||||
elog(ERROR, "ALTER TABLE: column name \"%s\" was already dropped",
|
||||
colName);
|
||||
attoid = tup->t_data->t_oid;
|
||||
|
||||
/*
|
||||
* Check constraints/indices etc here
|
||||
@@ -1124,8 +1131,9 @@ AlterTableDropColumn(const char *relationName,
|
||||
heap_close(attrdesc, NoLock);
|
||||
heap_freetuple(tup);
|
||||
|
||||
/* delete comments */
|
||||
DeleteComments(attoid);
|
||||
/* delete comment for this attribute only */
|
||||
CreateComments(RelationGetRelid(rel), RelOid_pg_class,
|
||||
(int32) attnum, NULL);
|
||||
|
||||
/* delete attrdef */
|
||||
drop_default(myrelid, attnum);
|
||||
@@ -1750,9 +1758,8 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
||||
Oid toast_idxid;
|
||||
char toast_relname[NAMEDATALEN + 1];
|
||||
char toast_idxname[NAMEDATALEN + 1];
|
||||
Relation toast_idxrel;
|
||||
IndexInfo *indexInfo;
|
||||
Oid classObjectId[1];
|
||||
Oid classObjectId[2];
|
||||
|
||||
/*
|
||||
* permissions checking. XXX exactly what is appropriate here?
|
||||
@@ -1870,50 +1877,49 @@ AlterTableCreateToastTable(const char *relationName, bool silent)
|
||||
* so there's no need to handle the toast rel as temp.
|
||||
*/
|
||||
toast_relid = heap_create_with_catalog(toast_relname, tupdesc,
|
||||
RELKIND_TOASTVALUE,
|
||||
RELKIND_TOASTVALUE, false,
|
||||
false, true);
|
||||
|
||||
/* make the toast relation visible, else index creation will fail */
|
||||
CommandCounterIncrement();
|
||||
|
||||
/* create index on chunk_id */
|
||||
/*
|
||||
* Create unique index on chunk_id, chunk_seq.
|
||||
*
|
||||
* NOTE: the tuple toaster could actually function with a single-column
|
||||
* index on chunk_id only. However, it couldn't be unique then. We
|
||||
* want it to be unique as a check against the possibility of duplicate
|
||||
* TOAST chunk OIDs. Too, the index might be a little more efficient this
|
||||
* way, since btree isn't all that happy with large numbers of equal keys.
|
||||
*/
|
||||
|
||||
indexInfo = makeNode(IndexInfo);
|
||||
indexInfo->ii_NumIndexAttrs = 1;
|
||||
indexInfo->ii_NumKeyAttrs = 1;
|
||||
indexInfo->ii_NumIndexAttrs = 2;
|
||||
indexInfo->ii_NumKeyAttrs = 2;
|
||||
indexInfo->ii_KeyAttrNumbers[0] = 1;
|
||||
indexInfo->ii_KeyAttrNumbers[1] = 2;
|
||||
indexInfo->ii_Predicate = NIL;
|
||||
indexInfo->ii_FuncOid = InvalidOid;
|
||||
indexInfo->ii_Unique = false;
|
||||
indexInfo->ii_Unique = true;
|
||||
|
||||
classObjectId[0] = OID_OPS_OID;
|
||||
classObjectId[1] = INT4_OPS_OID;
|
||||
|
||||
index_create(toast_relname, toast_idxname, indexInfo,
|
||||
BTREE_AM_OID, classObjectId,
|
||||
false, false, true);
|
||||
toast_idxid = index_create(toast_relname, toast_idxname, indexInfo,
|
||||
BTREE_AM_OID, classObjectId,
|
||||
false, true, true);
|
||||
|
||||
/*
|
||||
* Update toast rel's pg_class entry to show that it has an index.
|
||||
* The index OID is stored into the reltoastidxid field for
|
||||
* easy access by the tuple toaster.
|
||||
*/
|
||||
setRelhasindex(toast_relid, true);
|
||||
setRelhasindex(toast_relid, true, true, toast_idxid);
|
||||
|
||||
/*
|
||||
* Make index visible
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* Get the OID of the newly created index
|
||||
*/
|
||||
toast_idxrel = index_openr(toast_idxname);
|
||||
toast_idxid = RelationGetRelid(toast_idxrel);
|
||||
index_close(toast_idxrel);
|
||||
|
||||
/*
|
||||
* Store the toast table- and index-Oid's in the relation tuple
|
||||
* Store the toast table's OID in the parent relation's tuple
|
||||
*/
|
||||
((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
|
||||
((Form_pg_class) GETSTRUCT(reltup))->reltoastidxid = toast_idxid;
|
||||
simple_heap_update(class_rel, &reltup->t_self, reltup);
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
* Copyright (c) 1999-2001, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.31 2001/06/25 21:11:43 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.32 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/indexing.h"
|
||||
@@ -22,7 +23,6 @@
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_trigger.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "catalog/pg_class.h"
|
||||
#include "commands/comment.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_expr.h"
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "utils/acl.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
@@ -54,7 +55,7 @@ static void CommentAggregate(char *aggregate, List *arguments, char *comment);
|
||||
static void CommentProc(char *function, List *arguments, char *comment);
|
||||
static void CommentOperator(char *opname, List *arguments, char *comment);
|
||||
static void CommentTrigger(char *trigger, char *relation, char *comments);
|
||||
static void CreateComments(Oid oid, char *comment);
|
||||
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
* CommentObject --
|
||||
@@ -64,7 +65,7 @@ static void CreateComments(Oid oid, char *comment);
|
||||
* to this routine. If the routine cannot determine an Oid to
|
||||
* associated with the parameters handed to this routine, an
|
||||
* error is thrown. Otherwise the comment is added to pg_description
|
||||
* by calling the CreateComments() routine. If the comments were
|
||||
* by calling the CreateComments() routine. If the comment string is
|
||||
* empty, CreateComments() will drop any comments associated with
|
||||
* the object.
|
||||
*------------------------------------------------------------------
|
||||
@@ -74,135 +75,158 @@ void
|
||||
CommentObject(int objtype, char *objname, char *objproperty,
|
||||
List *objlist, char *comment)
|
||||
{
|
||||
|
||||
switch (objtype)
|
||||
{
|
||||
case (INDEX):
|
||||
case (SEQUENCE):
|
||||
case (TABLE):
|
||||
case (VIEW):
|
||||
case INDEX:
|
||||
case SEQUENCE:
|
||||
case TABLE:
|
||||
case VIEW:
|
||||
CommentRelation(objtype, objname, comment);
|
||||
break;
|
||||
case (COLUMN):
|
||||
case COLUMN:
|
||||
CommentAttribute(objname, objproperty, comment);
|
||||
break;
|
||||
case (DATABASE):
|
||||
case DATABASE:
|
||||
CommentDatabase(objname, comment);
|
||||
break;
|
||||
case (RULE):
|
||||
case RULE:
|
||||
CommentRewrite(objname, comment);
|
||||
break;
|
||||
case (TYPE_P):
|
||||
case TYPE_P:
|
||||
CommentType(objname, comment);
|
||||
break;
|
||||
case (AGGREGATE):
|
||||
case AGGREGATE:
|
||||
CommentAggregate(objname, objlist, comment);
|
||||
break;
|
||||
case (FUNCTION):
|
||||
case FUNCTION:
|
||||
CommentProc(objname, objlist, comment);
|
||||
break;
|
||||
case (OPERATOR):
|
||||
case OPERATOR:
|
||||
CommentOperator(objname, objlist, comment);
|
||||
break;
|
||||
case (TRIGGER):
|
||||
case TRIGGER:
|
||||
CommentTrigger(objname, objproperty, comment);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "An attempt was made to comment on a unknown type: %i",
|
||||
elog(ERROR, "An attempt was made to comment on a unknown type: %d",
|
||||
objtype);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
* CreateComments --
|
||||
*
|
||||
* This routine is handed the oid and the command associated
|
||||
* with that id and will insert, update, or delete (if the
|
||||
* comment is an empty string or a NULL pointer) the associated
|
||||
* comment from the system cataloge, pg_description.
|
||||
* Create a comment for the specified object descriptor. Inserts a new
|
||||
* pg_description tuple, or replaces an existing one with the same key.
|
||||
*
|
||||
* If the comment given is null or an empty string, instead delete any
|
||||
* existing comment for the specified key.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void
|
||||
CreateComments(Oid oid, char *comment)
|
||||
void
|
||||
CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
|
||||
{
|
||||
|
||||
Relation description;
|
||||
TupleDesc tupDesc;
|
||||
HeapScanDesc scan;
|
||||
ScanKeyData entry;
|
||||
HeapTuple desctuple = NULL,
|
||||
searchtuple;
|
||||
Relation descriptionindex;
|
||||
ScanKeyData skey[3];
|
||||
IndexScanDesc sd;
|
||||
RetrieveIndexResult indexRes;
|
||||
HeapTupleData oldtuple;
|
||||
Buffer buffer;
|
||||
HeapTuple newtuple = NULL;
|
||||
Datum values[Natts_pg_description];
|
||||
char nulls[Natts_pg_description];
|
||||
char replaces[Natts_pg_description];
|
||||
bool modified = false;
|
||||
int i;
|
||||
|
||||
/*** Open pg_description, form a new tuple, if necessary ***/
|
||||
/* Reduce empty-string to NULL case */
|
||||
if (comment != NULL && strlen(comment) == 0)
|
||||
comment = NULL;
|
||||
|
||||
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
||||
tupDesc = description->rd_att;
|
||||
if ((comment != NULL) && (strlen(comment) > 0))
|
||||
/* Prepare to form or update a tuple, if necessary */
|
||||
if (comment != NULL)
|
||||
{
|
||||
for (i = 0; i < Natts_pg_description; i++)
|
||||
{
|
||||
nulls[i] = ' ';
|
||||
replaces[i] = 'r';
|
||||
values[i] = (Datum) NULL;
|
||||
}
|
||||
i = 0;
|
||||
values[i++] = ObjectIdGetDatum(oid);
|
||||
values[i++] = ObjectIdGetDatum(classoid);
|
||||
values[i++] = Int32GetDatum(subid);
|
||||
values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
|
||||
}
|
||||
|
||||
/*** Now, open pg_description and attempt to find the old tuple ***/
|
||||
/* Open pg_description and its index */
|
||||
|
||||
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
|
||||
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
||||
descriptionindex = index_openr(DescriptionObjIndex);
|
||||
|
||||
/* Use the index to search for a matching old tuple */
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 1,
|
||||
(RegProcedure) F_OIDEQ,
|
||||
ObjectIdGetDatum(oid));
|
||||
scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
|
||||
searchtuple = heap_getnext(scan, 0);
|
||||
|
||||
/*** If a previous tuple exists, either delete or prep replacement ***/
|
||||
ScanKeyEntryInitialize(&skey[1],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 2,
|
||||
(RegProcedure) F_OIDEQ,
|
||||
ObjectIdGetDatum(classoid));
|
||||
|
||||
if (HeapTupleIsValid(searchtuple))
|
||||
ScanKeyEntryInitialize(&skey[2],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 3,
|
||||
(RegProcedure) F_INT4EQ,
|
||||
Int32GetDatum(subid));
|
||||
|
||||
sd = index_beginscan(descriptionindex, false, 3, skey);
|
||||
|
||||
oldtuple.t_datamcxt = CurrentMemoryContext;
|
||||
oldtuple.t_data = NULL;
|
||||
|
||||
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
||||
{
|
||||
oldtuple.t_self = indexRes->heap_iptr;
|
||||
heap_fetch(description, SnapshotNow, &oldtuple, &buffer, sd);
|
||||
pfree(indexRes);
|
||||
|
||||
/*** If the comment is blank, delete old entry, else update it ***/
|
||||
if (oldtuple.t_data == NULL)
|
||||
continue; /* time qual failed */
|
||||
|
||||
if ((comment == NULL) || (strlen(comment) == 0))
|
||||
simple_heap_delete(description, &searchtuple->t_self);
|
||||
/* Found the old tuple, so delete or update it */
|
||||
|
||||
if (comment == NULL)
|
||||
simple_heap_delete(description, &oldtuple.t_self);
|
||||
else
|
||||
{
|
||||
desctuple = heap_modifytuple(searchtuple, description, values,
|
||||
nulls, replaces);
|
||||
simple_heap_update(description, &searchtuple->t_self, desctuple);
|
||||
modified = TRUE;
|
||||
newtuple = heap_modifytuple(&oldtuple, description, values,
|
||||
nulls, replaces);
|
||||
simple_heap_update(description, &oldtuple.t_self, newtuple);
|
||||
}
|
||||
|
||||
ReleaseBuffer(buffer);
|
||||
break; /* Assume there can be only one match */
|
||||
}
|
||||
else
|
||||
|
||||
index_endscan(sd);
|
||||
|
||||
/* If we didn't find an old tuple, insert a new one */
|
||||
|
||||
if (oldtuple.t_data == NULL && comment != NULL)
|
||||
{
|
||||
|
||||
/*** Only if comment is non-blank do we form a new tuple ***/
|
||||
|
||||
if ((comment != NULL) && (strlen(comment) > 0))
|
||||
{
|
||||
desctuple = heap_formtuple(tupDesc, values, nulls);
|
||||
heap_insert(description, desctuple);
|
||||
modified = TRUE;
|
||||
}
|
||||
|
||||
newtuple = heap_formtuple(RelationGetDescr(description),
|
||||
values, nulls);
|
||||
heap_insert(description, newtuple);
|
||||
}
|
||||
|
||||
/*** Complete the scan, update indices, if necessary ***/
|
||||
/* Update indexes, if necessary */
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
if (modified)
|
||||
if (newtuple != NULL)
|
||||
{
|
||||
if (RelationGetForm(description)->relhasindex)
|
||||
{
|
||||
@@ -211,57 +235,78 @@ CreateComments(Oid oid, char *comment)
|
||||
CatalogOpenIndices(Num_pg_description_indices,
|
||||
Name_pg_description_indices, idescs);
|
||||
CatalogIndexInsert(idescs, Num_pg_description_indices, description,
|
||||
desctuple);
|
||||
newtuple);
|
||||
CatalogCloseIndices(Num_pg_description_indices, idescs);
|
||||
}
|
||||
heap_freetuple(desctuple);
|
||||
|
||||
heap_freetuple(newtuple);
|
||||
}
|
||||
|
||||
heap_close(description, RowExclusiveLock);
|
||||
/* Done */
|
||||
|
||||
index_close(descriptionindex);
|
||||
heap_close(description, NoLock);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
* DeleteComments --
|
||||
*
|
||||
* This routine is used to purge any comments
|
||||
* associated with the Oid handed to this routine,
|
||||
* regardless of the actual object type. It is
|
||||
* called, for example, when a relation is destroyed.
|
||||
* This routine is used to purge all comments associated with an object,
|
||||
* regardless of their objsubid. It is called, for example, when a relation
|
||||
* is destroyed.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
DeleteComments(Oid oid)
|
||||
DeleteComments(Oid oid, Oid classoid)
|
||||
{
|
||||
|
||||
Relation description;
|
||||
TupleDesc tupDesc;
|
||||
ScanKeyData entry;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple searchtuple;
|
||||
Relation descriptionindex;
|
||||
ScanKeyData skey[2];
|
||||
IndexScanDesc sd;
|
||||
RetrieveIndexResult indexRes;
|
||||
HeapTupleData oldtuple;
|
||||
Buffer buffer;
|
||||
|
||||
/* Open pg_description and its index */
|
||||
|
||||
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
||||
tupDesc = description->rd_att;
|
||||
descriptionindex = index_openr(DescriptionObjIndex);
|
||||
|
||||
/*** Now, open pg_description and attempt to find the old tuple ***/
|
||||
/* Use the index to search for all matching old tuples */
|
||||
|
||||
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
|
||||
ScanKeyEntryInitialize(&skey[0],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 1,
|
||||
(RegProcedure) F_OIDEQ,
|
||||
ObjectIdGetDatum(oid));
|
||||
scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
|
||||
searchtuple = heap_getnext(scan, 0);
|
||||
|
||||
/*** If a previous tuple exists, delete it ***/
|
||||
ScanKeyEntryInitialize(&skey[1],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 2,
|
||||
(RegProcedure) F_OIDEQ,
|
||||
ObjectIdGetDatum(classoid));
|
||||
|
||||
if (HeapTupleIsValid(searchtuple))
|
||||
simple_heap_delete(description, &searchtuple->t_self);
|
||||
sd = index_beginscan(descriptionindex, false, 2, skey);
|
||||
|
||||
/*** Complete the scan, update indices, if necessary ***/
|
||||
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
||||
{
|
||||
oldtuple.t_self = indexRes->heap_iptr;
|
||||
heap_fetch(description, SnapshotNow, &oldtuple, &buffer, sd);
|
||||
pfree(indexRes);
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(description, RowExclusiveLock);
|
||||
if (oldtuple.t_data == NULL)
|
||||
continue; /* time qual failed */
|
||||
|
||||
simple_heap_delete(description, &oldtuple.t_self);
|
||||
|
||||
ReleaseBuffer(buffer);
|
||||
}
|
||||
|
||||
/* Done */
|
||||
|
||||
index_endscan(sd);
|
||||
index_close(descriptionindex);
|
||||
heap_close(description, NoLock);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -273,59 +318,65 @@ DeleteComments(Oid oid)
|
||||
* the appropriate tuple, and inserting a comment using that
|
||||
* tuple's oid. Its parameters are the relation name and comments.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
*/
|
||||
|
||||
static void
|
||||
CommentRelation(int reltype, char *relname, char *comment)
|
||||
{
|
||||
HeapTuple reltuple;
|
||||
Oid oid;
|
||||
char relkind;
|
||||
Relation relation;
|
||||
|
||||
/*** First, check object security ***/
|
||||
/* First, check object security */
|
||||
|
||||
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
|
||||
elog(ERROR, "you are not permitted to comment on class '%s'", relname);
|
||||
|
||||
/*** Now, attempt to find the oid in the cached version of pg_class ***/
|
||||
/*
|
||||
* 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.)
|
||||
*
|
||||
* heap_openr will complain if it's an index, so we must do this:
|
||||
*/
|
||||
if (reltype != INDEX)
|
||||
relation = heap_openr(relname, AccessShareLock);
|
||||
else
|
||||
{
|
||||
relation = index_openr(relname);
|
||||
LockRelation(relation, AccessShareLock);
|
||||
}
|
||||
|
||||
reltuple = SearchSysCache(RELNAME,
|
||||
PointerGetDatum(relname),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(reltuple))
|
||||
elog(ERROR, "relation '%s' does not exist", relname);
|
||||
|
||||
oid = reltuple->t_data->t_oid;
|
||||
|
||||
relkind = ((Form_pg_class) GETSTRUCT(reltuple))->relkind;
|
||||
|
||||
ReleaseSysCache(reltuple);
|
||||
|
||||
/*** Next, verify that the relation type matches the intent ***/
|
||||
/* Next, verify that the relation type matches the intent */
|
||||
|
||||
switch (reltype)
|
||||
{
|
||||
case (INDEX):
|
||||
if (relkind != RELKIND_INDEX)
|
||||
case INDEX:
|
||||
if (relation->rd_rel->relkind != RELKIND_INDEX)
|
||||
elog(ERROR, "relation '%s' is not an index", relname);
|
||||
break;
|
||||
case (TABLE):
|
||||
if (relkind != RELKIND_RELATION)
|
||||
case TABLE:
|
||||
if (relation->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "relation '%s' is not a table", relname);
|
||||
break;
|
||||
case (VIEW):
|
||||
if (relkind != RELKIND_VIEW)
|
||||
case VIEW:
|
||||
if (relation->rd_rel->relkind != RELKIND_VIEW)
|
||||
elog(ERROR, "relation '%s' is not a view", relname);
|
||||
break;
|
||||
case (SEQUENCE):
|
||||
if (relkind != RELKIND_SEQUENCE)
|
||||
case SEQUENCE:
|
||||
if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
|
||||
elog(ERROR, "relation '%s' is not a sequence", relname);
|
||||
break;
|
||||
}
|
||||
|
||||
/*** Create the comments using the tuple's oid ***/
|
||||
/* Create the comment using the relation's oid */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
CreateComments(RelationGetRelid(relation), RelOid_pg_class, 0, comment);
|
||||
|
||||
/* Done, but hold lock until commit */
|
||||
|
||||
if (reltype != INDEX)
|
||||
heap_close(relation, NoLock);
|
||||
else
|
||||
index_close(relation);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -333,7 +384,7 @@ CommentRelation(int reltype, char *relname, char *comment)
|
||||
*
|
||||
* This routine is used to add/drop a comment from an attribute
|
||||
* such as a table's column. The routine will check security
|
||||
* restrictions and then attempt to fetch the oid of the associated
|
||||
* restrictions and then attempt to look up the specified
|
||||
* attribute. If successful, a comment is added/dropped, else an
|
||||
* elog() exception is thrown. The parameters are the relation
|
||||
* and attribute names, and the comments
|
||||
@@ -344,32 +395,30 @@ static void
|
||||
CommentAttribute(char *relname, char *attrname, char *comment)
|
||||
{
|
||||
Relation relation;
|
||||
Oid oid;
|
||||
AttrNumber attnum;
|
||||
|
||||
/*** First, check object security ***/
|
||||
/* First, check object security */
|
||||
|
||||
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
|
||||
elog(ERROR, "you are not permitted to comment on class '%s\'", relname);
|
||||
elog(ERROR, "you are not permitted to comment on class '%s'", relname);
|
||||
|
||||
/* Open the containing relation to ensure it won't go away meanwhile */
|
||||
|
||||
relation = heap_openr(relname, AccessShareLock);
|
||||
|
||||
/*** Now, fetch the attribute oid from the system cache ***/
|
||||
/* Now, fetch the attribute number from the system cache */
|
||||
|
||||
oid = GetSysCacheOid(ATTNAME,
|
||||
ObjectIdGetDatum(relation->rd_id),
|
||||
PointerGetDatum(attrname),
|
||||
0, 0);
|
||||
if (!OidIsValid(oid))
|
||||
attnum = get_attnum(RelationGetRelid(relation), attrname);
|
||||
if (attnum == InvalidAttrNumber)
|
||||
elog(ERROR, "'%s' is not an attribute of class '%s'",
|
||||
attrname, relname);
|
||||
|
||||
/*** Call CreateComments() to create/drop the comments ***/
|
||||
/* Create the comment using the relation's oid */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
CreateComments(RelationGetRelid(relation), RelOid_pg_class,
|
||||
(int32) attnum, comment);
|
||||
|
||||
/*** Now, close the heap relation and return ***/
|
||||
/* Done, but hold lock until commit */
|
||||
|
||||
heap_close(relation, NoLock);
|
||||
}
|
||||
@@ -394,7 +443,7 @@ CommentDatabase(char *database, char *comment)
|
||||
HeapTuple dbtuple;
|
||||
Oid oid;
|
||||
|
||||
/*** First find the tuple in pg_database for the database ***/
|
||||
/* First find the tuple in pg_database for the database */
|
||||
|
||||
pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
|
||||
ScanKeyEntryInitialize(&entry, 0, Anum_pg_database_datname,
|
||||
@@ -402,23 +451,23 @@ CommentDatabase(char *database, char *comment)
|
||||
scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, &entry);
|
||||
dbtuple = heap_getnext(scan, 0);
|
||||
|
||||
/*** Validate database exists, and fetch the db oid ***/
|
||||
/* Validate database exists, and fetch the db oid */
|
||||
|
||||
if (!HeapTupleIsValid(dbtuple))
|
||||
elog(ERROR, "database '%s' does not exist", database);
|
||||
oid = dbtuple->t_data->t_oid;
|
||||
|
||||
/*** Allow if the user matches the database dba or is a superuser ***/
|
||||
/* Allow if the user matches the database dba or is a superuser */
|
||||
|
||||
if (!(superuser() || is_dbadmin(oid)))
|
||||
elog(ERROR, "you are not permitted to comment on database '%s'",
|
||||
database);
|
||||
|
||||
/*** Create the comments with the pg_database oid ***/
|
||||
/* Create the comments with the pg_database oid */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
CreateComments(oid, RelOid_pg_database, 0, comment);
|
||||
|
||||
/*** Complete the scan and close any opened relations ***/
|
||||
/* Complete the scan and close any opened relations */
|
||||
|
||||
heap_endscan(scan);
|
||||
heap_close(pg_database, AccessShareLock);
|
||||
@@ -438,22 +487,19 @@ static void
|
||||
CommentRewrite(char *rule, char *comment)
|
||||
{
|
||||
Oid oid;
|
||||
Oid classoid;
|
||||
char *relation;
|
||||
int aclcheck;
|
||||
|
||||
/*** First, validate user ***/
|
||||
/* First, validate user */
|
||||
|
||||
#ifndef NO_SECURITY
|
||||
relation = RewriteGetRuleEventRel(rule);
|
||||
aclcheck = pg_aclcheck(relation, GetUserId(), ACL_RULE);
|
||||
if (aclcheck != ACLCHECK_OK)
|
||||
{
|
||||
elog(ERROR, "you are not permitted to comment on rule '%s'",
|
||||
rule);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*** Next, find the rule's oid ***/
|
||||
/* Next, find the rule's oid */
|
||||
|
||||
oid = GetSysCacheOid(RULENAME,
|
||||
PointerGetDatum(rule),
|
||||
@@ -461,9 +507,16 @@ CommentRewrite(char *rule, char *comment)
|
||||
if (!OidIsValid(oid))
|
||||
elog(ERROR, "rule '%s' does not exist", rule);
|
||||
|
||||
/*** Call CreateComments() to create/drop the comments ***/
|
||||
/* pg_rewrite doesn't have a hard-coded OID, so must look it up */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
classoid = GetSysCacheOid(RELNAME,
|
||||
PointerGetDatum(RewriteRelationName),
|
||||
0, 0, 0);
|
||||
Assert(OidIsValid(classoid));
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
|
||||
CreateComments(oid, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -482,13 +535,13 @@ CommentType(char *type, char *comment)
|
||||
{
|
||||
Oid oid;
|
||||
|
||||
/*** First, validate user ***/
|
||||
/* First, validate user */
|
||||
|
||||
if (!pg_ownercheck(GetUserId(), type, TYPENAME))
|
||||
elog(ERROR, "you are not permitted to comment on type '%s'",
|
||||
type);
|
||||
|
||||
/*** Next, find the type's oid ***/
|
||||
/* Next, find the type's oid */
|
||||
|
||||
oid = GetSysCacheOid(TYPENAME,
|
||||
PointerGetDatum(type),
|
||||
@@ -496,9 +549,9 @@ CommentType(char *type, char *comment)
|
||||
if (!OidIsValid(oid))
|
||||
elog(ERROR, "type '%s' does not exist", type);
|
||||
|
||||
/*** Call CreateComments() to create/drop the comments ***/
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
CreateComments(oid, RelOid_pg_type, 0, comment);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -518,9 +571,10 @@ CommentAggregate(char *aggregate, List *arguments, char *comment)
|
||||
char *aggtypename = NULL;
|
||||
Oid baseoid,
|
||||
oid;
|
||||
Oid classoid;
|
||||
bool defined;
|
||||
|
||||
/*** First, attempt to determine the base aggregate oid ***/
|
||||
/* First, attempt to determine the base aggregate oid */
|
||||
|
||||
if (aggtype)
|
||||
{
|
||||
@@ -530,21 +584,21 @@ CommentAggregate(char *aggregate, List *arguments, char *comment)
|
||||
elog(ERROR, "type '%s' does not exist", aggtypename);
|
||||
}
|
||||
else
|
||||
baseoid = 0;
|
||||
baseoid = InvalidOid;
|
||||
|
||||
/*** Next, validate the user's attempt to comment ***/
|
||||
/* Next, validate the user's attempt to comment */
|
||||
|
||||
if (!pg_aggr_ownercheck(GetUserId(), aggregate, baseoid))
|
||||
{
|
||||
if (aggtypename)
|
||||
elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
|
||||
aggregate, "with type", aggtypename);
|
||||
elog(ERROR, "you are not permitted to comment on aggregate '%s' with type '%s'",
|
||||
aggregate, aggtypename);
|
||||
else
|
||||
elog(ERROR, "you are not permitted to comment on aggregate '%s'",
|
||||
aggregate);
|
||||
}
|
||||
|
||||
/*** Now, attempt to find the actual tuple in pg_aggregate ***/
|
||||
/* Now, attempt to find the actual tuple in pg_aggregate */
|
||||
|
||||
oid = GetSysCacheOid(AGGNAME,
|
||||
PointerGetDatum(aggregate),
|
||||
@@ -553,17 +607,22 @@ CommentAggregate(char *aggregate, List *arguments, char *comment)
|
||||
if (!OidIsValid(oid))
|
||||
{
|
||||
if (aggtypename)
|
||||
{
|
||||
elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
|
||||
aggtypename, aggregate);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "aggregate '%s' does not exist", aggregate);
|
||||
}
|
||||
|
||||
/*** Call CreateComments() to create/drop the comments ***/
|
||||
/* pg_aggregate doesn't have a hard-coded OID, so must look it up */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
classoid = GetSysCacheOid(RELNAME,
|
||||
PointerGetDatum(AggregateRelationName),
|
||||
0, 0, 0);
|
||||
Assert(OidIsValid(classoid));
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
|
||||
CreateComments(oid, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -585,7 +644,7 @@ CommentProc(char *function, List *arguments, char *comment)
|
||||
int i,
|
||||
argcount;
|
||||
|
||||
/*** First, initialize function's argument list with their type oids ***/
|
||||
/* First, initialize function's argument list with their type oids */
|
||||
|
||||
MemSet(argoids, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
argcount = length(arguments);
|
||||
@@ -611,13 +670,13 @@ CommentProc(char *function, List *arguments, char *comment)
|
||||
}
|
||||
}
|
||||
|
||||
/*** Now, validate the user's ability to comment on this function ***/
|
||||
/* Now, validate the user's ability to comment on this function */
|
||||
|
||||
if (!pg_func_ownercheck(GetUserId(), function, argcount, argoids))
|
||||
elog(ERROR, "you are not permitted to comment on function '%s'",
|
||||
function);
|
||||
|
||||
/*** Now, find the corresponding oid for this procedure ***/
|
||||
/* Now, find the corresponding oid for this procedure */
|
||||
|
||||
oid = GetSysCacheOid(PROCNAME,
|
||||
PointerGetDatum(function),
|
||||
@@ -627,9 +686,9 @@ CommentProc(char *function, List *arguments, char *comment)
|
||||
if (!OidIsValid(oid))
|
||||
func_error("CommentProc", function, argcount, argoids, NULL);
|
||||
|
||||
/*** Call CreateComments() to create/drop the comments ***/
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
CreateComments(oid, RelOid_pg_proc, 0, comment);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -642,6 +701,10 @@ CommentProc(char *function, List *arguments, char *comment)
|
||||
* expected to be a couple of parse nodes pointed to be a List
|
||||
* object. If the comments string is empty, the associated comment
|
||||
* is dropped.
|
||||
*
|
||||
* NOTE: we actually attach the comment to the procedure that underlies
|
||||
* the operator. This is a feature, not a bug: we want the same comment
|
||||
* to be visible for both operator and function.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -660,14 +723,14 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
rightoid = InvalidOid;
|
||||
bool defined;
|
||||
|
||||
/*** Initialize our left and right argument types ***/
|
||||
/* Initialize our left and right argument types */
|
||||
|
||||
if (typenode1 != NULL)
|
||||
lefttype = TypeNameToInternalName(typenode1);
|
||||
if (typenode2 != NULL)
|
||||
righttype = TypeNameToInternalName(typenode2);
|
||||
|
||||
/*** Attempt to fetch the left oid, if specified ***/
|
||||
/* Attempt to fetch the left oid, if specified */
|
||||
|
||||
if (lefttype != NULL)
|
||||
{
|
||||
@@ -676,7 +739,7 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
elog(ERROR, "left type '%s' does not exist", lefttype);
|
||||
}
|
||||
|
||||
/*** Attempt to fetch the right oid, if specified ***/
|
||||
/* Attempt to fetch the right oid, if specified */
|
||||
|
||||
if (righttype != NULL)
|
||||
{
|
||||
@@ -685,7 +748,7 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
elog(ERROR, "right type '%s' does not exist", righttype);
|
||||
}
|
||||
|
||||
/*** Determine operator type ***/
|
||||
/* Determine operator type */
|
||||
|
||||
if (OidIsValid(leftoid) && (OidIsValid(rightoid)))
|
||||
oprtype = 'b';
|
||||
@@ -696,7 +759,7 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
else
|
||||
elog(ERROR, "operator '%s' is of an illegal type'", opername);
|
||||
|
||||
/*** Attempt to fetch the operator oid ***/
|
||||
/* Attempt to fetch the operator oid */
|
||||
|
||||
optuple = SearchSysCache(OPERNAME,
|
||||
PointerGetDatum(opername),
|
||||
@@ -708,13 +771,13 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
|
||||
oid = optuple->t_data->t_oid;
|
||||
|
||||
/*** Valid user's ability to comment on this operator ***/
|
||||
/* Valid user's ability to comment on this operator */
|
||||
|
||||
if (!pg_oper_ownercheck(GetUserId(), oid))
|
||||
elog(ERROR, "you are not permitted to comment on operator '%s'",
|
||||
opername);
|
||||
|
||||
/*** Get the procedure associated with the operator ***/
|
||||
/* Get the procedure associated with the operator */
|
||||
|
||||
data = (Form_pg_operator) GETSTRUCT(optuple);
|
||||
oid = data->oprcode;
|
||||
@@ -723,9 +786,9 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
|
||||
ReleaseSysCache(optuple);
|
||||
|
||||
/*** Call CreateComments() to create/drop the comments ***/
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
|
||||
CreateComments(oid, comment);
|
||||
CreateComments(oid, RelOid_pg_proc, 0, comment);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------
|
||||
@@ -742,55 +805,48 @@ CommentOperator(char *opername, List *arguments, char *comment)
|
||||
static void
|
||||
CommentTrigger(char *trigger, char *relname, char *comment)
|
||||
{
|
||||
|
||||
Form_pg_trigger data;
|
||||
Relation pg_trigger,
|
||||
relation;
|
||||
HeapTuple triggertuple;
|
||||
HeapScanDesc scan;
|
||||
ScanKeyData entry;
|
||||
Oid oid = InvalidOid;
|
||||
ScanKeyData entry[2];
|
||||
Oid oid;
|
||||
|
||||
/*** First, validate the user's action ***/
|
||||
/* First, validate the user's action */
|
||||
|
||||
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
|
||||
elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
|
||||
trigger, "defined for relation", relname);
|
||||
|
||||
/*** Now, fetch the trigger oid from pg_trigger ***/
|
||||
/* Now, fetch the trigger oid from pg_trigger */
|
||||
|
||||
relation = heap_openr(relname, AccessShareLock);
|
||||
pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
|
||||
ScanKeyEntryInitialize(&entry, 0, Anum_pg_trigger_tgrelid,
|
||||
F_OIDEQ, RelationGetRelid(relation));
|
||||
scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 1, &entry);
|
||||
ScanKeyEntryInitialize(&entry[0], 0x0, Anum_pg_trigger_tgrelid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(RelationGetRelid(relation)));
|
||||
ScanKeyEntryInitialize(&entry[1], 0x0, Anum_pg_trigger_tgname,
|
||||
F_NAMEEQ,
|
||||
NameGetDatum(trigger));
|
||||
scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 2, entry);
|
||||
triggertuple = heap_getnext(scan, 0);
|
||||
while (HeapTupleIsValid(triggertuple))
|
||||
{
|
||||
data = (Form_pg_trigger) GETSTRUCT(triggertuple);
|
||||
if (namestrcmp(&(data->tgname), trigger) == 0)
|
||||
{
|
||||
oid = triggertuple->t_data->t_oid;
|
||||
break;
|
||||
}
|
||||
triggertuple = heap_getnext(scan, 0);
|
||||
}
|
||||
|
||||
/*** If no trigger exists for the relation specified, notify user ***/
|
||||
/* If no trigger exists for the relation specified, notify user */
|
||||
|
||||
if (oid == InvalidOid)
|
||||
{
|
||||
if (!HeapTupleIsValid(triggertuple))
|
||||
elog(ERROR, "trigger '%s' defined for relation '%s' does not exist",
|
||||
trigger, relname);
|
||||
}
|
||||
|
||||
/*** Create the comments with the pg_trigger oid ***/
|
||||
|
||||
CreateComments(oid, comment);
|
||||
|
||||
/*** Complete the scan and close any opened relations ***/
|
||||
oid = triggertuple->t_data->t_oid;
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
/* Create the comments with the pg_trigger oid */
|
||||
|
||||
CreateComments(oid, RelationGetRelid(pg_trigger), 0, comment);
|
||||
|
||||
/* Done, but hold lock on relation */
|
||||
|
||||
heap_close(pg_trigger, AccessShareLock);
|
||||
heap_close(relation, NoLock);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.140 2001/07/11 21:53:59 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.141 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -235,43 +235,45 @@ CopyDonePeek(FILE *fp, int c, int pickup)
|
||||
|
||||
/*
|
||||
* DoCopy executes the SQL COPY statement.
|
||||
*
|
||||
* Either unload or reload contents of table <relname>, depending on <from>.
|
||||
* (<from> = TRUE means we are inserting into the table.)
|
||||
*
|
||||
* If <pipe> is false, transfer is between the table and the file named
|
||||
* <filename>. Otherwise, transfer is between the table and our regular
|
||||
* input/output stream. The latter could be either stdin/stdout or a
|
||||
* socket, depending on whether we're running under Postmaster control.
|
||||
*
|
||||
* Iff <binary>, unload or reload in the binary format, as opposed to the
|
||||
* more wasteful but more robust and portable text format.
|
||||
*
|
||||
* Iff <oids>, unload or reload the format that includes OID information.
|
||||
* On input, we accept OIDs whether or not the table has an OID column,
|
||||
* but silently drop them if it does not. On output, we report an error
|
||||
* if the user asks for OIDs in a table that has none (not providing an
|
||||
* OID column might seem friendlier, but could seriously confuse programs).
|
||||
*
|
||||
* If in the text format, delimit columns with delimiter <delim> and print
|
||||
* NULL values as <null_print>.
|
||||
*
|
||||
* When loading in the text format from an input stream (as opposed to
|
||||
* a file), recognize a "." on a line by itself as EOF. Also recognize
|
||||
* a stream EOF. When unloading in the text format to an output stream,
|
||||
* write a "." on a line by itself at the end of the data.
|
||||
*
|
||||
* Do not allow a Postgres user without superuser privilege to read from
|
||||
* or write to a file.
|
||||
*
|
||||
* Do not allow the copy if user doesn't have proper permission to access
|
||||
* the table.
|
||||
*/
|
||||
|
||||
void
|
||||
DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
|
||||
char *filename, char *delim, char *null_print)
|
||||
{
|
||||
/*----------------------------------------------------------------------------
|
||||
Either unload or reload contents of class <relname>, depending on <from>.
|
||||
|
||||
If <pipe> is false, transfer is between the class and the file named
|
||||
<filename>. Otherwise, transfer is between the class and our regular
|
||||
input/output stream. The latter could be either stdin/stdout or a
|
||||
socket, depending on whether we're running under Postmaster control.
|
||||
|
||||
Iff <binary>, unload or reload in the binary format, as opposed to the
|
||||
more wasteful but more robust and portable text format.
|
||||
|
||||
If in the text format, delimit columns with delimiter <delim> and print
|
||||
NULL values as <null_print>.
|
||||
|
||||
When loading in the text format from an input stream (as opposed to
|
||||
a file), recognize a "." on a line by itself as EOF. Also recognize
|
||||
a stream EOF. When unloading in the text format to an output stream,
|
||||
write a "." on a line by itself at the end of the data.
|
||||
|
||||
Iff <oids>, unload or reload the format that includes OID information.
|
||||
|
||||
Do not allow a Postgres user without superuser privilege to read from
|
||||
or write to a file.
|
||||
|
||||
Do not allow the copy if user doesn't have proper permission to access
|
||||
the class.
|
||||
----------------------------------------------------------------------------*/
|
||||
|
||||
FILE *fp;
|
||||
Relation rel;
|
||||
const AclMode required_access = from ? ACL_INSERT : ACL_SELECT;
|
||||
const AclMode required_access = (from ? ACL_INSERT : ACL_SELECT);
|
||||
int result;
|
||||
|
||||
/*
|
||||
@@ -305,10 +307,15 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
|
||||
|
||||
if (from)
|
||||
{ /* copy from file to database */
|
||||
if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
||||
elog(ERROR, "You cannot change sequence relation %s", relname);
|
||||
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "You cannot copy view %s", relname);
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
{
|
||||
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "You cannot copy view %s", relname);
|
||||
else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
||||
elog(ERROR, "You cannot change sequence relation %s", relname);
|
||||
else
|
||||
elog(ERROR, "You cannot copy object %s", relname);
|
||||
}
|
||||
if (pipe)
|
||||
{
|
||||
if (IsUnderPostmaster)
|
||||
@@ -332,8 +339,15 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
|
||||
}
|
||||
else
|
||||
{ /* copy from database to file */
|
||||
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "You cannot copy view %s", relname);
|
||||
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
||||
{
|
||||
if (rel->rd_rel->relkind == RELKIND_VIEW)
|
||||
elog(ERROR, "You cannot copy view %s", relname);
|
||||
else if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
|
||||
elog(ERROR, "You cannot copy sequence %s", relname);
|
||||
else
|
||||
elog(ERROR, "You cannot copy object %s", relname);
|
||||
}
|
||||
if (pipe)
|
||||
{
|
||||
if (IsUnderPostmaster)
|
||||
@@ -410,6 +424,10 @@ CopyTo(Relation rel, bool binary, bool oids, FILE *fp,
|
||||
int16 fld_size;
|
||||
char *string;
|
||||
|
||||
if (oids && !rel->rd_rel->relhasoids)
|
||||
elog(ERROR, "COPY: table %s does not have OIDs",
|
||||
RelationGetRelationName(rel));
|
||||
|
||||
tupDesc = rel->rd_att;
|
||||
attr_count = rel->rd_att->natts;
|
||||
attr = rel->rd_att->attrs;
|
||||
@@ -706,6 +724,10 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
|
||||
elements = NULL;
|
||||
}
|
||||
|
||||
/* Silently drop incoming OIDs if table does not have OIDs */
|
||||
if (!rel->rd_rel->relhasoids)
|
||||
oids = false;
|
||||
|
||||
values = (Datum *) palloc(attr_count * sizeof(Datum));
|
||||
nulls = (char *) palloc(attr_count * sizeof(char));
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.78 2001/06/22 21:37:14 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.79 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -34,7 +34,7 @@
|
||||
*/
|
||||
|
||||
static List *MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
List **supOids, List **supconstr);
|
||||
List **supOids, List **supconstr, bool *supHasOids);
|
||||
static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
|
||||
static void StoreCatalogInheritance(Oid relationId, List *supers);
|
||||
static int findAttrByName(const char *attributeName, List *schema);
|
||||
@@ -57,6 +57,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
TupleDesc descriptor;
|
||||
List *inheritOids;
|
||||
List *old_constraints;
|
||||
bool parentHasOids;
|
||||
List *rawDefaults;
|
||||
List *listptr;
|
||||
int i;
|
||||
@@ -73,7 +74,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
* including inherited attributes.
|
||||
*/
|
||||
schema = MergeAttributes(schema, stmt->inhRelnames, stmt->istemp,
|
||||
&inheritOids, &old_constraints);
|
||||
&inheritOids, &old_constraints, &parentHasOids);
|
||||
|
||||
numberOfAttributes = length(schema);
|
||||
if (numberOfAttributes <= 0)
|
||||
@@ -135,7 +136,9 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
}
|
||||
|
||||
relationId = heap_create_with_catalog(relname, descriptor,
|
||||
relkind, stmt->istemp,
|
||||
relkind,
|
||||
stmt->hasoids || parentHasOids,
|
||||
stmt->istemp,
|
||||
allowSystemTableMods);
|
||||
|
||||
StoreCatalogInheritance(relationId, inheritOids);
|
||||
@@ -248,6 +251,7 @@ TruncateRelation(char *name)
|
||||
* 'supOids' receives an integer list of the OIDs of the parent relations.
|
||||
* 'supconstr' receives a list of constraints belonging to the parents,
|
||||
* updated as necessary to be valid for the child.
|
||||
* 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE.
|
||||
*
|
||||
* Return value:
|
||||
* Completed schema list.
|
||||
@@ -293,12 +297,13 @@ TruncateRelation(char *name)
|
||||
*/
|
||||
static List *
|
||||
MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
List **supOids, List **supconstr)
|
||||
List **supOids, List **supconstr, bool *supHasOids)
|
||||
{
|
||||
List *entry;
|
||||
List *inhSchema = NIL;
|
||||
List *parentOids = NIL;
|
||||
List *constraints = NIL;
|
||||
bool parentHasOids = false;
|
||||
bool have_bogus_defaults = false;
|
||||
char *bogus_marker = "Bogus!"; /* marks conflicting defaults */
|
||||
int child_attno;
|
||||
@@ -341,7 +346,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
|
||||
/*
|
||||
* Scan the parents left-to-right, and merge their attributes to form
|
||||
* a list of inherited attributes (inhSchema).
|
||||
* a list of inherited attributes (inhSchema). Also check to see if
|
||||
* we need to inherit an OID column.
|
||||
*/
|
||||
child_attno = 0;
|
||||
foreach(entry, supers)
|
||||
@@ -371,6 +377,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
parentOids = lappendi(parentOids, relation->rd_id);
|
||||
setRelhassubclassInRelation(relation->rd_id, true);
|
||||
|
||||
parentHasOids |= relation->rd_rel->relhasoids;
|
||||
|
||||
tupleDesc = RelationGetDescr(relation);
|
||||
constr = tupleDesc->constr;
|
||||
|
||||
@@ -598,6 +606,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
|
||||
*supOids = parentOids;
|
||||
*supconstr = constraints;
|
||||
*supHasOids = parentHasOids;
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.77 2001/08/04 00:14:43 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.78 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -354,15 +354,15 @@ dropdb(const char *dbname)
|
||||
|
||||
heap_endscan(pgdbscan);
|
||||
|
||||
/* Delete any comments associated with the database */
|
||||
DeleteComments(db_id, RelationGetRelid(pgdbrel));
|
||||
|
||||
/*
|
||||
* Close pg_database, but keep exclusive lock till commit to ensure
|
||||
* that any new backend scanning pg_database will see the tuple dead.
|
||||
*/
|
||||
heap_close(pgdbrel, NoLock);
|
||||
|
||||
/* Delete any comments associated with the database */
|
||||
DeleteComments(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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.55 2001/08/09 18:28:17 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.56 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -212,7 +212,7 @@ DefineIndex(char *heapRelationName,
|
||||
* backends to flush their relcache entries and in particular their
|
||||
* cached lists of the indexes for this relation.
|
||||
*/
|
||||
setRelhasindex(relationId, true);
|
||||
setRelhasindex(relationId, true, primary, InvalidOid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.61 2001/06/05 19:34:56 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.62 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -85,9 +85,8 @@ RemoveOperator(char *operatorName, /* operator name */
|
||||
elog(ERROR, "RemoveOperator: operator '%s': permission denied",
|
||||
operatorName);
|
||||
|
||||
/*** Delete any comments associated with this operator ***/
|
||||
|
||||
DeleteComments(tup->t_data->t_oid);
|
||||
/* Delete any comments associated with this operator */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
@@ -146,11 +145,8 @@ SingleOpOperatorRemove(Oid typeOid)
|
||||
scan = heap_beginscan(rel, 0, SnapshotNow, 1, key);
|
||||
while (HeapTupleIsValid(tup = heap_getnext(scan, 0)))
|
||||
{
|
||||
|
||||
/*** This is apparently a routine not in use, but remove ***/
|
||||
/*** any comments anyways ***/
|
||||
|
||||
DeleteComments(tup->t_data->t_oid);
|
||||
/* Delete any comments associated with this operator */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(rel));
|
||||
|
||||
simple_heap_delete(rel, &tup->t_self);
|
||||
}
|
||||
@@ -242,7 +238,6 @@ RemoveType(char *typeName) /* type name to be removed */
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
Oid typeOid;
|
||||
char *shadow_type;
|
||||
|
||||
if (!pg_ownercheck(GetUserId(), typeName, TYPENAME))
|
||||
@@ -257,11 +252,8 @@ RemoveType(char *typeName) /* type name to be removed */
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "RemoveType: type '%s' does not exist", typeName);
|
||||
|
||||
typeOid = tup->t_data->t_oid;
|
||||
|
||||
/*** Delete any comments associated with this type ***/
|
||||
|
||||
DeleteComments(typeOid);
|
||||
/* Delete any comments associated with this type */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
@@ -347,9 +339,8 @@ RemoveFunction(char *functionName, /* function name to be removed */
|
||||
elog(NOTICE, "Removing built-in function \"%s\"", functionName);
|
||||
}
|
||||
|
||||
/*** Delete any comments associated with this function ***/
|
||||
|
||||
DeleteComments(tup->t_data->t_oid);
|
||||
/* Delete any comments associated with this function */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
@@ -421,9 +412,8 @@ RemoveAggregate(char *aggName, char *aggType)
|
||||
}
|
||||
}
|
||||
|
||||
/*** Remove any comments related to this aggregate ***/
|
||||
|
||||
DeleteComments(tup->t_data->t_oid);
|
||||
/* Remove any comments related to this aggregate */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.61 2001/06/29 21:08:24 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.62 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -161,9 +161,10 @@ DefineSequence(CreateSeqStmt *seq)
|
||||
}
|
||||
|
||||
stmt->relname = seq->seqname;
|
||||
stmt->istemp = seq->istemp;
|
||||
stmt->inhRelnames = NIL;
|
||||
stmt->constraints = NIL;
|
||||
stmt->istemp = seq->istemp;
|
||||
stmt->hasoids = false;
|
||||
|
||||
DefineRelation(stmt, RELKIND_SEQUENCE);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.94 2001/08/02 15:59:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.95 2001/08/10 18:57:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -336,10 +336,8 @@ DropTrigger(DropTrigStmt *stmt)
|
||||
|
||||
if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
|
||||
{
|
||||
|
||||
/*** Delete any comments associated with this trigger ***/
|
||||
|
||||
DeleteComments(tuple->t_data->t_oid);
|
||||
/* Delete any comments associated with this trigger */
|
||||
DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel));
|
||||
|
||||
simple_heap_delete(tgrel, &tuple->t_self);
|
||||
tgfound++;
|
||||
@@ -407,10 +405,8 @@ RelationRemoveTriggers(Relation rel)
|
||||
|
||||
while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
|
||||
{
|
||||
|
||||
/*** Delete any comments associated with this trigger ***/
|
||||
|
||||
DeleteComments(tup->t_data->t_oid);
|
||||
/* Delete any comments associated with this trigger */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel));
|
||||
|
||||
simple_heap_delete(tgrel, &tup->t_self);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.206 2001/07/18 00:46:24 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.207 2001/08/10 18:57:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -434,6 +434,12 @@ vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
|
||||
pgcform->relpages = (int32) num_pages;
|
||||
pgcform->reltuples = num_tuples;
|
||||
pgcform->relhasindex = hasindex;
|
||||
/*
|
||||
* If we have discovered that there are no indexes, then there's
|
||||
* no primary key either. This could be done more thoroughly...
|
||||
*/
|
||||
if (!hasindex)
|
||||
pgcform->relhaspkey = false;
|
||||
|
||||
/* invalidate the tuple in the cache and write the buffer */
|
||||
RelationInvalidateHeapTuple(rd, &rtup);
|
||||
@@ -904,7 +910,8 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
|
||||
/*
|
||||
* Other checks...
|
||||
*/
|
||||
if (!OidIsValid(tuple.t_data->t_oid))
|
||||
if (!OidIsValid(tuple.t_data->t_oid) &&
|
||||
onerel->rd_rel->relhasoids)
|
||||
elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.",
|
||||
relname, blkno, offnum, (int) tupgone);
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.3 2001/07/18 00:46:25 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v 1.4 2001/08/10 18:57:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -339,7 +339,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats,
|
||||
/*
|
||||
* Other checks...
|
||||
*/
|
||||
if (!OidIsValid(tuple.t_data->t_oid))
|
||||
if (!OidIsValid(tuple.t_data->t_oid) &&
|
||||
onerel->rd_rel->relhasoids)
|
||||
elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.",
|
||||
relname, blkno, offnum, (int) tupgone);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: view.c,v 1.54 2001/03/22 03:59:25 momjian Exp $
|
||||
* $Id: view.c,v 1.55 2001/08/10 18:57:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -88,10 +88,11 @@ DefineVirtualRelation(char *relname, List *tlist)
|
||||
* nil...
|
||||
*/
|
||||
createStmt->relname = relname;
|
||||
createStmt->istemp = false;
|
||||
createStmt->tableElts = attrList;
|
||||
createStmt->inhRelnames = NIL;
|
||||
createStmt->constraints = NIL;
|
||||
createStmt->istemp = false;
|
||||
createStmt->hasoids = false;
|
||||
|
||||
/*
|
||||
* finally create the relation...
|
||||
|
||||
Reference in New Issue
Block a user