/*-------------------------------------------------------------------------
*
* postinit . c
* postgres initialization utilities
*
* 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 / utils / init / postinit . c , v 1.101 2002 / 03 / 31 06 : 26 : 32 tgl Exp $
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include "postgres.h"
# include <fcntl.h>
# include <sys/file.h>
# include <sys/types.h>
# include <math.h>
# include <unistd.h>
# include "catalog/catalog.h"
# include "access/heapam.h"
# include "catalog/catname.h"
# include "catalog/pg_database.h"
# include "catalog/pg_shadow.h"
# include "commands/trigger.h"
# include "commands/variable.h" /* for set_default_client_encoding() */
# include "mb/pg_wchar.h"
# include "miscadmin.h"
# include "storage/backendid.h"
# include "storage/proc.h"
# include "storage/sinval.h"
# include "storage/smgr.h"
# include "utils/fmgroids.h"
# include "utils/guc.h"
# include "utils/portal.h"
# include "utils/relcache.h"
# include "utils/syscache.h"
static void ReverifyMyDatabase ( const char * name ) ;
static void InitCommunication ( void ) ;
static void ShutdownPostgres ( void ) ;
static bool ThereIsAtLeastOneUser ( void ) ;
/*** InitPostgres support ***/
/* --------------------------------
* ReverifyMyDatabase
*
* Since we are forced to fetch the database OID out of pg_database without
* benefit of locking or transaction ID checking ( see utils / misc / database . c ) ,
* we might have gotten a wrong answer . Or , we might have attached to a
* database that ' s in process of being destroyed by destroydb ( ) . This
* routine is called after we have all the locking and other infrastructure
* running - - - now we can check that we are really attached to a valid
* database .
*
* In reality , if destroydb ( ) is running in parallel with our startup ,
* it ' s pretty likely that we will have failed before now , due to being
* unable to read some of the system tables within the doomed database .
* This routine just exists to make * sure * we have not started up in an
* invalid database . If we quit now , we should have managed to avoid
* creating any serious problems .
*
* This is also a handy place to fetch the database encoding info out
* of pg_database , if we are in MULTIBYTE mode .
*
* To avoid having to read pg_database more times than necessary
* during session startup , this place is also fitting to set up any
* database - specific configuration variables .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static void
ReverifyMyDatabase ( const char * name )
{
Relation pgdbrel ;
HeapScanDesc pgdbscan ;
ScanKeyData key ;
HeapTuple tup ;
Form_pg_database dbform ;
/*
* Because we grab AccessShareLock here , we can be sure that destroydb
* is not running in parallel with us ( any more ) .
*/
pgdbrel = heap_openr ( DatabaseRelationName , AccessShareLock ) ;
ScanKeyEntryInitialize ( & key , 0 , Anum_pg_database_datname ,
F_NAMEEQ , NameGetDatum ( name ) ) ;
pgdbscan = heap_beginscan ( pgdbrel , 0 , SnapshotNow , 1 , & key ) ;
tup = heap_getnext ( pgdbscan , 0 ) ;
if ( ! HeapTupleIsValid ( tup ) | |
tup - > t_data - > t_oid ! = MyDatabaseId )
{
/* OOPS */
heap_close ( pgdbrel , AccessShareLock ) ;
/*
* The only real problem I could have created is to load dirty
* buffers for the dead database into shared buffer cache ; if I
* did , some other backend will eventually try to write them and
* die in mdblindwrt . Flush any such pages to forestall trouble .
*/
DropBuffers ( MyDatabaseId ) ;
/* Now I can commit hara-kiri with a clear conscience... */
elog ( FATAL , " Database \" %s \" , OID %u, has disappeared from pg_database " ,
name , MyDatabaseId ) ;
}
/*
* Also check that the database is currently allowing connections .
*/
dbform = ( Form_pg_database ) GETSTRUCT ( tup ) ;
if ( ! dbform - > datallowconn )
elog ( FATAL , " Database \" %s \" is not currently accepting connections " ,
name ) ;
/*
* OK , we ' re golden . Only other to - do item is to save the MULTIBYTE
* encoding info out of the pg_database tuple - - - or complain , if we
* can ' t support it .
*/
# ifdef MULTIBYTE
SetDatabaseEncoding ( dbform - > encoding ) ;
# else
Commit Karel's patch.
-------------------------------------------------------------------
Subject: Re: [PATCHES] encoding names
From: Karel Zak <zakkr@zf.jcu.cz>
To: Peter Eisentraut <peter_e@gmx.net>
Cc: pgsql-patches <pgsql-patches@postgresql.org>
Date: Fri, 31 Aug 2001 17:24:38 +0200
On Thu, Aug 30, 2001 at 01:30:40AM +0200, Peter Eisentraut wrote:
> > - convert encoding 'name' to 'id'
>
> I thought we decided not to add functions returning "new" names until we
> know exactly what the new names should be, and pending schema
Ok, the patch not to add functions.
> better
>
> ...(): encoding name too long
Fixed.
I found new bug in command/variable.c in parse_client_encoding(), nobody
probably never see this error:
if (pg_set_client_encoding(encoding))
{
elog(ERROR, "Conversion between %s and %s is not supported",
value, GetDatabaseEncodingName());
}
because pg_set_client_encoding() returns -1 for error and 0 as true.
It's fixed too.
IMHO it can be apply.
Karel
PS:
* following files are renamed:
src/utils/mb/Unicode/KOI8_to_utf8.map -->
src/utils/mb/Unicode/koi8r_to_utf8.map
src/utils/mb/Unicode/WIN_to_utf8.map -->
src/utils/mb/Unicode/win1251_to_utf8.map
src/utils/mb/Unicode/utf8_to_KOI8.map -->
src/utils/mb/Unicode/utf8_to_koi8r.map
src/utils/mb/Unicode/utf8_to_WIN.map -->
src/utils/mb/Unicode/utf8_to_win1251.map
* new file:
src/utils/mb/encname.c
* removed file:
src/utils/mb/common.c
--
Karel Zak <zakkr@zf.jcu.cz>
http://home.zf.jcu.cz/~zakkr/
C, PostgreSQL, PHP, WWW, http://docs.linux.cz, http://mape.jcu.cz
24 years ago
if ( dbform - > encoding ! = PG_SQL_ASCII )
elog ( FATAL , " database was initialized with MULTIBYTE encoding %d, \n \t but the backend was compiled without multibyte support. \n \t looks like you need to initdb or recompile. " ,
dbform - > encoding ) ;
# endif
/*
* Set up datbase - specific configuration variables .
*/
if ( IsUnderPostmaster )
{
Datum datum ;
bool isnull ;
datum = heap_getattr ( tup , Anum_pg_database_datconfig ,
RelationGetDescr ( pgdbrel ) , & isnull ) ;
if ( ! isnull )
{
ArrayType * a ;
a = ( ArrayType * ) pg_detoast_datum ( ( struct varlena * ) datum ) ;
ProcessGUCArray ( a , PGC_S_DATABASE ) ;
}
}
heap_endscan ( pgdbscan ) ;
heap_close ( pgdbrel , AccessShareLock ) ;
}
/* --------------------------------
* InitCommunication
*
* This routine initializes stuff needed for ipc , locking , etc .
* it should be called something more informative .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static void
InitCommunication ( void )
{
/*
* initialize shared memory and semaphores appropriately .
*/
if ( ! IsUnderPostmaster ) /* postmaster already did this */
{
/*
* we ' re running a postgres backend by itself with no front end or
* postmaster . Create private " shmem " and semaphores . Setting
* MaxBackends = 16 is arbitrary .
*/
CreateSharedMemoryAndSemaphores ( true , 16 ) ;
}
}
/*
* Early initialization of a backend ( either standalone or under postmaster ) .
* This happens even before InitPostgres .
*
* If you ' re wondering why this is separate from InitPostgres at all :
* the critical distinction is that this stuff has to happen before we can
* run XLOG - related initialization , which is done before InitPostgres - - - in
* fact , for cases such as checkpoint creation processes , InitPostgres may
* never be done at all .
*/
void
BaseInit ( void )
{
/*
* Attach to shared memory and semaphores , and initialize our
* input / output / debugging file descriptors .
*/
InitCommunication ( ) ;
DebugFileOpen ( ) ;
/* Do local initialization of storage and buffer managers */
smgrinit ( ) ;
InitBufferPoolAccess ( ) ;
InitLocalBuffer ( ) ;
}
/* --------------------------------
* InitPostgres
* Initialize POSTGRES .
*
* Note :
* Be very careful with the order of calls in the InitPostgres function .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
void
InitPostgres ( const char * dbname , const char * username )
{
bool bootstrap = IsBootstrapProcessingMode ( ) ;
/*
* Set up the global variables holding database name , id , and path .
*
* We take a shortcut in the bootstrap case , otherwise we have to look up
* the db name in pg_database .
*/
SetDatabaseName ( dbname ) ;
if ( bootstrap )
{
MyDatabaseId = TemplateDbOid ;
SetDatabasePath ( GetDatabasePath ( MyDatabaseId ) ) ;
}
else
{
char * fullpath ,
datpath [ MAXPGPATH ] ;
/*
* Formerly we validated DataDir here , but now that ' s done
* earlier .
*/
/*
* Find oid and path of the database we ' re about to open . Since
* we ' re not yet up and running we have to use the hackish
* GetRawDatabaseInfo .
*/
GetRawDatabaseInfo ( dbname , & MyDatabaseId , datpath ) ;
if ( ! OidIsValid ( MyDatabaseId ) )
elog ( FATAL ,
" Database \" %s \" does not exist in the system catalog. " ,
dbname ) ;
fullpath = GetDatabasePath ( MyDatabaseId ) ;
/* Verify the database path */
if ( access ( fullpath , F_OK ) = = - 1 )
elog ( FATAL , " Database \" %s \" does not exist. \n \t "
" The database subdirectory '%s' is missing. " ,
dbname , fullpath ) ;
ValidatePgVersion ( fullpath ) ;
if ( chdir ( fullpath ) = = - 1 )
elog ( FATAL , " Unable to change directory to '%s': %m " , fullpath ) ;
SetDatabasePath ( fullpath ) ;
}
/*
* Code after this point assumes we are in the proper directory !
*/
/*
* Set up my per - backend PROC struct in shared memory . ( We need to
* know MyDatabaseId before we can do this , since it ' s entered into
* the PROC struct . )
*/
InitProcess ( ) ;
/*
* Initialize my entry in the shared - invalidation manager ' s array of
* per - backend data . ( Formerly this came before InitProcess , but now
* it must happen after , because it uses MyProc . ) Once I have done
* this , I am visible to other backends !
*
* Sets up MyBackendId , a unique backend identifier .
*/
MyBackendId = InvalidBackendId ;
InitBackendSharedInvalidationState ( ) ;
if ( MyBackendId > MaxBackends | | MyBackendId < = 0 )
elog ( FATAL , " InitPostgres: bad backend id %d " , MyBackendId ) ;
/*
* Initialize the transaction system override state .
*/
AmiTransactionOverride ( bootstrap ) ;
/*
* Initialize the relation descriptor cache . This must create
* at least the minimum set of " nailed-in " cache entries . No
* catalog access happens here .
*/
RelationCacheInitialize ( ) ;
/*
* Initialize all the system catalog caches . Note that no catalog
* access happens here ; we only set up the cache structure .
*/
InitCatalogCache ( ) ;
/* Initialize portal manager */
EnablePortalManager ( ) ;
/*
* Initialize the deferred trigger manager - - - must happen before
* first transaction start .
*/
DeferredTriggerInit ( ) ;
/* start a new transaction here before access to db */
if ( ! bootstrap )
StartTransactionCommand ( ) ;
/*
* It ' s now possible to do real access to the system catalogs .
*
* Replace faked - up relcache entries with correct info .
*/
RelationCacheInitializePhase2 ( ) ;
/*
* Figure out our postgres user id . In standalone mode we use a fixed
* id , otherwise we figure it out from the authenticated user name .
*/
if ( bootstrap )
InitializeSessionUserIdStandalone ( ) ;
else if ( ! IsUnderPostmaster )
{
InitializeSessionUserIdStandalone ( ) ;
if ( ! ThereIsAtLeastOneUser ( ) )
{
elog ( WARNING , " There are currently no users defined in this database system. " ) ;
elog ( WARNING , " You should immediately run 'CREATE USER \" %s \" WITH SYSID %d CREATEUSER;'. " ,
username , BOOTSTRAP_USESYSID ) ;
}
}
else
{
/* normal multiuser case */
InitializeSessionUserId ( username ) ;
}
/*
* Unless we are bootstrapping , double - check that InitMyDatabaseInfo ( )
* got a correct result . We can ' t do this until all the
* database - access infrastructure is up .
*/
if ( ! bootstrap )
ReverifyMyDatabase ( dbname ) ;
# ifdef MULTIBYTE
/* set default client encoding --- uses info from ReverifyMyDatabase */
set_default_client_encoding ( ) ;
# endif
/*
* Final phase of relation cache startup : write a new cache file
* if necessary . This is done after ReverifyMyDatabase to avoid
* writing a cache file into a dead database .
*/
RelationCacheInitializePhase3 ( ) ;
/*
* Set up process - exit callback to do pre - shutdown cleanup . This should
* be last because we want shmem_exit to call this routine before the exit
* callbacks that are registered by buffer manager , lock manager , etc .
* We need to run this code before we close down database access !
*/
on_shmem_exit ( ShutdownPostgres , 0 ) ;
/* close the transaction we started above */
if ( ! bootstrap )
CommitTransactionCommand ( ) ;
}
/*
* Backend - shutdown callback . Do cleanup that we want to be sure happens
* before all the supporting modules begin to nail their doors shut via
* their own callbacks . Note that because this has to be registered very
* late in startup , it will not get called if we suffer a failure * during *
* startup .
*
* User - level cleanup , such as temp - relation removal and UNLISTEN , happens
* via separate callbacks that execute before this one . We don ' t combine the
* callbacks because we still want this one to happen if the user - level
* cleanup fails .
*/
static void
ShutdownPostgres ( void )
{
/*
* These operations are really just a minimal subset of
* AbortTransaction ( ) . We don ' t want to do any inessential cleanup ,
* since that just raises the odds of failure - - - but there ' s some
* stuff we need to do .
*
* Release any LW locks and buffer context locks we might be holding .
* This is a kluge to improve the odds that we won ' t get into a
* self - made stuck - lock scenario while trying to shut down .
*/
LWLockReleaseAll ( ) ;
AbortBufferIO ( ) ;
UnlockBuffers ( ) ;
/*
* In case a transaction is open , delete any files it created . This
* has to happen before bufmgr shutdown , so having smgr register a
* callback for it wouldn ' t work .
*/
smgrDoPendingDeletes ( false ) ; /* delete as though aborting xact */
}
/*
* Returns true if at least one user is defined in this database cluster .
*/
static bool
ThereIsAtLeastOneUser ( void )
{
Relation pg_shadow_rel ;
TupleDesc pg_shadow_dsc ;
HeapScanDesc scan ;
bool result ;
pg_shadow_rel = heap_openr ( ShadowRelationName , AccessExclusiveLock ) ;
pg_shadow_dsc = RelationGetDescr ( pg_shadow_rel ) ;
scan = heap_beginscan ( pg_shadow_rel , false , SnapshotNow , 0 , 0 ) ;
result = HeapTupleIsValid ( heap_getnext ( scan , 0 ) ) ;
heap_endscan ( scan ) ;
heap_close ( pg_shadow_rel , AccessExclusiveLock ) ;
return result ;
}