Re: fix schema ownership on first connection preliminary patch - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: fix schema ownership on first connection preliminary patch |
Date | |
Msg-id | 200407192138.i6JLcHb06479@candle.pha.pa.us Whole thread Raw |
In response to | fix schema ownership on first connection preliminary patch v2 (Bruce Momjian <pgman@candle.pha.pa.us>) |
List | pgsql-patches |
Tom is reviewing this. --------------------------------------------------------------------------- Bruce Momjian wrote: > > I have added the v2 version of this patch to the patch queue (attached). > I agree with Tom that there is no need for regression tests for this > feature and have removed that part of the patch. > > http://momjian.postgresql.org/cgi-bin/pgpatches > > I will try to apply it within the next 48 hours. > > --------------------------------------------------------------------------- > > Fabien COELHO wrote: > > > > Dear hackers, > > > > Please find attached a preliminary patch to fix schema ownership on first > > connection. It is for comments and advices as I still have doubts about > > various how-and-where issues, thus it is not submitted to the patch list. > > > > (1) It adds a new "datisinit" attribute to pg_database, which tells > > whether the database initialization was performed or not. > > The documentation is updated accordingly. > > > > (2) This boolean is tested in postinit.c:ReverifyMyDatabase, > > and InitializeDatabase is called if necessary. > > > > (3) The routine updates pg_database datisinit, as well as pg_namespace > > ownership and acl stuff. The acl item update part is not yet > > appropriate: I'd like some answers before going on. > > > > (4) Some validation is added. It passes for me. > > > > > > My questions: > > > > - is the place for the first connection housekeeping updates appropriate? > > it seems so from ReverifyMyDatabase comments, but these are only comments. > > > > - it is quite convenient to use SPI_* stuff for this very rare updates, > > but I'm not that confident about the issue... On the other hand, the > > all-C solution would result in a much longer and less obvious code. > > > > - it is unclear to me whether it should be allowed to skip this under > > some condition, when the database is accessed in "debug" mode from > > a standalone postgres for instance? > > > > - also I'm wondering how to react (what to do and how to do it) on > > errors within or under these initialization. For instance, how to > > be sure that the CurrentUser is reset as expected? > > > > Thanks in advance for your answers and comments. > > > > Have a nice day. > > > > -- > > Fabien Coelho - coelho@cri.ensmp.fr > > Content-Description: > > [ Attachment, skipping... ] > > > > > ---------------------------(end of broadcast)--------------------------- > > TIP 7: don't forget to increase your free space map settings > > -- > Bruce Momjian | http://candle.pha.pa.us > pgman@candle.pha.pa.us | (610) 359-1001 > + If your life is a hard drive, | 13 Roberts Road > + Christ can be your backup. | Newtown Square, Pennsylvania 19073 > Dear hackers, > > Please find attached a patch to fix schema ownership on first > connection, so that non system schemas reflect the database owner. > > > This revised patch includes fixes after Tom comments on names used in > the validation. However, the validation is still there. It is easy to > edit it out if required, but I still think that such a test is a good thing. > > > (1) It adds a new "datisinit" attribute to pg_database, which tells > whether the database initialization was performed or not. > The documentation is updated accordingly. > > > (2) The datisnit boolean is tested in postinit.c:ReverifyMyDatabase, > and InitializeDatabase is called if necessary. > > > (3) The routine updates pg_database datisinit, as well as pg_namespace > ownership and acl stuff. > > > I think that the race condition if two backends connect for > the first time to the same database is correctly taken care of, > as only one backend will update the datisinit flag and then will fix the > schema ownership. > > > (4) Some validation is added. > > > Some questions: > > - is the place for the first connection housekeeping updates appropriate? > it seems so from ReverifyMyDatabase comments, but these are only comments. > > > - it is quite convenient to use SPI_* stuff for this very rare updates, > but I'm not that confident about the issue... On the other hand, the > all-C solution would result in a much longer and less obvious code:-( > > > - it is unclear to me whether it should be allowed to skip this under > some condition, when the database is accessed in "debug" mode from > a standalone postgres for instance? > > > - also I'm wondering how to react (what to do and how to do it) on > errors within or under these initialization. For instance, how to > be sure that the CurrentUser is reset as expected? > > > Thanks in advance for any comments. > > Have a nice day. > > -- > Fabien. > > *** ./doc/src/sgml/catalogs.sgml.orig Mon Jun 7 09:08:11 2004 > --- ./doc/src/sgml/catalogs.sgml Wed Jun 9 10:26:39 2004 > *************** > *** 1536,1541 **** > --- 1536,1552 ---- > </row> > > <row> > + <entry><structfield>datisinit</structfield></entry> > + <entry><type>bool</type></entry> > + <entry></entry> > + <entry> > + False when a database is just created but housekeeping initialization > + tasks are not performed yet. On the first connection, the initialization > + is performed and the boolean is turned to true. > + </entry> > + </row> > + > + <row> > <entry><structfield>datlastsysoid</structfield></entry> > <entry><type>oid</type></entry> > <entry></entry> > *** ./src/backend/commands/dbcommands.c.orig Wed May 26 17:28:40 2004 > --- ./src/backend/commands/dbcommands.c Wed Jun 9 10:26:39 2004 > *************** > *** 424,429 **** > --- 424,430 ---- > new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding); > new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false); > new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true); > + new_record[Anum_pg_database_datisinit - 1] = BoolGetDatum(false); > new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid); > new_record[Anum_pg_database_datvacuumxid - 1] = TransactionIdGetDatum(src_vacuumxid); > new_record[Anum_pg_database_datfrozenxid - 1] = TransactionIdGetDatum(src_frozenxid); > *** ./src/backend/utils/adt/acl.c.orig Thu Jun 3 15:05:57 2004 > --- ./src/backend/utils/adt/acl.c Wed Jun 9 10:26:39 2004 > *************** > *** 2203,2205 **** > --- 2203,2225 ---- > errmsg("unrecognized privilege type: \"%s\"", priv_type))); > return ACL_NO_RIGHTS; /* keep compiler quiet */ > } > + > + /* acl acl_switch_grantor(acl, oldgrantor, newgrantor); > + * switch grantor id in aclitem array. > + * used internally for fixing owner rights in new databases. > + * must be STRICT. > + */ > + Datum acl_switch_grantor(PG_FUNCTION_ARGS) > + { > + Acl * acls = PG_GETARG_ACL_P_COPY(0); > + int i, > + old_grantor = PG_GETARG_INT32(1), > + new_grantor = PG_GETARG_INT32(2); > + AclItem * item; > + > + for (i=0, item=ACL_DAT(acls); i<ACL_NUM(acls); i++, item++) > + if (item->ai_grantor == old_grantor) > + item->ai_grantor = new_grantor; > + > + PG_RETURN_ACL_P(acls); > + } > *** ./src/backend/utils/init/postinit.c.orig Tue Jun 1 10:21:23 2004 > --- ./src/backend/utils/init/postinit.c Wed Jun 9 11:52:02 2004 > *************** > *** 50,55 **** > --- 50,110 ---- > > /*** InitPostgres support ***/ > > + #include "executor/spi.h" > + > + /* Do housekeeping initializations if required, on first connection. > + * This function is expected to be called from within a transaction block. > + */ > + static void InitializeDatabase(const char * dbname) > + { > + /* su */ > + AclId saved_user = GetUserId(); > + SetUserId(1); > + > + /* Querying in C is nice, but SQL is nicer. > + * This is only done once in a lifetime of the database, > + * so paying for the parser/optimiser cost is not that bad? > + * What if that fails? > + */ > + SetQuerySnapshot(); > + > + if (SPI_connect() != SPI_OK_CONNECT) > + ereport(ERROR, (errmsg("SPI_connect failed"))); > + > + if (SPI_exec("UPDATE " SystemCatalogName "." DatabaseRelationName > + " SET datisinit=TRUE" > + " WHERE datname=CURRENT_DATABASE()" > + " AND datisinit=FALSE" , 0) != SPI_OK_UPDATE) > + ereport(ERROR, (errmsg("database initialization %s update failed", > + DatabaseRelationName))); > + > + if (SPI_processed==1) > + { > + /* ok, we have it! */ > + > + if (SPI_exec("UPDATE " SystemCatalogName "." NamespaceRelationName > + " SET nspowner=datdba," > + " nspacl = acl_switch_grantor(nspacl, 1, datdba)" > + " FROM " SystemCatalogName "." DatabaseRelationName " " > + " WHERE nspname NOT LIKE" > + " ALL(ARRAY['pg_%','information_schema'])" > + " AND datname=CURRENT_DATABASE()" > + " AND nspowner!=datdba;", 0) != SPI_OK_UPDATE) > + ereport(ERROR, (errmsg("database initialization %s update failed", > + NamespaceRelationName))); > + > + if (SPI_processed>0) > + ereport(LOG, /* don't bother the user about these details... */ > + (errmsg("database initialization schema owner updates: %d", > + SPI_processed))); > + } > + /* some other concurrent connection did it, let us proceed. */ > + > + if (SPI_finish() != SPI_OK_FINISH) > + ereport(ERROR, (errmsg("SPI_finish failed"))); > + > + SetUserId(saved_user); > + } > > /* -------------------------------- > * ReverifyMyDatabase > *************** > *** 130,135 **** > --- 185,196 ---- > errmsg("database \"%s\" is not currently accepting connections", > name))); > > + /* Do we need the housekeeping initialization of the database? > + * could be skipped on standalone "panic" mode? > + */ > + if (!dbform->datisinit) > + InitializeDatabase(name); > + > /* > * OK, we're golden. Only other to-do item is to save the encoding > * info out of the pg_database tuple. > *** ./src/include/catalog/catname.h.orig Sat Nov 29 23:40:58 2003 > --- ./src/include/catalog/catname.h Wed Jun 9 10:26:39 2004 > *************** > *** 14,19 **** > --- 14,20 ---- > #ifndef CATNAME_H > #define CATNAME_H > > + #define SystemCatalogName "pg_catalog" > > #define AggregateRelationName "pg_aggregate" > #define AccessMethodRelationName "pg_am" > *** ./src/include/catalog/catversion.h.orig Mon Jun 7 09:08:19 2004 > --- ./src/include/catalog/catversion.h Wed Jun 9 10:26:39 2004 > *************** > *** 53,58 **** > */ > > /* yyyymmddN */ > ! #define CATALOG_VERSION_NO 200406061 > > #endif > --- 53,58 ---- > */ > > /* yyyymmddN */ > ! #define CATALOG_VERSION_NO 200406081 > > #endif > *** ./src/include/catalog/pg_attribute.h.orig Mon Apr 5 12:06:43 2004 > --- ./src/include/catalog/pg_attribute.h Wed Jun 9 10:26:39 2004 > *************** > *** 287,299 **** > DATA(insert ( 1262 encoding 23 -1 4 3 0 -1 -1 t p i t f f t 0)); > DATA(insert ( 1262 datistemplate 16 -1 1 4 0 -1 -1 t p c t f f t 0)); > DATA(insert ( 1262 datallowconn 16 -1 1 5 0 -1 -1 t p c t f f t 0)); > ! DATA(insert ( 1262 datlastsysoid 26 -1 4 6 0 -1 -1 t p i t f f t 0)); > ! DATA(insert ( 1262 datvacuumxid 28 -1 4 7 0 -1 -1 t p i t f f t 0)); > ! DATA(insert ( 1262 datfrozenxid 28 -1 4 8 0 -1 -1 t p i t f f t 0)); > /* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */ > ! DATA(insert ( 1262 datpath 25 -1 -1 9 0 -1 -1 f p i t f f t 0)); > ! DATA(insert ( 1262 datconfig 1009 -1 -1 10 1 -1 -1 f x i f f f t 0)); > ! DATA(insert ( 1262 datacl 1034 -1 -1 11 1 -1 -1 f x i f f f t 0)); > DATA(insert ( 1262 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0)); > DATA(insert ( 1262 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0)); > DATA(insert ( 1262 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0)); > --- 287,300 ---- > DATA(insert ( 1262 encoding 23 -1 4 3 0 -1 -1 t p i t f f t 0)); > DATA(insert ( 1262 datistemplate 16 -1 1 4 0 -1 -1 t p c t f f t 0)); > DATA(insert ( 1262 datallowconn 16 -1 1 5 0 -1 -1 t p c t f f t 0)); > ! DATA(insert ( 1262 datisinit 16 -1 1 6 0 -1 -1 t p c t f f t 0)); > ! DATA(insert ( 1262 datlastsysoid 26 -1 4 7 0 -1 -1 t p i t f f t 0)); > ! DATA(insert ( 1262 datvacuumxid 28 -1 4 8 0 -1 -1 t p i t f f t 0)); > ! DATA(insert ( 1262 datfrozenxid 28 -1 4 9 0 -1 -1 t p i t f f t 0)); > /* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */ > ! DATA(insert ( 1262 datpath 25 -1 -1 10 0 -1 -1 f p i t f f t 0)); > ! DATA(insert ( 1262 datconfig 1009 -1 -1 11 1 -1 -1 f x i f f f t 0)); > ! DATA(insert ( 1262 datacl 1034 -1 -1 12 1 -1 -1 f x i f f f t 0)); > DATA(insert ( 1262 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0)); > DATA(insert ( 1262 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0)); > DATA(insert ( 1262 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0)); > *** ./src/include/catalog/pg_class.h.orig Mon Apr 5 12:06:43 2004 > --- ./src/include/catalog/pg_class.h Wed Jun 9 10:26:39 2004 > *************** > *** 146,152 **** > DESCR(""); > DATA(insert OID = 1261 ( pg_group PGNSP 87 PGUID 0 1261 0 0 0 0 f t r 3 0 0 0 0 0 f f f f _null_ )); > DESCR(""); > ! DATA(insert OID = 1262 ( pg_database PGNSP 88 PGUID 0 1262 0 0 0 0 f t r 11 0 0 0 0 0 t f f f _null_ )); > DESCR(""); > DATA(insert OID = 376 ( pg_xactlock PGNSP 0 PGUID 0 0 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ )); > DESCR(""); > --- 146,152 ---- > DESCR(""); > DATA(insert OID = 1261 ( pg_group PGNSP 87 PGUID 0 1261 0 0 0 0 f t r 3 0 0 0 0 0 f f f f _null_ )); > DESCR(""); > ! DATA(insert OID = 1262 ( pg_database PGNSP 88 PGUID 0 1262 0 0 0 0 f t r 12 0 0 0 0 0 t f f f _null_ )); > DESCR(""); > DATA(insert OID = 376 ( pg_xactlock PGNSP 0 PGUID 0 0 0 0 0 0 f t s 1 0 0 0 0 0 f f f f _null_ )); > DESCR(""); > *** ./src/include/catalog/pg_database.h.orig Tue Feb 10 02:55:26 2004 > --- ./src/include/catalog/pg_database.h Wed Jun 9 10:26:39 2004 > *************** > *** 38,43 **** > --- 38,44 ---- > int4 encoding; /* character encoding */ > bool datistemplate; /* allowed as CREATE DATABASE template? */ > bool datallowconn; /* new connections allowed? */ > + bool datisinit; /* was it already initialized? */ > Oid datlastsysoid; /* highest OID to consider a system OID */ > TransactionId datvacuumxid; /* all XIDs before this are vacuumed */ > TransactionId datfrozenxid; /* all XIDs before this are frozen */ > *************** > *** 57,76 **** > * compiler constants for pg_database > * ---------------- > */ > ! #define Natts_pg_database 11 > #define Anum_pg_database_datname 1 > #define Anum_pg_database_datdba 2 > #define Anum_pg_database_encoding 3 > #define Anum_pg_database_datistemplate 4 > #define Anum_pg_database_datallowconn 5 > ! #define Anum_pg_database_datlastsysoid 6 > ! #define Anum_pg_database_datvacuumxid 7 > ! #define Anum_pg_database_datfrozenxid 8 > ! #define Anum_pg_database_datpath 9 > ! #define Anum_pg_database_datconfig 10 > ! #define Anum_pg_database_datacl 11 > > ! DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 0 0 "" _null_ _null_ )); > DESCR("Default template database"); > #define TemplateDbOid 1 > > --- 58,78 ---- > * compiler constants for pg_database > * ---------------- > */ > ! #define Natts_pg_database 12 > #define Anum_pg_database_datname 1 > #define Anum_pg_database_datdba 2 > #define Anum_pg_database_encoding 3 > #define Anum_pg_database_datistemplate 4 > #define Anum_pg_database_datallowconn 5 > ! #define Anum_pg_database_datisinit 6 > ! #define Anum_pg_database_datlastsysoid 7 > ! #define Anum_pg_database_datvacuumxid 8 > ! #define Anum_pg_database_datfrozenxid 9 > ! #define Anum_pg_database_datpath 10 > ! #define Anum_pg_database_datconfig 11 > ! #define Anum_pg_database_datacl 12 > > ! DATA(insert OID = 1 ( template1 PGUID ENCODING t t t 0 0 0 "" _null_ _null_ )); > DESCR("Default template database"); > #define TemplateDbOid 1 > > *** ./src/include/catalog/pg_proc.h.orig Mon Jun 7 09:08:20 2004 > --- ./src/include/catalog/pg_proc.h Wed Jun 9 10:26:39 2004 > *************** > *** 3588,3593 **** > --- 3588,3596 ---- > DATA(insert OID = 2243 ( bit_or PGNSP PGUID 12 t f f f i 1 1560 "1560" _null_ aggregate_dummy- _null_)); > DESCR("bitwise-or bit aggregate"); > > + DATA(insert OID = 2245 ( acl_switch_grantor PGNSP PGUID 12 f f t f i 3 1034 "1034 23 23" _null_ acl_switch_grantor- _null_)); > + DESCR("internal function to update grantors in acls"); > + > /* > * Symbolic values for provolatile column: these indicate whether the result > * of a function is dependent *only* on the values of its explicit arguments, > *** ./src/include/utils/acl.h.orig Thu Jun 3 15:05:58 2004 > --- ./src/include/utils/acl.h Wed Jun 9 10:26:39 2004 > *************** > *** 236,241 **** > --- 236,242 ---- > extern Datum makeaclitem(PG_FUNCTION_ARGS); > extern Datum aclitem_eq(PG_FUNCTION_ARGS); > extern Datum hash_aclitem(PG_FUNCTION_ARGS); > + extern Datum acl_switch_grantor(PG_FUNCTION_ARGS); > > /* > * prototypes for functions in aclchk.c > > ---------------------------(end of broadcast)--------------------------- > TIP 6: Have you searched our list archives? > > http://archives.postgresql.org -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
pgsql-patches by date: