Support toasting of shared system relations, and provide toast tables for

pg_database, pg_shadow, pg_group, all of which now have potentially-long
fields.  Along the way, get rid of SharedSystemRelationNames list: shared
rels are now identified in their include/pg_catalog/*.h files by a
BKI_SHARED_RELATION macro, while indexes and toast rels inherit sharedness
automatically from their parent table.  Fix some bugs with failure to detoast
pg_group.grolist during ALTER GROUP.
This commit is contained in:
Tom Lane 2002-04-27 21:24:34 +00:00
parent 108871f4fc
commit c06f6a6bc2
27 changed files with 403 additions and 531 deletions

View File

@ -1,15 +1,15 @@
%{
/*-------------------------------------------------------------------------
*
* backendparse.y
* yacc parser grammer for the "backend" initialization program.
* bootparse.y
* yacc parser grammar for the "backend" initialization program.
*
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.45 2002/04/17 20:57:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.46 2002/04/27 21:24:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -88,8 +88,9 @@ int num_columns_read = 0;
%type <list> boot_index_params
%type <ielem> boot_index_param
%type <ival> boot_const boot_ident
%type <ival> optbootstrap optwithoutoids boot_tuple boot_tuplelist
%type <ival> boot_const boot_ident
%type <ival> optbootstrap optsharedrelation optwithoutoids
%type <ival> boot_tuple boot_tuplelist
%type <oidval> optoideq
%token <ival> CONST ID
@ -97,7 +98,7 @@ int num_columns_read = 0;
%token STRING XDEFINE
%token XDECLARE INDEX ON USING XBUILD INDICES UNIQUE
%token COMMA EQUALS LPAREN RPAREN
%token OBJ_ID XBOOTSTRAP XWITHOUT_OIDS NULLVAL
%token OBJ_ID XBOOTSTRAP XSHARED_RELATION XWITHOUT_OIDS NULLVAL
%start TopLevel
%nonassoc low
@ -150,16 +151,14 @@ Boot_CloseStmt:
;
Boot_CreateStmt:
XCREATE optbootstrap optwithoutoids boot_ident LPAREN
XCREATE optbootstrap optsharedrelation optwithoutoids boot_ident LPAREN
{
do_start();
numattr = 0;
if ($2)
elog(DEBUG3, "creating bootstrap relation %s...",
LexIDStr($4));
else
elog(DEBUG3, "creating relation %s...",
LexIDStr($4));
elog(DEBUG3, "creating%s%s relation %s...",
$2 ? " bootstrap" : "",
$3 ? " shared" : "",
LexIDStr($5));
}
boot_typelist
{
@ -171,21 +170,22 @@ Boot_CreateStmt:
if ($2)
{
extern Relation reldesc;
TupleDesc tupdesc;
if (reldesc)
if (boot_reldesc)
{
elog(DEBUG3, "create bootstrap: warning, open relation exists, closing first");
closerel(NULL);
}
tupdesc = CreateTupleDesc(numattr, attrtypes);
reldesc = heap_create(LexIDStr($4),
PG_CATALOG_NAMESPACE,
tupdesc,
true, true);
reldesc->rd_rel->relhasoids = ! ($3);
boot_reldesc = heap_create(LexIDStr($5),
PG_CATALOG_NAMESPACE,
tupdesc,
$3,
true,
true);
boot_reldesc->rd_rel->relhasoids = ! ($4);
elog(DEBUG3, "bootstrap relation created");
}
else
@ -194,11 +194,12 @@ Boot_CreateStmt:
TupleDesc tupdesc;
tupdesc = CreateTupleDesc(numattr,attrtypes);
id = heap_create_with_catalog(LexIDStr($4),
id = heap_create_with_catalog(LexIDStr($5),
PG_CATALOG_NAMESPACE,
tupdesc,
RELKIND_RELATION,
! ($3),
$3,
! ($4),
true);
elog(DEBUG3, "relation created with oid %u", id);
}
@ -221,7 +222,7 @@ Boot_InsertStmt:
if (num_columns_read != numattr)
elog(ERROR, "incorrect number of columns in row (expected %d, got %d)",
numattr, num_columns_read);
if (reldesc == (Relation)NULL)
if (boot_reldesc == (Relation) NULL)
{
elog(ERROR, "relation not open");
err_out();
@ -283,6 +284,11 @@ optbootstrap:
| { $$ = 0; }
;
optsharedrelation:
XSHARED_RELATION { $$ = 1; }
| { $$ = 0; }
;
optwithoutoids:
XWITHOUT_OIDS { $$ = 1; }
| { $$ = 0; }

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootscanner.l,v 1.21 2001/08/10 18:57:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootscanner.l,v 1.22 2002/04/27 21:24:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -71,6 +71,8 @@ create { return(XCREATE); }
OID { return(OBJ_ID); }
bootstrap { return(XBOOTSTRAP); }
"shared_relation" { return(XSHARED_RELATION); }
"without_oids" { return(XWITHOUT_OIDS); }
_null_ { return(NULLVAL); }
insert { return(INSERT_TUPLE); }
@ -94,7 +96,6 @@ insert { return(INSERT_TUPLE); }
"index" { return(INDEX); }
"on" { return(ON); }
"using" { return(USING); }
"without_oids" { return(XWITHOUT_OIDS); }
{arrayid} {
yylval.ival = EnterString(MapArrayTypeName((char*)yytext));

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.126 2002/04/25 02:56:55 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.127 2002/04/27 21:24:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -59,6 +59,9 @@ static void cleanup(void);
* global variables
* ----------------
*/
Relation boot_reldesc; /* current relation descriptor */
/*
* In the lexical analyzer, we need to get the reference number quickly from
* the string, and the string from the reference number. Thus we have
@ -500,20 +503,20 @@ boot_openrel(char *relname)
heap_close(rel, NoLock);
}
if (reldesc != NULL)
if (boot_reldesc != NULL)
closerel(NULL);
elog(DEBUG3, "open relation %s, attrsize %d", relname ? relname : "(null)",
(int) ATTRIBUTE_TUPLE_SIZE);
reldesc = heap_openr(relname, NoLock);
numattr = reldesc->rd_rel->relnatts;
boot_reldesc = heap_openr(relname, NoLock);
numattr = boot_reldesc->rd_rel->relnatts;
for (i = 0; i < numattr; i++)
{
if (attrtypes[i] == NULL)
attrtypes[i] = AllocateAttribute();
memmove((char *) attrtypes[i],
(char *) reldesc->rd_att->attrs[i],
(char *) boot_reldesc->rd_att->attrs[i],
ATTRIBUTE_TUPLE_SIZE);
/* Some old pg_attribute tuples might not have attisset. */
@ -523,8 +526,9 @@ boot_openrel(char *relname)
* defined yet.
*/
if (namestrcmp(&attrtypes[i]->attname, "attisset") == 0)
attrtypes[i]->attisset = get_attisset(RelationGetRelid(reldesc),
NameStr(attrtypes[i]->attname));
attrtypes[i]->attisset =
get_attisset(RelationGetRelid(boot_reldesc),
NameStr(attrtypes[i]->attname));
else
attrtypes[i]->attisset = false;
@ -547,9 +551,9 @@ closerel(char *name)
{
if (name)
{
if (reldesc)
if (boot_reldesc)
{
if (strcmp(RelationGetRelationName(reldesc), name) != 0)
if (strcmp(RelationGetRelationName(boot_reldesc), name) != 0)
elog(ERROR, "closerel: close of '%s' when '%s' was expected",
name, relname ? relname : "(null)");
}
@ -559,13 +563,13 @@ closerel(char *name)
}
if (reldesc == NULL)
if (boot_reldesc == NULL)
elog(ERROR, "no open relation to close");
else
{
elog(DEBUG3, "close relation %s", relname ? relname : "(null)");
heap_close(reldesc, NoLock);
reldesc = (Relation) NULL;
heap_close(boot_reldesc, NoLock);
boot_reldesc = (Relation) NULL;
}
}
@ -585,7 +589,7 @@ DefineAttr(char *name, char *type, int attnum)
int attlen;
Oid typeoid;
if (reldesc != NULL)
if (boot_reldesc != NULL)
{
elog(LOG, "warning: no open relations allowed with 'create' command");
closerel(relname);
@ -674,7 +678,7 @@ InsertOneTuple(Oid objectid)
if (objectid != (Oid) 0)
tuple->t_data->t_oid = objectid;
heap_insert(reldesc, tuple);
heap_insert(boot_reldesc, tuple);
heap_freetuple(tuple);
elog(DEBUG3, "row inserted");
@ -706,13 +710,13 @@ InsertOneValue(char *value, int i)
elog(DEBUG3, "Typ != NULL");
app = Typ;
while (*app && (*app)->am_oid != reldesc->rd_att->attrs[i]->atttypid)
while (*app && (*app)->am_oid != boot_reldesc->rd_att->attrs[i]->atttypid)
++app;
ap = *app;
if (ap == NULL)
{
elog(FATAL, "unable to find atttypid %u in Typ list",
reldesc->rd_att->attrs[i]->atttypid);
boot_reldesc->rd_att->attrs[i]->atttypid);
}
values[i] = OidFunctionCall3(ap->am_typ.typinput,
CStringGetDatum(value),
@ -806,8 +810,8 @@ cleanup()
elog(FATAL, "Memory manager fault: cleanup called twice.\n");
proc_exit(1);
}
if (reldesc != (Relation) NULL)
heap_close(reldesc, NoLock);
if (boot_reldesc != (Relation) NULL)
heap_close(boot_reldesc, NoLock);
CommitTransactionCommand();
proc_exit(Warnings);
}

View File

@ -1,4 +1,4 @@
$Header: /cvsroot/pgsql/src/backend/catalog/README,v 1.6 2002/04/15 23:46:13 momjian Exp $
$Header: /cvsroot/pgsql/src/backend/catalog/README,v 1.7 2002/04/27 21:24:33 tgl Exp $
This directory contains .c files that manipulate the system catalogs;
src/include/catalog contains the .h files that define the structure
@ -69,15 +69,14 @@ manually create appropriate entries for them in the pre-loaded contents of
pg_class, pg_attribute, and pg_type. You'll also need to add code to function
heap_create() in heap.c to force the correct OID to be assigned when the table
is first referenced. (It's near the top of the function with the comment
beginning in 'Real ugly stuff'.) Avoid making new catalogs be bootstrap
beginning in "Real ugly stuff".) Avoid making new catalogs be bootstrap
catalogs if at all possible; generally, only tables that must be written to
in order to create a table should be bootstrapped.
- Certain BOOTSTRAP tables must be at the start of the Makefile
POSTGRES_BKI_SRCS variable, as these will not be created through standard
function means, but will be written directly to disk. That's how pg_class is
created without depending on functions which depend on the existence of
pg_class. The list of files this currently includes is:
POSTGRES_BKI_SRCS variable, as these will not be created through the standard
heap_create_with_catalog process, because it needs these tables to exist
already. The list of files this currently includes is:
pg_proc.h pg_type.h pg_attribute.h pg_class.h
Also, indexing.h must be last, since the indexes can't be created until all
the tables are in place. There are reputedly some other order dependencies

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.45 2002/04/12 20:38:18 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/catalog.c,v 1.46 2002/04/27 21:24:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -177,29 +177,6 @@ IsReservedName(const char *name)
name[2] == '_');
}
/*
* IsSharedSystemRelationName
* True iff name is the name of a shared system catalog relation.
*
* Note: This function assumes that this is a system relation
* in the first place. If that is not known, check the namespace
* (with IsSystemNamespace) before calling this function.
*/
bool
IsSharedSystemRelationName(const char *relname)
{
int i;
i = 0;
while (SharedSystemRelationNames[i] != NULL)
{
if (strcmp(SharedSystemRelationNames[i], relname) == 0)
return TRUE;
i++;
}
return FALSE;
}
/*
* newoid - returns a unique identifier across all catalogs.

View File

@ -10,7 +10,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/catalog/Attic/genbki.sh,v 1.26 2002/03/26 19:15:24 tgl Exp $
# $Header: /cvsroot/pgsql/src/backend/catalog/Attic/genbki.sh,v 1.27 2002/04/27 21:24:33 tgl Exp $
#
# NOTES
# non-essential whitespace is removed from the generated file.
@ -217,6 +217,7 @@ BEGIN {
inside = 0;
raw = 0;
bootstrap = "";
shared_relation = "";
without_oids = "";
nc = 0;
reln_open = 0;
@ -331,6 +332,9 @@ raw == 1 { print; next; }
if ($0 ~ /BOOTSTRAP/) {
bootstrap = "bootstrap ";
}
if ($0 ~ /BKI_SHARED_RELATION/) {
shared_relation = "shared_relation ";
}
if ($0 ~ /BKI_WITHOUT_OIDS/) {
without_oids = "without_oids ";
}
@ -358,7 +362,7 @@ inside == 1 {
# if this is the last line, then output the bki catalog stuff.
# ----
if ($1 ~ /}/) {
print "create " bootstrap without_oids catalog;
print "create " bootstrap shared_relation without_oids catalog;
print "\t(";
for (j=1; j<i-1; j++) {
@ -375,6 +379,7 @@ inside == 1 {
reln_open = 1;
inside = 0;
bootstrap = "";
shared_relation = "";
without_oids = "";
next;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.196 2002/04/12 20:38:18 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.197 2002/04/27 21:24:33 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -210,11 +210,12 @@ Relation
heap_create(const char *relname,
Oid relnamespace,
TupleDesc tupDesc,
bool shared_relation,
bool storage_create,
bool allow_system_table_mods)
{
Oid relid;
Oid dbid = MyDatabaseId;
Oid dbid = shared_relation ? InvalidOid : MyDatabaseId;
bool nailme = false;
RelFileNode rnode;
Relation rel;
@ -225,16 +226,15 @@ heap_create(const char *relname,
if (!allow_system_table_mods &&
(IsSystemNamespace(relnamespace) || IsToastNamespace(relnamespace)) &&
IsNormalProcessingMode())
elog(ERROR, "invalid relation \"%s\"; "
elog(ERROR, "cannot create %s.%s: "
"system catalog modifications are currently disallowed",
relname);
get_namespace_name(relnamespace), relname);
/*
* Real ugly stuff to assign the proper relid in the relation
* descriptor follows. Note that only "bootstrapped" relations whose
* OIDs are hard-coded in pg_class.h need be listed here. We also
* have to take special care for those rels that should be nailed
* in cache and/or are shared across databases.
* OIDs are hard-coded in pg_class.h should be listed here. We also
* have to recognize those rels that must be nailed in cache.
*/
if (IsSystemNamespace(relnamespace))
{
@ -260,24 +260,19 @@ heap_create(const char *relname,
}
else if (strcmp(ShadowRelationName, relname) == 0)
{
dbid = InvalidOid;
relid = RelOid_pg_shadow;
}
else if (strcmp(GroupRelationName, relname) == 0)
{
dbid = InvalidOid;
relid = RelOid_pg_group;
}
else if (strcmp(DatabaseRelationName, relname) == 0)
{
dbid = InvalidOid;
relid = RelOid_pg_database;
}
else
{
relid = newoid();
if (IsSharedSystemRelationName(relname))
dbid = InvalidOid;
}
}
else
@ -651,6 +646,7 @@ heap_create_with_catalog(const char *relname,
Oid relnamespace,
TupleDesc tupdesc,
char relkind,
bool shared_relation,
bool relhasoids,
bool allow_system_table_mods)
{
@ -678,8 +674,12 @@ heap_create_with_catalog(const char *relname,
* necessary anymore, but we may as well avoid the cycles of creating
* and deleting the file in case we fail.)
*/
new_rel_desc = heap_create(relname, relnamespace, tupdesc,
false, allow_system_table_mods);
new_rel_desc = heap_create(relname,
relnamespace,
tupdesc,
shared_relation,
false,
allow_system_table_mods);
/* Fetch the relation OID assigned by heap_create */
new_rel_oid = new_rel_desc->rd_att->attrs[0]->attrelid;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.176 2002/04/12 20:38:19 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.177 2002/04/27 21:24:34 tgl Exp $
*
*
* INTERFACE ROUTINES
@ -67,7 +67,6 @@ static TupleDesc BuildFuncTupleDesc(Oid funcOid,
static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
int numatts, AttrNumber *attNums,
Oid *classObjectId);
static void ConstructIndexReldesc(Relation indexRelation, Oid amoid);
static void UpdateRelationRelation(Relation indexRelation);
static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid);
@ -305,26 +304,6 @@ ConstructTupleDescriptor(Relation heapRelation,
return indexTupDesc;
}
/* ----------------------------------------------------------------
* ConstructIndexReldesc
* ----------------------------------------------------------------
*/
static void
ConstructIndexReldesc(Relation indexRelation, Oid amoid)
{
/*
* Set up some additional fields of the index' pg_class entry. In
* particular, initialize knowledge of whether the index is shared.
*/
indexRelation->rd_rel->relowner = GetUserId();
indexRelation->rd_rel->relam = amoid;
indexRelation->rd_rel->relisshared =
IsSystemNamespace(RelationGetNamespace(indexRelation)) &&
IsSharedSystemRelationName(RelationGetRelationName(indexRelation));
indexRelation->rd_rel->relkind = RELKIND_INDEX;
indexRelation->rd_rel->relhasoids = false;
}
/* ----------------------------------------------------------------
* UpdateRelationRelation
* ----------------------------------------------------------------
@ -561,6 +540,7 @@ index_create(Oid heapRelationId,
Relation heapRelation;
Relation indexRelation;
TupleDesc indexTupDesc;
bool shared_relation;
Oid namespaceId;
Oid indexoid;
@ -571,7 +551,12 @@ index_create(Oid heapRelationId,
*/
heapRelation = heap_open(heapRelationId, ShareLock);
/*
* The index will be in the same namespace as its parent table,
* and is shared across databases if and only if the parent is.
*/
namespaceId = RelationGetNamespace(heapRelation);
shared_relation = heapRelation->rd_rel->relisshared;
/*
* check parameters
@ -585,6 +570,16 @@ index_create(Oid heapRelationId,
IsNormalProcessingMode())
elog(ERROR, "User-defined indexes on system catalogs are not supported");
/*
* We cannot allow indexing a shared relation after initdb (because
* there's no way to make the entry in other databases' pg_class).
* Unfortunately we can't distinguish initdb from a manually started
* standalone backend. However, we can at least prevent this mistake
* under normal multi-user operation.
*/
if (shared_relation && IsUnderPostmaster)
elog(ERROR, "Shared indexes cannot be created after initdb");
if (get_relname_relid(indexRelationName, namespaceId))
elog(ERROR, "index named \"%s\" already exists",
indexRelationName);
@ -607,6 +602,7 @@ index_create(Oid heapRelationId,
indexRelation = heap_create(indexRelationName,
namespaceId,
indexTupDesc,
shared_relation,
false,
allow_system_table_mods);
indexoid = RelationGetRelid(indexRelation);
@ -619,16 +615,18 @@ index_create(Oid heapRelationId,
LockRelation(indexRelation, AccessExclusiveLock);
/*
* construct the index relation descriptor
* Fill in fields of the index's pg_class entry that are not set
* correctly by heap_create.
*
* XXX should have a proper way to create cataloged relations
* XXX should have a cleaner way to create cataloged indexes
*/
ConstructIndexReldesc(indexRelation, accessMethodObjectId);
indexRelation->rd_rel->relowner = GetUserId();
indexRelation->rd_rel->relam = accessMethodObjectId;
indexRelation->rd_rel->relkind = RELKIND_INDEX;
indexRelation->rd_rel->relhasoids = false;
/* ----------------
* add index to catalogs
* (append RELATION tuple)
* ----------------
/*
* store index's pg_class entry
*/
UpdateRelationRelation(indexRelation);

View File

@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.78 2002/04/15 05:22:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.79 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -152,6 +152,7 @@ copy_heap(Oid OIDOldHeap, const char *NewName)
RelationGetNamespace(OldHeap),
tupdesc,
OldHeap->rd_rel->relkind,
OldHeap->rd_rel->relisshared,
OldHeap->rd_rel->relhasoids,
allowSystemTableMods);

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.87 2002/04/21 00:26:42 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.88 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -475,11 +475,14 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
elog(ERROR, "permission denied");
MemSet(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_database_datconfig-1] = 'r';
if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
{
/* RESET ALL */
repl_null[Anum_pg_database_datconfig-1] = 'n';
repl_val[Anum_pg_database_datconfig-1] = (Datum) 0;
}
else
{
Datum datum;
@ -491,16 +494,12 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
datum = heap_getattr(tuple, Anum_pg_database_datconfig,
RelationGetDescr(rel), &isnull);
a = isnull ? ((ArrayType *) NULL) : DatumGetArrayTypeP(datum);
if (valuestr)
a = GUCArrayAdd(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable, valuestr);
a = GUCArrayAdd(a, stmt->variable, valuestr);
else
a = GUCArrayDelete(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable);
a = GUCArrayDelete(a, stmt->variable);
repl_val[Anum_pg_database_datconfig-1] = PointerGetDatum(a);
}
@ -546,8 +545,6 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
if (gottuple)
{
Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple);
text *tmptext;
bool isnull;
/* oid of the database */
if (dbIdP)
@ -573,16 +570,21 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
/* database path (as registered in pg_database) */
if (dbpath)
{
tmptext = DatumGetTextP(heap_getattr(tuple,
Anum_pg_database_datpath,
RelationGetDescr(relation),
&isnull));
Datum datum;
bool isnull;
datum = heap_getattr(tuple,
Anum_pg_database_datpath,
RelationGetDescr(relation),
&isnull);
if (!isnull)
{
Assert(VARSIZE(tmptext) - VARHDRSZ < MAXPGPATH);
text *pathtext = DatumGetTextP(datum);
int pathlen = VARSIZE(pathtext) - VARHDRSZ;
strncpy(dbpath, VARDATA(tmptext), VARSIZE(tmptext) - VARHDRSZ);
*(dbpath + VARSIZE(tmptext) - VARHDRSZ) = '\0';
Assert(pathlen >= 0 && pathlen < MAXPGPATH);
strncpy(dbpath, VARDATA(pathtext), pathlen);
*(dbpath + pathlen) = '\0';
}
else
strcpy(dbpath, "");

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.11 2002/04/27 03:45:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.12 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -200,6 +200,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
namespaceId,
descriptor,
relkind,
false,
stmt->hasoids || parentHasOids,
allowSystemTableMods);
@ -2840,6 +2841,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
HeapTuple reltup;
HeapTupleData classtuple;
TupleDesc tupdesc;
bool shared_relation;
Relation class_rel;
Buffer buffer;
Relation ridescs[Num_pg_class_indices];
@ -2856,6 +2858,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
*/
rel = heap_open(relOid, AccessExclusiveLock);
/* Check permissions */
if (rel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
RelationGetRelationName(rel));
@ -2863,6 +2866,19 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
if (!pg_class_ownercheck(relOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
/*
* Toast table is shared if and only if its parent is.
*
* We cannot allow toasting a shared relation after initdb (because
* there's no way to mark it toasted in other databases' pg_class).
* Unfortunately we can't distinguish initdb from a manually started
* standalone backend. However, we can at least prevent this mistake
* under normal multi-user operation.
*/
shared_relation = rel->rd_rel->relisshared;
if (shared_relation && IsUnderPostmaster)
elog(ERROR, "Shared relations cannot be toasted after initdb");
/*
* lock the pg_class tuple for update (is that really needed?)
*/
@ -2962,6 +2978,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
PG_TOAST_NAMESPACE,
tupdesc,
RELKIND_TOASTVALUE,
shared_relation,
false,
true);

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.98 2002/04/27 15:30:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.99 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,7 +40,10 @@
extern bool Password_encryption;
static void CheckPgUserAclNotNull(void);
static void UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple,
List *members);
static IdList *IdListToArray(List *members);
static List *IdArrayToList(IdList *oldarray);
/*
@ -151,16 +154,11 @@ write_group_file(Relation urel, Relation grel)
bool first_user = true;
datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
if (isnull)
continue; /* ignore NULL groupnames */
groname = NameStr(*DatumGetName(datum));
grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
/* Ignore NULL group lists */
/* ignore NULL groupnames --- shouldn't happen */
if (isnull)
continue;
groname = NameStr(*DatumGetName(datum));
grolist_p = DatumGetIdListP(grolist_datum);
/*
* Check for illegal characters in the group name.
*/
@ -171,8 +169,15 @@ write_group_file(Relation urel, Relation grel)
continue;
}
grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
/* Ignore NULL group lists */
if (isnull)
continue;
/* be sure the IdList is not toasted */
/* scan it */
grolist_p = DatumGetIdListP(grolist_datum);
/* scan grolist */
num = IDLIST_NUM(grolist_p);
aidp = IDLIST_DAT(grolist_p);
for (i = 0; i < num; ++i)
@ -290,8 +295,9 @@ write_user_file(Relation urel)
int i;
datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
/* ignore NULL usernames (shouldn't happen) */
if (isnull)
continue; /* ignore NULL usernames */
continue;
usename = NameStr(*DatumGetName(datum));
datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
@ -516,24 +522,19 @@ CreateUser(CreateUserStmt *stmt)
while (!user_exists && !sysid_exists &&
HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
Datum datum;
bool null;
Form_pg_shadow shadow_form = (Form_pg_shadow) GETSTRUCT(tuple);
int32 this_sysid;
datum = heap_getattr(tuple, Anum_pg_shadow_usename,
pg_shadow_dsc, &null);
Assert(!null);
user_exists = (strcmp(NameStr(*DatumGetName(datum)), stmt->user) == 0);
user_exists = (strcmp(NameStr(shadow_form->usename), stmt->user) == 0);
datum = heap_getattr(tuple, Anum_pg_shadow_usesysid,
pg_shadow_dsc, &null);
Assert(!null);
this_sysid = shadow_form->usesysid;
if (havesysid) /* customized id wanted */
sysid_exists = (DatumGetInt32(datum) == sysid);
sysid_exists = (this_sysid == sysid);
else
{
/* pick 1 + max */
if (DatumGetInt32(datum) > max_id)
max_id = DatumGetInt32(datum);
if (this_sysid > max_id)
max_id = this_sysid;
}
}
heap_endscan(scan);
@ -551,10 +552,12 @@ CreateUser(CreateUserStmt *stmt)
/*
* Build a tuple to insert
*/
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
new_record[Anum_pg_shadow_usename - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->user));
new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(sysid);
AssertState(BoolIsValid(createdb));
new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb);
new_record[Anum_pg_shadow_usetrace - 1] = BoolGetDatum(false);
@ -577,20 +580,14 @@ CreateUser(CreateUserStmt *stmt)
DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
}
}
else
new_record_nulls[Anum_pg_shadow_passwd - 1] = 'n';
if (validUntil)
new_record[Anum_pg_shadow_valuntil - 1] =
DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
new_record_nulls[Anum_pg_shadow_usesysid - 1] = ' ';
new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
new_record_nulls[Anum_pg_shadow_usetrace - 1] = ' ';
new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
new_record_nulls[Anum_pg_shadow_passwd - 1] = password ? ' ' : 'n';
new_record_nulls[Anum_pg_shadow_valuntil - 1] = validUntil ? ' ' : 'n';
else
new_record_nulls[Anum_pg_shadow_valuntil - 1] = 'n';
new_record_nulls[Anum_pg_shadow_useconfig - 1] = 'n';
@ -651,11 +648,11 @@ AlterUser(AlterUserStmt *stmt)
{
Datum new_record[Natts_pg_shadow];
char new_record_nulls[Natts_pg_shadow];
char new_record_repl[Natts_pg_shadow];
Relation pg_shadow_rel;
TupleDesc pg_shadow_dsc;
HeapTuple tuple,
new_tuple;
bool null;
List *option;
char *password = NULL; /* PostgreSQL user password */
bool encrypt_password = Password_encryption; /* encrypt password? */
@ -749,33 +746,23 @@ AlterUser(AlterUserStmt *stmt)
elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
/*
* Build a tuple to update, perusing the information just obtained
* Build an updated tuple, perusing the information just obtained
*/
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
MemSet(new_record_repl, ' ', sizeof(new_record_repl));
new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
CStringGetDatum(stmt->user));
new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
/* sysid - leave as is */
new_record[Anum_pg_shadow_usesysid - 1] = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usesysid - 1] = null ? 'n' : ' ';
new_record_repl[Anum_pg_shadow_usename - 1] = 'r';
/* createdb */
if (createdb < 0)
{
/* don't change */
new_record[Anum_pg_shadow_usecreatedb - 1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = null ? 'n' : ' ';
}
else
if (createdb >= 0)
{
new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0);
new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
new_record_repl[Anum_pg_shadow_usecreatedb - 1] = 'r';
}
/* trace - leave as is */
new_record[Anum_pg_shadow_usetrace - 1] = heap_getattr(tuple, Anum_pg_shadow_usetrace, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usetrace - 1] = null ? 'n' : ' ';
/*
* createuser (superuser) and catupd
*
@ -784,22 +771,13 @@ AlterUser(AlterUserStmt *stmt)
* with a situation where no existing superuser can alter the
* catalogs, including pg_shadow!
*/
if (createuser < 0)
{
/* don't change */
new_record[Anum_pg_shadow_usesuper - 1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usesuper - 1] = null ? 'n' : ' ';
new_record[Anum_pg_shadow_usecatupd - 1] = heap_getattr(tuple, Anum_pg_shadow_usecatupd, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_usecatupd - 1] = null ? 'n' : ' ';
}
else
if (createuser >= 0)
{
new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser > 0);
new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
new_record_repl[Anum_pg_shadow_usesuper - 1] = 'r';
new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser > 0);
new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
new_record_repl[Anum_pg_shadow_usecatupd - 1] = 'r';
}
/* password */
@ -816,14 +794,7 @@ AlterUser(AlterUserStmt *stmt)
new_record[Anum_pg_shadow_passwd - 1] =
DirectFunctionCall1(textin, CStringGetDatum(encrypted_password));
}
new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
}
else
{
/* leave as is */
new_record[Anum_pg_shadow_passwd - 1] =
heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_passwd - 1] = null ? 'n' : ' ';
new_record_repl[Anum_pg_shadow_passwd - 1] = 'r';
}
/* valid until */
@ -831,22 +802,11 @@ AlterUser(AlterUserStmt *stmt)
{
new_record[Anum_pg_shadow_valuntil - 1] =
DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
new_record_nulls[Anum_pg_shadow_valuntil - 1] = ' ';
}
else
{
/* leave as is */
new_record[Anum_pg_shadow_valuntil - 1] =
heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_valuntil - 1] = null ? 'n' : ' ';
new_record_repl[Anum_pg_shadow_valuntil - 1] = 'r';
}
/* leave useconfig as is */
new_record[Anum_pg_shadow_useconfig - 1] =
heap_getattr(tuple, Anum_pg_shadow_useconfig, pg_shadow_dsc, &null);
new_record_nulls[Anum_pg_shadow_useconfig - 1] = null ? 'n' : ' ';
new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
new_tuple = heap_modifytuple(tuple, pg_shadow_rel, new_record,
new_record_nulls, new_record_repl);
simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple);
/* Update indexes */
@ -876,7 +836,6 @@ AlterUser(AlterUserStmt *stmt)
}
/*
* ALTER USER ... SET
*/
@ -896,6 +855,10 @@ AlterUserSet(AlterUserSetStmt *stmt)
? ((A_Const *) lfirst(stmt->value))->val.val.str
: NULL);
/*
* RowExclusiveLock is sufficient, because we don't need to update
* the flat password file.
*/
rel = heap_openr(ShadowRelationName, RowExclusiveLock);
oldtuple = SearchSysCache(SHADOWNAME,
PointerGetDatum(stmt->user),
@ -925,16 +888,12 @@ AlterUserSet(AlterUserSetStmt *stmt)
datum = SysCacheGetAttr(SHADOWNAME, oldtuple,
Anum_pg_shadow_useconfig, &isnull);
array = isnull ? ((ArrayType *) NULL) : DatumGetArrayTypeP(datum);
if (valuestr)
array = GUCArrayAdd(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable, valuestr);
array = GUCArrayAdd(array, stmt->variable, valuestr);
else
array = GUCArrayDelete(isnull
? NULL
: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
stmt->variable);
array = GUCArrayDelete(array, stmt->variable);
repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(array);
}
@ -982,16 +941,14 @@ DropUser(DropUserStmt *stmt)
foreach(item, stmt->users)
{
const char *user = strVal(lfirst(item));
HeapTuple tuple,
tmp_tuple;
Relation pg_rel;
TupleDesc pg_dsc;
ScanKeyData scankey;
HeapScanDesc scan;
Datum datum;
bool null;
int32 usesysid;
const char *user = strVal(lfirst(item));
tuple = SearchSysCache(SHADOWNAME,
PointerGetDatum(user),
@ -1000,7 +957,7 @@ DropUser(DropUserStmt *stmt)
elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
(length(stmt->users) > 1) ? " (no users removed)" : "");
usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
usesysid = ((Form_pg_shadow) GETSTRUCT(tuple))->usesysid;
if (usesysid == GetUserId())
elog(ERROR, "current user cannot be dropped");
@ -1028,10 +985,7 @@ DropUser(DropUserStmt *stmt)
{
char *dbname;
datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
pg_dsc, &null);
Assert(!null);
dbname = NameStr(*DatumGetName(datum));
dbname = NameStr(((Form_pg_database) GETSTRUCT(tmp_tuple))->datname);
elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
user, dbname,
(length(stmt->users) > 1) ? " (no users removed)" : "");
@ -1066,8 +1020,7 @@ DropUser(DropUserStmt *stmt)
AlterGroupStmt ags;
/* the group name from which to try to drop the user: */
datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
ags.name = DatumGetCString(DirectFunctionCall1(nameout, datum));
ags.name = pstrdup(NameStr(((Form_pg_group) GETSTRUCT(tmp_tuple))->groname));
ags.action = -1;
ags.listUsers = makeList1(makeInteger(usesysid));
AlterGroup(&ags, "DROP USER");
@ -1202,24 +1155,19 @@ CreateGroup(CreateGroupStmt *stmt)
while (!group_exists && !sysid_exists &&
HeapTupleIsValid(tuple = heap_getnext(scan, false)))
{
Datum datum;
bool null;
Form_pg_group group_form = (Form_pg_group) GETSTRUCT(tuple);
int32 this_sysid;
datum = heap_getattr(tuple, Anum_pg_group_groname,
pg_group_dsc, &null);
Assert(!null);
group_exists = (strcmp(NameStr(*DatumGetName(datum)), stmt->name) == 0);
group_exists = (strcmp(NameStr(group_form->groname), stmt->name) == 0);
datum = heap_getattr(tuple, Anum_pg_group_grosysid,
pg_group_dsc, &null);
Assert(!null);
this_sysid = group_form->grosysid;
if (havesysid) /* customized id wanted */
sysid_exists = (DatumGetInt32(datum) == sysid);
sysid_exists = (this_sysid == sysid);
else
{
/* pick 1 + max */
if (DatumGetInt32(datum) > max_id)
max_id = DatumGetInt32(datum);
if (this_sysid > max_id)
max_id = this_sysid;
}
}
heap_endscan(scan);
@ -1231,44 +1179,30 @@ CreateGroup(CreateGroupStmt *stmt)
elog(ERROR, "CREATE GROUP: group sysid %d is already assigned",
sysid);
if (!havesysid)
sysid = max_id + 1;
/*
* Translate the given user names to ids
*/
foreach(item, userElts)
{
const char *groupuser = strVal(lfirst(item));
Value *v;
int32 userid = get_usesysid(groupuser);
v = makeInteger(get_usesysid(groupuser));
if (!member(v, newlist))
newlist = lappend(newlist, v);
if (!intMember(userid, newlist))
newlist = lappendi(newlist, userid);
}
/* build an array to insert */
if (newlist)
{
int i;
userarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
userarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
userarray->flags = 0;
ARR_NDIM(userarray) = 1; /* one dimensional array */
ARR_LBOUND(userarray)[0] = 1; /* axis starts at one */
ARR_DIMS(userarray)[0] = length(newlist); /* axis is this long */
/* fill the array */
i = 0;
foreach(item, newlist)
((int *) ARR_DATA_PTR(userarray))[i++] = intVal(lfirst(item));
}
userarray = IdListToArray(newlist);
else
userarray = NULL;
/*
* Form a tuple to insert
*/
if (!havesysid)
sysid = max_id + 1;
new_record[Anum_pg_group_groname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
new_record[Anum_pg_group_grosysid - 1] = Int32GetDatum(sysid);
@ -1318,6 +1252,11 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
Relation pg_group_rel;
TupleDesc pg_group_dsc;
HeapTuple group_tuple;
IdList *oldarray;
Datum datum;
bool null;
List *newlist,
*item;
/*
* Make sure the user can do this.
@ -1337,6 +1276,14 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
if (!HeapTupleIsValid(group_tuple))
elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
/* Fetch old group membership. */
datum = heap_getattr(group_tuple, Anum_pg_group_grolist,
pg_group_dsc, &null);
oldarray = null ? ((IdList *) NULL) : DatumGetIdListP(datum);
/* initialize list with old array contents */
newlist = IdArrayToList(oldarray);
/*
* Now decide what to do.
*/
@ -1345,49 +1292,18 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
if (stmt->action == +1) /* add users, might also be invoked by
* create user */
{
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
ArrayType *newarray,
*oldarray;
List *newlist = NIL,
*item;
HeapTuple tuple;
bool null = false;
Datum datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
int i;
oldarray = (ArrayType *) datum;
Assert(null || ARR_NDIM(oldarray) == 1);
/* first add the old array to the hitherto empty list */
if (!null)
for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
{
int index,
arrval;
Value *v;
bool valueNull;
index = i;
arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
sizeof(int), 0, &valueNull));
v = makeInteger(arrval);
/* filter out duplicates */
if (!member(v, newlist))
newlist = lappend(newlist, v);
}
/*
* now convert the to be added usernames to sysids and add them to
* convert the to be added usernames to sysids and add them to
* the list
*/
foreach(item, stmt->listUsers)
{
Value *v;
int32 sysid;
if (strcmp(tag, "ALTER GROUP") == 0)
{
/* Get the uid of the proposed user to add. */
v = makeInteger(get_usesysid(strVal(lfirst(item))));
sysid = get_usesysid(strVal(lfirst(item)));
}
else if (strcmp(tag, "CREATE USER") == 0)
{
@ -1395,16 +1311,16 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
* in this case we already know the uid and it wouldn't be
* in the cache anyway yet
*/
v = lfirst(item);
sysid = intVal(lfirst(item));
}
else
{
elog(ERROR, "AlterGroup: unknown tag %s", tag);
v = NULL; /* keep compiler quiet */
sysid = 0; /* keep compiler quiet */
}
if (!member(v, newlist))
newlist = lappend(newlist, v);
if (!intMember(sysid, newlist))
newlist = lappendi(newlist, sysid);
else
/*
* we silently assume here that this error will only come
@ -1414,147 +1330,47 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
tag, strVal(lfirst(item)), stmt->name);
}
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
newarray->flags = 0;
ARR_NDIM(newarray) = 1; /* one dimensional array */
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
/* fill the array */
i = 0;
foreach(item, newlist)
((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
/*
* Form a tuple with the new array and write it back.
*/
new_record[Anum_pg_group_groname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
new_record_nulls[Anum_pg_group_groname - 1] = ' ';
new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
/* Update indexes */
if (RelationGetForm(pg_group_rel)->relhasindex)
{
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
/* Do the update */
UpdateGroupMembership(pg_group_rel, group_tuple, newlist);
} /* endif alter group add user */
else if (stmt->action == -1) /* drop users from group */
{
Datum datum;
bool null;
bool is_dropuser = strcmp(tag, "DROP USER") == 0;
datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
if (null)
if (newlist == NIL)
{
if (!is_dropuser)
elog(WARNING, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
}
else
{
HeapTuple tuple;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
ArrayType *oldarray,
*newarray;
List *newlist = NIL,
*item;
int i;
oldarray = (ArrayType *) datum;
Assert(ARR_NDIM(oldarray) == 1);
/* first add the old array to the hitherto empty list */
for (i = ARR_LBOUND(oldarray)[0]; i < ARR_LBOUND(oldarray)[0] + ARR_DIMS(oldarray)[0]; i++)
{
int index,
arrval;
Value *v;
bool valueNull;
index = i;
arrval = DatumGetInt32(array_ref(oldarray, 1, &index, true /* by value */ ,
sizeof(int), 0, &valueNull));
v = makeInteger(arrval);
/* filter out duplicates */
if (!member(v, newlist))
newlist = lappend(newlist, v);
}
/*
* now convert the to be dropped usernames to sysids and
* convert the to be dropped usernames to sysids and
* remove them from the list
*/
foreach(item, stmt->listUsers)
{
Value *v;
int32 sysid;
if (!is_dropuser)
{
/* Get the uid of the proposed user to drop. */
v = makeInteger(get_usesysid(strVal(lfirst(item))));
sysid = get_usesysid(strVal(lfirst(item)));
}
else
{
/* for dropuser we already know the uid */
v = lfirst(item);
sysid = intVal(lfirst(item));
}
if (member(v, newlist))
newlist = LispRemove(v, newlist);
if (intMember(sysid, newlist))
newlist = lremovei(sysid, newlist);
else if (!is_dropuser)
elog(WARNING, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
}
newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
newarray->size = ARR_OVERHEAD(1) + length(newlist) * sizeof(int32);
newarray->flags = 0;
ARR_NDIM(newarray) = 1; /* one dimensional array */
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
ARR_DIMS(newarray)[0] = length(newlist); /* axis is this long */
/* fill the array */
i = 0;
foreach(item, newlist)
((int *) ARR_DATA_PTR(newarray))[i++] = intVal(lfirst(item));
/*
* Insert the new tuple with the updated user list
*/
new_record[Anum_pg_group_groname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
new_record_nulls[Anum_pg_group_groname - 1] = ' ';
new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
/* Update indexes */
if (RelationGetForm(pg_group_rel)->relhasindex)
{
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
/* Do the update */
UpdateGroupMembership(pg_group_rel, group_tuple, newlist);
} /* endif group not null */
} /* endif alter group drop user */
@ -1571,6 +1387,107 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
update_pg_pwd_and_pg_group(NULL);
}
/*
* Subroutine for AlterGroup: given a pg_group tuple and a desired new
* membership (expressed as an integer list), form and write an updated tuple.
* The pg_group relation must be open and locked already.
*/
static void
UpdateGroupMembership(Relation group_rel, HeapTuple group_tuple,
List *members)
{
IdList *newarray;
Datum new_record[Natts_pg_group];
char new_record_nulls[Natts_pg_group];
char new_record_repl[Natts_pg_group];
HeapTuple tuple;
newarray = IdListToArray(members);
/*
* Form an updated tuple with the new array and write it back.
*/
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
MemSet(new_record_repl, ' ', sizeof(new_record_repl));
new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
new_record_repl[Anum_pg_group_grolist - 1] = 'r';
tuple = heap_modifytuple(group_tuple, group_rel,
new_record, new_record_nulls, new_record_repl);
simple_heap_update(group_rel, &group_tuple->t_self, tuple);
/* Update indexes */
if (RelationGetForm(group_rel)->relhasindex)
{
Relation idescs[Num_pg_group_indices];
CatalogOpenIndices(Num_pg_group_indices,
Name_pg_group_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_group_indices, group_rel,
tuple);
CatalogCloseIndices(Num_pg_group_indices, idescs);
}
}
/*
* Convert an integer list of sysids to an array.
*/
static IdList *
IdListToArray(List *members)
{
int nmembers = length(members);
IdList *newarray;
List *item;
int i;
newarray = palloc(ARR_OVERHEAD(1) + nmembers * sizeof(int32));
newarray->size = ARR_OVERHEAD(1) + nmembers * sizeof(int32);
newarray->flags = 0;
ARR_NDIM(newarray) = 1; /* one dimensional array */
ARR_LBOUND(newarray)[0] = 1; /* axis starts at one */
ARR_DIMS(newarray)[0] = nmembers; /* axis is this long */
i = 0;
foreach(item, members)
{
((int *) ARR_DATA_PTR(newarray))[i++] = lfirsti(item);
}
return newarray;
}
/*
* Convert an array of sysids to an integer list.
*/
static List *
IdArrayToList(IdList *oldarray)
{
List *newlist = NIL;
int hibound,
i;
if (oldarray == NULL)
return NIL;
Assert(ARR_NDIM(oldarray) == 1);
hibound = ARR_DIMS(oldarray)[0];
for (i = 0; i < hibound; i++)
{
int32 sysid;
sysid = ((int *) ARR_DATA_PTR(oldarray))[i];
/* filter out any duplicates --- probably a waste of time */
if (!intMember(sysid, newlist))
newlist = lappendi(newlist, sysid);
}
return newlist;
}
/*
@ -1580,10 +1497,7 @@ void
DropGroup(DropGroupStmt *stmt)
{
Relation pg_group_rel;
HeapScanDesc scan;
HeapTuple tuple;
TupleDesc pg_group_dsc;
bool gro_exists = false;
/*
* Make sure the user can do this.
@ -1592,34 +1506,18 @@ DropGroup(DropGroupStmt *stmt)
elog(ERROR, "DROP GROUP: permission denied");
/*
* Scan the pg_group table and delete all matching groups.
* Drop the group.
*/
pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
pg_group_dsc = RelationGetDescr(pg_group_rel);
scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 0, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scan, false)))
{
Datum datum;
bool null;
datum = heap_getattr(tuple, Anum_pg_group_groname,
pg_group_dsc, &null);
if (!null && strcmp(NameStr(*DatumGetName(datum)), stmt->name) == 0)
{
gro_exists = true;
simple_heap_delete(pg_group_rel, &tuple->t_self);
}
}
heap_endscan(scan);
/*
* Did we find any?
*/
if (!gro_exists)
tuple = SearchSysCacheCopy(GRONAME,
PointerGetDatum(stmt->name),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
simple_heap_delete(pg_group_rel, &tuple->t_self);
heap_close(pg_group_rel, NoLock);
/*

View File

@ -27,7 +27,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.159 2002/04/27 03:45:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.160 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -733,6 +733,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
namespaceId,
tupdesc,
RELKIND_RELATION,
false,
true,
allowSystemTableMods);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.162 2002/04/19 16:36:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.163 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1319,7 +1319,10 @@ LookupOpclassInfo(Oid operatorClassOid,
* The relation descriptor is built just from the supplied parameters,
* without actually looking at any system table entries. We cheat
* quite a lot since we only need to work for a few basic system
* catalogs...
* catalogs.
*
* formrdesc is currently used for: pg_class, pg_attribute, pg_proc,
* and pg_type (see RelationCacheInitialize).
*
* Note that these catalogs can't have constraints, default values,
* rules, or triggers, since we don't cope with any of that.
@ -1374,9 +1377,10 @@ formrdesc(const char *relationName,
/*
* It's important to distinguish between shared and non-shared
* relations, even at bootstrap time, to make sure we know where they
* are stored.
* are stored. At present, all relations that formrdesc is used for
* are not shared.
*/
relation->rd_rel->relisshared = IsSharedSystemRelationName(relationName);
relation->rd_rel->relisshared = false;
relation->rd_rel->relpages = 1;
relation->rd_rel->reltuples = 1;
@ -1434,15 +1438,8 @@ formrdesc(const char *relationName,
/* In bootstrap mode, we have no indexes */
if (!IsBootstrapProcessingMode())
{
/*
* This list is incomplete, but it only has to work for the set of
* rels that formrdesc is used for ...
*/
if (strcmp(relationName, RelationRelationName) == 0 ||
strcmp(relationName, AttributeRelationName) == 0 ||
strcmp(relationName, ProcedureRelationName) == 0 ||
strcmp(relationName, TypeRelationName) == 0)
relation->rd_rel->relhasindex = true;
/* Otherwise, all the rels formrdesc is used for have indexes */
relation->rd_rel->relhasindex = true;
}
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.63 2002/03/02 21:39:33 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.64 2002/04/27 21:24:34 tgl Exp $
*
* NOTES
* Globals used all over the place should be declared here and not
@ -18,14 +18,6 @@
*/
#include "postgres.h"
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <math.h>
#include <unistd.h>
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "libpq/pqcomm.h"
#include "miscadmin.h"
#include "storage/backendid.h"
@ -47,14 +39,11 @@ struct Port *MyProcPort;
long MyCancelKey;
char *DataDir = NULL;
/*
* The PGDATA directory user says to use, or defaults to via environment
* variable. NULL if no option given and no environment variable set
*/
Relation reldesc; /* current relation descriptor */
char OutputFileName[MAXPGPATH];
char pg_pathname[MAXPGPATH]; /* full path to postgres
@ -85,27 +74,3 @@ bool allowSystemTableMods = false;
int SortMem = 512;
int VacuumMem = 8192;
int NBuffers = DEF_NBUFFERS;
/* ----------------
* List of relations that are shared across all databases in an installation.
*
* This used to be binary-searched, requiring that it be kept in sorted order.
* We just do a linear search now so there's no requirement that the list
* be ordered. The list is so small it shouldn't make much difference.
* make sure the list is null-terminated
* - jolly 8/19/95
* ----------------
*/
char *SharedSystemRelationNames[] = {
DatabaseRelationName,
DatabaseNameIndex,
DatabaseOidIndex,
GroupRelationName,
GroupNameIndex,
GroupSysidIndex,
ShadowRelationName,
ShadowNameIndex,
ShadowSysidIndex,
NULL
};

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.86 2002/04/04 04:25:49 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.87 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -612,9 +612,8 @@ InitializeSessionUserId(const char *username)
Anum_pg_shadow_useconfig, &isnull);
if (!isnull)
{
ArrayType *a;
ArrayType *a = DatumGetArrayTypeP(datum);
a = (ArrayType *) pg_detoast_datum((struct varlena *)datum);
ProcessGUCArray(a, PGC_S_USER);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.102 2002/04/01 03:34:26 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.103 2002/04/27 21:24:34 tgl Exp $
*
*
*-------------------------------------------------------------------------
@ -138,7 +138,7 @@ ReverifyMyDatabase(const char *name)
#endif
/*
* Set up datbase-specific configuration variables.
* Set up database-specific configuration variables.
*/
if (IsUnderPostmaster)
{
@ -149,9 +149,8 @@ ReverifyMyDatabase(const char *name)
RelationGetDescr(pgdbrel), &isnull);
if (!isnull)
{
ArrayType *a;
ArrayType *a = DatumGetArrayTypeP(datum);
a = (ArrayType *) pg_detoast_datum((struct varlena *)datum);
ProcessGUCArray(a, PGC_S_DATABASE);
}
}

View File

@ -27,7 +27,7 @@
# Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.151 2002/04/21 00:26:43 tgl Exp $
# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.152 2002/04/27 21:24:34 tgl Exp $
#
#-------------------------------------------------------------------------
@ -658,10 +658,13 @@ $ECHO_N "enabling unlimited row size for system tables... "$ECHO_C
"$PGPATH"/postgres $PGSQL_OPT template1 >/dev/null <<EOF
ALTER TABLE pg_attrdef CREATE TOAST TABLE;
ALTER TABLE pg_database CREATE TOAST TABLE;
ALTER TABLE pg_description CREATE TOAST TABLE;
ALTER TABLE pg_group CREATE TOAST TABLE;
ALTER TABLE pg_proc CREATE TOAST TABLE;
ALTER TABLE pg_relcheck CREATE TOAST TABLE;
ALTER TABLE pg_rewrite CREATE TOAST TABLE;
ALTER TABLE pg_shadow CREATE TOAST TABLE;
ALTER TABLE pg_statistic CREATE TOAST TABLE;
EOF
if [ "$?" -ne 0 ]; then

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: bootstrap.h,v 1.28 2002/03/26 19:16:20 tgl Exp $
* $Id: bootstrap.h,v 1.29 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,7 +31,7 @@ typedef struct hashnode
} hashnode;
extern Relation reldesc;
extern Relation boot_reldesc;
extern Form_pg_attribute attrtypes[MAXATTR];
extern int numattr;
extern int BootstrapMain(int ac, char *av[]);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catalog.h,v 1.23 2002/04/12 20:38:30 tgl Exp $
* $Id: catalog.h,v 1.24 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,7 +30,6 @@ extern bool IsSystemNamespace(Oid namespaceId);
extern bool IsToastNamespace(Oid namespaceId);
extern bool IsReservedName(const char *name);
extern bool IsSharedSystemRelationName(const char *relname);
extern Oid newoid(void);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catname.h,v 1.24 2002/03/22 21:34:44 tgl Exp $
* $Id: catname.h,v 1.25 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,6 +42,4 @@
#define RelCheckRelationName "pg_relcheck"
#define TriggerRelationName "pg_trigger"
extern char *SharedSystemRelationNames[];
#endif /* CATNAME_H */

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.127 2002/04/26 01:24:08 tgl Exp $
* $Id: catversion.h,v 1.128 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200204251
#define CATALOG_VERSION_NO 200204271
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: heap.h,v 1.49 2002/03/31 06:26:32 tgl Exp $
* $Id: heap.h,v 1.50 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,6 +30,7 @@ typedef struct RawColumnDefault
extern Relation heap_create(const char *relname,
Oid relnamespace,
TupleDesc tupDesc,
bool shared_relation,
bool storage_create,
bool allow_system_table_mods);
@ -39,6 +40,7 @@ extern Oid heap_create_with_catalog(const char *relname,
Oid relnamespace,
TupleDesc tupdesc,
char relkind,
bool shared_relation,
bool relhasoids,
bool allow_system_table_mods);

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_database.h,v 1.23 2002/04/21 00:26:43 tgl Exp $
* $Id: pg_database.h,v 1.24 2002/04/27 21:24:34 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -31,7 +31,7 @@
* typedef struct FormData_pg_database
* ----------------
*/
CATALOG(pg_database) BOOTSTRAP
CATALOG(pg_database) BOOTSTRAP BKI_SHARED_RELATION
{
NameData datname; /* database name */
int4 datdba; /* sysid of owner */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_group.h,v 1.13 2001/11/05 17:46:32 momjian Exp $
* $Id: pg_group.h,v 1.14 2002/04/27 21:24:34 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -25,7 +25,7 @@
* ----------------
*/
CATALOG(pg_group) BOOTSTRAP BKI_WITHOUT_OIDS
CATALOG(pg_group) BOOTSTRAP BKI_SHARED_RELATION BKI_WITHOUT_OIDS
{
NameData groname;
int4 grosysid;

View File

@ -9,7 +9,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_shadow.h,v 1.19 2002/04/11 05:32:03 petere Exp $
* $Id: pg_shadow.h,v 1.20 2002/04/27 21:24:34 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -29,7 +29,7 @@
* typedef struct FormData_pg_shadow
* ----------------
*/
CATALOG(pg_shadow) BOOTSTRAP BKI_WITHOUT_OIDS
CATALOG(pg_shadow) BOOTSTRAP BKI_SHARED_RELATION BKI_WITHOUT_OIDS
{
NameData usename;
int4 usesysid;
@ -37,8 +37,9 @@ CATALOG(pg_shadow) BOOTSTRAP BKI_WITHOUT_OIDS
bool usetrace;
bool usesuper; /* read this field via superuser() only */
bool usecatupd;
/* remaining fields may be null; use heap_getattr to read them! */
text passwd;
int4 valuntil;
int4 valuntil; /* actually abstime */
text useconfig[1];
} FormData_pg_shadow;

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1995, Regents of the University of California
*
* $Id: postgres.h,v 1.56 2001/11/05 17:46:31 momjian Exp $
* $Id: postgres.h,v 1.57 2002/04/27 21:24:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -597,7 +597,7 @@ extern int assertTest(int val);
#define CATALOG(x) typedef struct CppConcat(FormData_,x)
#define BOOTSTRAP
#define BKI_SHARED_RELATION
#define BKI_WITHOUT_OIDS
/* these need to expand into some harmless, repeatable declaration */