diff --git a/doc/src/sgml/ref/create_database.sgml b/doc/src/sgml/ref/create_database.sgml index d9e371b1be..0bae8450c1 100644 --- a/doc/src/sgml/ref/create_database.sgml +++ b/doc/src/sgml/ref/create_database.sgml @@ -1,5 +1,5 @@ @@ -23,7 +23,10 @@ Postgres documentation 1999-12-11 -CREATE DATABASE name [ WITH LOCATION = 'dbpath' ] +CREATE DATABASE name + [ WITH [ LOCATION = 'dbpath' ] + [ TEMPLATE = template ] + [ ENCODING = encoding ] ] @@ -48,8 +51,30 @@ CREATE DATABASE name [ WITH LOCATIO dbpath - An alternate location where to store the new database in the filesystem. - See below for caveats. + An alternate filesystem location in which to store the new database, + specified as a string literal; + or DEFAULT to use the default location. + + + + + template + + + Name of template from which to create the new database, + or DEFAULT to use the default template + (template1). + + + + + encoding + + + Multibyte encoding method to use in the new database. Specify + a string literal name (e.g., 'SQL_ASCII'), + or an integer encoding number, or DEFAULT + to use the default encoding. @@ -98,11 +123,10 @@ CREATE DATABASE name [ WITH LOCATIO - ERROR: Single quotes are not allowed in database names. - ERROR: Single quotes are not allowed in database paths. + ERROR: database path may not contain single quotes - The database name and + The database location dbpath cannot contain single quotes. This is required so that the shell commands that create the database directory can execute safely. @@ -111,18 +135,7 @@ CREATE DATABASE name [ WITH LOCATIO - ERROR: The path 'xxx' is invalid. - - - The expansion of the specified dbpath - (see below) failed. Check the path you entered or make sure that the - environment variable you are referencing does exist. - - - - - - ERROR: createdb: May not be called in a transaction block. + ERROR: CREATE DATABASE: may not be called in a transaction block If you have an explicit transaction block in progress you cannot call @@ -133,6 +146,9 @@ CREATE DATABASE name [ WITH LOCATIO ERROR: Unable to create database directory 'path'. + + + ERROR: Could not initialize database directory. @@ -169,10 +185,10 @@ CREATE DATABASE name [ WITH LOCATIO command. - If the path contains a slash, the leading part is interpreted - as an environment variable, which must be known to the + If the path name does not contain a slash, it is interpreted + as an environment variable name, which must be known to the server process. This way the database administrator can - exercise control over at which locations databases can be created. + exercise control over locations in which databases can be created. (A customary choice is, e.g., 'PGDATA2'.) If the server is compiled with ALLOW_ABSOLUTE_DBPATHS (not so by default), absolute path names, as identified by @@ -181,6 +197,29 @@ CREATE DATABASE name [ WITH LOCATIO are allowed as well. + + By default, the new database will be created by cloning the standard + system database template1. A different template can be + specified by writing TEMPLATE = + name. In particular, + by writing TEMPLATE = template0, you can create a virgin + database containing only the standard objects predefined by your + version of Postgres. This is useful if you wish to avoid copying + any installation-local objects that may have been added to template1. + + + + The optional encoding parameter allows selection of the database encoding, + if your server was compiled with multibyte encoding support. When not + specified, it defaults to the encoding used by the selected template + database. + + + + Optional parameters can be written in any order, not only the order + illustrated above. + + 1999-12-11 @@ -221,6 +260,33 @@ comment from Olly; response from Thomas... Not sure if the dump/reload would guarantee that the alternate data area gets refreshed though... --> + + + Although it is possible to copy a database other than template1 by + specifying its name as the template, this is not (yet) intended as + a general-purpose COPY DATABASE facility. In particular, it is + essential that the source database be idle (no data-altering transactions + in progress) + for the duration of the copying operation. CREATE DATABASE will check + that no backend processes (other than itself) are connected to + the source database at the start of the operation, but this does not + guarantee that changes cannot be made while the copy proceeds. Therefore, + we recommend that databases used as templates be treated as read-only. + + + + Two useful flags exist in pg_database for each + database: datistemplate and + datallowconn. datistemplate + may be set to indicate that a database is intended as a template for + CREATE DATABASE. If this flag is set, the database may be cloned by + any user with CREATEDB privileges; if it is not set, only superusers + and the owner of the database may clone it. + If datallowconn is false, then no new connections + to that database will be allowed (but existing sessions are not killed + simply by setting the flag false). The template0 + database is normally marked this way to prevent modification of it. + diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 2442679250..70fd952db6 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -8,12 +8,11 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.66 2000/11/12 20:51:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.67 2000/11/14 18:37:41 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "commands/dbcommands.h" #include #include @@ -27,6 +26,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_shadow.h" #include "commands/comment.h" +#include "commands/dbcommands.h" #include "miscadmin.h" #include "storage/sinval.h" /* for DatabaseHasActiveBackends */ #include "utils/builtins.h" @@ -35,29 +35,40 @@ /* non-export function prototypes */ +static bool get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, + int *encodingP, bool *dbIsTemplateP, + Oid *dbLastSysOidP, char *dbpath); static bool get_user_info(Oid use_sysid, bool *use_super, bool *use_createdb); -static bool get_db_info(const char *name, char *dbpath, Oid *dbIdP, int4 *ownerIdP); -static char * resolve_alt_dbpath(const char * dbpath, Oid dboid); -static bool remove_dbdirs(const char * real_loc, const char * altloc); +static char *resolve_alt_dbpath(const char *dbpath, Oid dboid); +static bool remove_dbdirs(const char *real_loc, const char *altloc); /* * CREATE DATABASE */ void -createdb(const char *dbname, const char *dbpath, int encoding) +createdb(const char *dbname, const char *dbpath, + const char *dbtemplate, int encoding) { + char *nominal_loc; + char *alt_loc; + char *target_dir; + char src_loc[MAXPGPATH]; char buf[2 * MAXPGPATH + 100]; - char *altloc; - char *real_loc; int ret; bool use_super, use_createdb; + Oid src_dboid; + int4 src_owner; + int src_encoding; + bool src_istemplate; + Oid src_lastsysoid; + char src_dbpath[MAXPGPATH]; Relation pg_database_rel; HeapTuple tuple; TupleDesc pg_database_dsc; Datum new_record[Natts_pg_database]; - char new_record_nulls[Natts_pg_database] = {' ', ' ', ' ', ' ', ' '}; + char new_record_nulls[Natts_pg_database]; Oid dboid; if (!get_user_info(GetUserId(), &use_super, &use_createdb)) @@ -66,53 +77,162 @@ createdb(const char *dbname, const char *dbpath, int encoding) if (!use_createdb && !use_super) elog(ERROR, "CREATE DATABASE: permission denied"); - if (get_db_info(dbname, NULL, NULL, NULL)) - elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname); - /* don't call this in a transaction block */ if (IsTransactionBlock()) elog(ERROR, "CREATE DATABASE: may not be called in a transaction block"); /* - * Insert a new tuple into pg_database + * Check for db name conflict. There is a race condition here, since + * another backend could create the same DB name before we commit. + * However, holding an exclusive lock on pg_database for the whole time + * we are copying the source database doesn't seem like a good idea, + * so accept possibility of race to create. We will check again after + * we grab the exclusive lock. */ - pg_database_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock); - pg_database_dsc = RelationGetDescr(pg_database_rel); + if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL)) + elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname); + + /* + * Lookup database (template) to be cloned. + */ + if (!dbtemplate) + dbtemplate = "template1"; /* Default template database name */ + + if (!get_db_info(dbtemplate, &src_dboid, &src_owner, &src_encoding, + &src_istemplate, &src_lastsysoid, src_dbpath)) + elog(ERROR, "CREATE DATABASE: template \"%s\" does not exist", + dbtemplate); + /* + * Permission check: to copy a DB that's not marked datistemplate, + * you must be superuser or the owner thereof. + */ + if (!src_istemplate) + { + if (!use_super && GetUserId() != src_owner) + elog(ERROR, "CREATE DATABASE: permission to copy \"%s\" denied", + dbtemplate); + } + /* + * Determine physical path of source database + */ + alt_loc = resolve_alt_dbpath(src_dbpath, src_dboid); + if (!alt_loc) + alt_loc = GetDatabasePath(src_dboid); + strcpy(src_loc, alt_loc); + + /* + * The source DB can't have any active backends, except this one + * (exception is to allow CREATE DB while connected to template1). + * Otherwise we might copy inconsistent data. This check is not + * bulletproof, since someone might connect while we are copying... + */ + if (DatabaseHasActiveBackends(src_dboid, true)) + elog(ERROR, "CREATE DATABASE: source database \"%s\" is being accessed by other users", dbtemplate); + + /* If encoding is defaulted, use source's encoding */ + if (encoding < 0) + encoding = src_encoding; /* - * Preassign OID for pg_database tuple, so that we know current - * OID counter value + * Preassign OID for pg_database tuple, so that we can compute db path. */ dboid = newoid(); + /* + * Compute nominal location (where we will try to access the database), + * and resolve alternate physical location if one is specified. + */ + nominal_loc = GetDatabasePath(dboid); + alt_loc = resolve_alt_dbpath(dbpath, dboid); + + if (strchr(nominal_loc, '\'')) + elog(ERROR, "database path may not contain single quotes"); + if (alt_loc && strchr(alt_loc, '\'')) + elog(ERROR, "database path may not contain single quotes"); + if (strchr(src_loc, '\'')) + elog(ERROR, "database path may not contain single quotes"); + /* ... otherwise we'd be open to shell exploits below */ + +#ifdef XLOG + /* Try to force any dirty buffers out to disk */ + BufferSync(); +#endif + + /* + * Close virtual file descriptors so the kernel has more available for + * the mkdir() and system() calls below. + */ + closeAllVfds(); + + /* + * Check we can create the target directory --- but then remove it + * because we rely on cp(1) to create it for real. + */ + target_dir = alt_loc ? alt_loc : nominal_loc; + + if (mkdir(target_dir, S_IRWXU) != 0) + elog(ERROR, "CREATE DATABASE: unable to create database directory '%s': %m", + target_dir); + rmdir(target_dir); + + /* Make the symlink, if needed */ + if (alt_loc) + { + if (symlink(alt_loc, nominal_loc) != 0) + elog(ERROR, "CREATE DATABASE: could not link '%s' to '%s': %m", + nominal_loc, alt_loc); + } + + /* Copy the template database to the new location */ + snprintf(buf, sizeof(buf), "cp -r '%s' '%s'", src_loc, target_dir); + + ret = system(buf); + /* Some versions of SunOS seem to return ECHILD after a system() call */ + if (ret != 0 && errno != ECHILD) + { + if (remove_dbdirs(nominal_loc, alt_loc)) + elog(ERROR, "CREATE DATABASE: could not initialize database directory"); + else + elog(ERROR, "CREATE DATABASE: could not initialize database directory; delete failed as well"); + } + + /* + * Now OK to grab exclusive lock on pg_database. + */ + pg_database_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + + /* Check to see if someone else created same DB name meanwhile. */ + if (get_db_info(dbname, NULL, NULL, NULL, NULL, NULL, NULL)) + { + remove_dbdirs(nominal_loc, alt_loc); + elog(ERROR, "CREATE DATABASE: database \"%s\" already exists", dbname); + } + + /* + * Insert a new tuple into pg_database + */ + pg_database_dsc = RelationGetDescr(pg_database_rel); + /* Form tuple */ new_record[Anum_pg_database_datname - 1] = DirectFunctionCall1(namein, CStringGetDatum(dbname)); new_record[Anum_pg_database_datdba - 1] = Int32GetDatum(GetUserId()); new_record[Anum_pg_database_encoding - 1] = Int32GetDatum(encoding); - /* Save current OID val */ - new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(dboid); + new_record[Anum_pg_database_datistemplate - 1] = BoolGetDatum(false); + new_record[Anum_pg_database_datallowconn - 1] = BoolGetDatum(true); + new_record[Anum_pg_database_datlastsysoid - 1] = ObjectIdGetDatum(src_lastsysoid); /* no nulls here, GetRawDatabaseInfo doesn't like them */ new_record[Anum_pg_database_datpath - 1] = DirectFunctionCall1(textin, CStringGetDatum(dbpath ? dbpath : "")); + memset(new_record_nulls, ' ', sizeof(new_record_nulls)); + tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls); - tuple->t_data->t_oid = dboid; /* override heap_insert */ + tuple->t_data->t_oid = dboid; /* override heap_insert's OID selection */ - - /* - * Update table - */ heap_insert(pg_database_rel, tuple); - real_loc = GetDatabasePath(tuple->t_data->t_oid); - altloc = resolve_alt_dbpath(dbpath, tuple->t_data->t_oid); - - if (strchr(real_loc, '\'') && strchr(altloc, '\'')) - elog(ERROR, "database path may not contain single quotes"); - /* ... otherwise we'd be open to shell exploits below */ - /* * Update indexes (there aren't any currently) */ @@ -129,59 +249,23 @@ createdb(const char *dbname, const char *dbpath, int encoding) } #endif + /* Close pg_database, but keep lock till commit */ heap_close(pg_database_rel, NoLock); - - /* - * Close virtual file descriptors so the kernel has more available for - * the mkdir() and system() calls below. - */ - closeAllVfds(); - - /* Copy the template database to the new location */ - - if (mkdir((altloc ? altloc : real_loc), S_IRWXU) != 0) - elog(ERROR, "CREATE DATABASE: unable to create database directory '%s': %s", - (altloc ? altloc : real_loc), strerror(errno)); - - if (altloc) - { - if (symlink(altloc, real_loc) != 0) - elog(ERROR, "CREATE DATABASE: could not link %s to %s: %s", - real_loc, altloc, strerror(errno)); - } - - snprintf(buf, sizeof(buf), "cp '%s'/* '%s'", - GetDatabasePath(TemplateDbOid), real_loc); - - ret = system(buf); - /* Some versions of SunOS seem to return ECHILD after a system() call */ - if (ret != 0 && errno != ECHILD) - { - if (remove_dbdirs(real_loc, altloc)) - elog(ERROR, "CREATE DATABASE: could not initialize database directory"); - else - elog(ERROR, "CREATE DATABASE: could not initialize database directory; delete failed as well"); - } - -#ifdef XLOG - BufferSync(); -#endif } - /* * DROP DATABASE */ - void dropdb(const char *dbname) { int4 db_owner; + bool db_istemplate; bool use_super; Oid db_id; - char *altloc; - char *real_loc; + char *alt_loc; + char *nominal_loc; char dbpath[MAXPGPATH]; Relation pgdbrel; HeapScanDesc pgdbscan; @@ -190,9 +274,6 @@ dropdb(const char *dbname) AssertArg(dbname); - if (strcmp(dbname, "template1") == 0) - elog(ERROR, "DROP DATABASE: may not be executed on the template1 database"); - if (strcmp(dbname, DatabaseName) == 0) elog(ERROR, "DROP DATABASE: cannot be executed on the currently open database"); @@ -202,15 +283,6 @@ dropdb(const char *dbname) if (!get_user_info(GetUserId(), &use_super, NULL)) elog(ERROR, "current user name is invalid"); - if (!get_db_info(dbname, dbpath, &db_id, &db_owner)) - elog(ERROR, "DROP DATABASE: database \"%s\" does not exist", dbname); - - if (GetUserId() != db_owner && !use_super) - elog(ERROR, "DROP DATABASE: permission denied"); - - real_loc = GetDatabasePath(db_id); - altloc = resolve_alt_dbpath(dbpath, db_id); - /* * Obtain exclusive lock on pg_database. We need this to ensure that * no new backend starts up in the target database while we are @@ -222,14 +294,29 @@ dropdb(const char *dbname) */ pgdbrel = heap_openr(DatabaseRelationName, AccessExclusiveLock); + if (!get_db_info(dbname, &db_id, &db_owner, NULL, + &db_istemplate, NULL, dbpath)) + elog(ERROR, "DROP DATABASE: database \"%s\" does not exist", dbname); + + if (!use_super && GetUserId() != db_owner) + elog(ERROR, "DROP DATABASE: permission denied"); + + /* + * Disallow dropping a DB that is marked istemplate. This is just + * to prevent people from accidentally dropping template0 or template1; + * they can do so if they're really determined ... + */ + if (db_istemplate) + elog(ERROR, "DROP DATABASE: database is marked as a template"); + + nominal_loc = GetDatabasePath(db_id); + alt_loc = resolve_alt_dbpath(dbpath, db_id); + /* * Check for active backends in the target database. */ if (DatabaseHasActiveBackends(db_id, false)) - { - heap_close(pgdbrel, AccessExclusiveLock); elog(ERROR, "DROP DATABASE: database \"%s\" is being accessed by other users", dbname); - } /* * Find the database's tuple by OID (should be unique, we trust). @@ -242,8 +329,6 @@ dropdb(const char *dbname) tup = heap_getnext(pgdbscan, 0); if (!HeapTupleIsValid(tup)) { - heap_close(pgdbrel, AccessExclusiveLock); - /* * This error should never come up since the existence of the * database is checked earlier @@ -252,9 +337,6 @@ dropdb(const char *dbname) dbname); } - /* Delete any comments associated with the database */ - DeleteComments(db_id); - /* Remove the database's tuple from pg_database */ heap_delete(pgdbrel, &tup->t_self, NULL); @@ -266,6 +348,9 @@ dropdb(const char *dbname) */ 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 @@ -273,16 +358,10 @@ dropdb(const char *dbname) */ DropBuffers(db_id); - /* - * Close virtual file descriptors so the kernel has more available for - * the system() call below. - */ - closeAllVfds(); - /* * Remove the database's subdirectory and everything in it. */ - remove_dbdirs(real_loc, altloc); + remove_dbdirs(nominal_loc, alt_loc); } @@ -292,28 +371,32 @@ dropdb(const char *dbname) */ static bool -get_db_info(const char *name, char *dbpath, Oid *dbIdP, int4 *ownerIdP) +get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP, + int *encodingP, bool *dbIsTemplateP, + Oid *dbLastSysOidP, char *dbpath) { Relation relation; - HeapTuple tuple; ScanKeyData scanKey; HeapScanDesc scan; + HeapTuple tuple; AssertArg(name); - relation = heap_openr(DatabaseRelationName, AccessExclusiveLock /* ??? */ ); + /* Caller may wish to grab a better lock on pg_database beforehand... */ + relation = heap_openr(DatabaseRelationName, AccessShareLock); ScanKeyEntryInitialize(&scanKey, 0, Anum_pg_database_datname, F_NAMEEQ, NameGetDatum(name)); scan = heap_beginscan(relation, 0, SnapshotNow, 1, &scanKey); if (!HeapScanIsValid(scan)) - elog(ERROR, "Cannot begin scan of %s.", DatabaseRelationName); + elog(ERROR, "Cannot begin scan of %s", DatabaseRelationName); tuple = heap_getnext(scan, 0); if (HeapTupleIsValid(tuple)) { + Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple); text *tmptext; bool isnull; @@ -322,22 +405,23 @@ get_db_info(const char *name, char *dbpath, Oid *dbIdP, int4 *ownerIdP) *dbIdP = tuple->t_data->t_oid; /* uid of the owner */ if (ownerIdP) - { - *ownerIdP = (int4) heap_getattr(tuple, - Anum_pg_database_datdba, - RelationGetDescr(relation), - &isnull); - if (isnull) - *ownerIdP = -1; /* hopefully no one has that id already ;) */ - } + *ownerIdP = dbform->datdba; + /* multibyte encoding */ + if (encodingP) + *encodingP = dbform->encoding; + /* allowed as template? */ + if (dbIsTemplateP) + *dbIsTemplateP = dbform->datistemplate; + /* last system OID used in database */ + if (dbLastSysOidP) + *dbLastSysOidP = dbform->datlastsysoid; /* database path (as registered in pg_database) */ if (dbpath) { - tmptext = (text *) heap_getattr(tuple, - Anum_pg_database_datpath, - RelationGetDescr(relation), - &isnull); - + tmptext = DatumGetTextP(heap_getattr(tuple, + Anum_pg_database_datpath, + RelationGetDescr(relation), + &isnull)); if (!isnull) { Assert(VARSIZE(tmptext) - VARHDRSZ < MAXPGPATH); @@ -349,16 +433,9 @@ get_db_info(const char *name, char *dbpath, Oid *dbIdP, int4 *ownerIdP) strcpy(dbpath, ""); } } - else - { - if (dbIdP) - *dbIdP = InvalidOid; - } heap_endscan(scan); - - /* We will keep the lock on the relation until end of transaction. */ - heap_close(relation, NoLock); + heap_close(relation, AccessShareLock); return HeapTupleIsValid(tuple); } @@ -396,6 +473,8 @@ resolve_alt_dbpath(const char * dbpath, Oid dboid) if (strchr(dbpath, '/')) { + if (dbpath[0] != '/') + elog(ERROR, "Relative paths are not allowed as database locations"); #ifndef ALLOW_ABSOLUTE_DBPATHS elog(ERROR, "Absolute paths are not allowed as database locations"); #endif @@ -406,9 +485,9 @@ resolve_alt_dbpath(const char * dbpath, Oid dboid) /* must be environment variable */ char * var = getenv(dbpath); if (!var) - elog(ERROR, "environment variable %s not set", dbpath); + elog(ERROR, "Postmaster environment variable '%s' not set", dbpath); if (var[0] != '/') - elog(ERROR, "environment variable %s must be absolute path", dbpath); + elog(ERROR, "Postmaster environment variable '%s' must be absolute path", dbpath); prefix = var; } @@ -421,24 +500,36 @@ resolve_alt_dbpath(const char * dbpath, Oid dboid) static bool -remove_dbdirs(const char * real_loc, const char * altloc) +remove_dbdirs(const char * nominal_loc, const char * alt_loc) { + const char *target_dir; char buf[MAXPGPATH + 100]; bool success = true; - if (altloc) + target_dir = alt_loc ? alt_loc : nominal_loc; + + /* + * Close virtual file descriptors so the kernel has more available for + * the system() call below. + */ + closeAllVfds(); + + if (alt_loc) + { /* remove symlink */ - if (unlink(real_loc) != 0) + if (unlink(nominal_loc) != 0) { - elog(NOTICE, "could not remove '%s': %s", real_loc, strerror(errno)); + elog(NOTICE, "could not remove '%s': %m", nominal_loc); success = false; } + } + + snprintf(buf, sizeof(buf), "rm -rf '%s'", target_dir); - snprintf(buf, sizeof(buf), "rm -rf '%s'", altloc ? altloc : real_loc); if (system(buf) != 0 && errno != ECHILD) { elog(NOTICE, "database directory '%s' could not be removed", - altloc ? altloc : real_loc); + target_dir); success = false; } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 3490ef8062..9c342b5dc1 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.131 2000/11/12 00:36:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.132 2000/11/14 18:37:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2231,6 +2231,8 @@ _copyCreatedbStmt(CreatedbStmt *from) newnode->dbname = pstrdup(from->dbname); if (from->dbpath) newnode->dbpath = pstrdup(from->dbpath); + if (from->dbtemplate) + newnode->dbtemplate = pstrdup(from->dbtemplate); newnode->encoding = from->encoding; return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 8fb81a45c0..aac0902a02 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -20,7 +20,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.81 2000/11/12 00:36:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.82 2000/11/14 18:37:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1087,6 +1087,8 @@ _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b) return false; if (!equalstr(a->dbpath, b->dbpath)) return false; + if (!equalstr(a->dbtemplate, b->dbtemplate)) + return false; if (a->encoding != b->encoding) return false; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 5572828d25..3c4a2e00c9 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.208 2000/11/08 22:09:58 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.209 2000/11/14 18:37:49 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -56,8 +56,8 @@ #include "miscadmin.h" #include "mb/pg_wchar.h" #else -#define GetTemplateEncoding() 0 /* SQL_ASCII */ -#define GetTemplateEncodingName() "SQL_ASCII" +#define GetStandardEncoding() 0 /* SQL_ASCII */ +#define GetStandardEncodingName() "SQL_ASCII" #endif extern List *parsetree; /* final parse result is delivered here */ @@ -146,8 +146,7 @@ static void doNegateFloat(Value *v); %type alter_column_action %type drop_behavior -%type createdb_opt_location -%type createdb_opt_encoding +%type createdb_opt_list, createdb_opt_item %type opt_lock, lock_type %type opt_lmode, opt_force @@ -347,7 +346,7 @@ static void doNegateFloat(Value *v); OFFSET, OIDS, OPERATOR, OWNER, PASSWORD, PROCEDURAL, REINDEX, RENAME, RESET, RETURNS, ROW, RULE, SEQUENCE, SERIAL, SETOF, SHARE, SHOW, START, STATEMENT, STDIN, STDOUT, SYSID, - TEMP, TOAST, TRUNCATE, TRUSTED, + TEMP, TEMPLATE, TOAST, TRUNCATE, TRUSTED, UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION /* The grammar thinks these are keywords, but they are not in the keywords.c @@ -687,7 +686,8 @@ CreateSchemaStmt: CREATE SCHEMA UserId CreatedbStmt *n = makeNode(CreatedbStmt); n->dbname = $3; n->dbpath = NULL; - n->encoding = GetTemplateEncoding(); + n->dbtemplate = NULL; + n->encoding = -1; $$ = (Node *)n; } ; @@ -2924,16 +2924,34 @@ LoadStmt: LOAD file_name * *****************************************************************************/ -CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_location createdb_opt_encoding +CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_list { CreatedbStmt *n = makeNode(CreatedbStmt); - - if ($5 == NULL && $6 == -1) - elog(ERROR, "CREATE DATABASE WITH requires at least one option"); + List *l; n->dbname = $3; - n->dbpath = $5; - n->encoding = ($6 == -1) ? GetTemplateEncoding() : $6; + /* set default options */ + n->dbpath = NULL; + n->dbtemplate = NULL; + n->encoding = -1; + /* process additional options */ + foreach(l, $5) + { + List *optitem = (List *) lfirst(l); + + switch (lfirsti(optitem)) + { + case 1: + n->dbpath = (char *) lsecond(optitem); + break; + case 2: + n->dbtemplate = (char *) lsecond(optitem); + break; + case 3: + n->encoding = lfirsti(lnext(optitem)); + break; + } + } $$ = (Node *)n; } | CREATE DATABASE database_name @@ -2941,27 +2959,51 @@ CreatedbStmt: CREATE DATABASE database_name WITH createdb_opt_location createdb CreatedbStmt *n = makeNode(CreatedbStmt); n->dbname = $3; n->dbpath = NULL; - n->encoding = GetTemplateEncoding(); + n->dbtemplate = NULL; + n->encoding = -1; $$ = (Node *)n; } ; -createdb_opt_location: LOCATION '=' Sconst { $$ = $3; } - | LOCATION '=' DEFAULT { $$ = NULL; } - | /*EMPTY*/ { $$ = NULL; } +createdb_opt_list: createdb_opt_item + { $$ = makeList1($1); } + | createdb_opt_list createdb_opt_item + { $$ = lappend($1, $2); } ; -createdb_opt_encoding: ENCODING '=' Sconst +/* + * createdb_opt_item returns 2-element lists, with the first element + * being an integer code to indicate which item was specified. + */ +createdb_opt_item: LOCATION '=' Sconst { + $$ = lconsi(1, makeList1($3)); + } + | LOCATION '=' DEFAULT + { + $$ = lconsi(1, makeList1((char *) NULL)); + } + | TEMPLATE '=' name + { + $$ = lconsi(2, makeList1($3)); + } + | TEMPLATE '=' DEFAULT + { + $$ = lconsi(2, makeList1((char *) NULL)); + } + | ENCODING '=' Sconst + { + int encoding; #ifdef MULTIBYTE - $$ = pg_char_to_encoding($3); - if ($$ == -1) + encoding = pg_char_to_encoding($3); + if (encoding == -1) elog(ERROR, "%s is not a valid encoding name", $3); #else - if (strcasecmp($3, GetTemplateEncodingName()) != 0) + if (strcasecmp($3, GetStandardEncodingName()) != 0) elog(ERROR, "Multi-byte support is not enabled"); - $$ = GetTemplateEncoding(); + encoding = GetStandardEncoding(); #endif + $$ = lconsi(3, makeListi1(encoding)); } | ENCODING '=' Iconst { @@ -2969,18 +3011,14 @@ createdb_opt_encoding: ENCODING '=' Sconst if (!pg_get_encent_by_encoding($3)) elog(ERROR, "%d is not a valid encoding code", $3); #else - if ($3 != GetTemplateEncoding()) + if ($3 != GetStandardEncoding()) elog(ERROR, "Multi-byte support is not enabled"); #endif - $$ = $3; + $$ = lconsi(3, makeListi1($3)); } | ENCODING '=' DEFAULT { - $$ = GetTemplateEncoding(); - } - | /*EMPTY*/ - { - $$ = -1; + $$ = lconsi(3, makeListi1(-1)); } ; @@ -5495,6 +5533,7 @@ TokenId: ABSOLUTE { $$ = "absolute"; } | STDOUT { $$ = "stdout"; } | SYSID { $$ = "sysid"; } | TEMP { $$ = "temp"; } + | TEMPLATE { $$ = "template"; } | TEMPORARY { $$ = "temporary"; } | TIMEZONE_HOUR { $$ = "timezone_hour"; } | TIMEZONE_MINUTE { $$ = "timezone_minute"; } diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index f3e4d85e4c..19ec40f15b 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.84 2000/11/08 21:28:06 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.85 2000/11/14 18:37:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -247,6 +247,7 @@ static ScanKeyword ScanKeywords[] = { {"sysid", SYSID}, {"table", TABLE}, {"temp", TEMP}, + {"template", TEMPLATE}, {"temporary", TEMPORARY}, {"then", THEN}, {"time", TIME}, diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 3a140488e4..68f7c06cd0 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.186 2000/11/14 18:11:31 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.187 2000/11/14 18:37:42 tgl Exp $ * * NOTES * @@ -279,14 +279,8 @@ checkDataDir(const char *checkdir) exit(2); } -#ifdef OLD_FILE_NAMING - snprintf(path, sizeof(path), "%s%cbase%ctemplate1%cpg_class", - checkdir, SEP_CHAR, SEP_CHAR, SEP_CHAR); -#else - snprintf(path, sizeof(path), "%s%cbase%c%u%c%u", - checkdir, SEP_CHAR, SEP_CHAR, - TemplateDbOid, SEP_CHAR, RelOid_pg_class); -#endif + snprintf(path, sizeof(path), "%s%cglobal%cpg_control", + checkdir, SEP_CHAR, SEP_CHAR); fp = AllocateFile(path, PG_BINARY_R); if (fp == NULL) diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 79ac7c0f15..63a4331528 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.101 2000/11/08 16:31:06 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.102 2000/11/14 18:37:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -604,7 +604,8 @@ ProcessUtility(Node *parsetree, set_ps_display(commandTag = "CREATE DATABASE"); - createdb(stmt->dbname, stmt->dbpath, stmt->encoding); + createdb(stmt->dbname, stmt->dbpath, + stmt->dbtemplate, stmt->encoding); } break; diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index f786eb1d10..3a9e5a1797 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.70 2000/11/12 20:51:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.71 2000/11/14 18:37:44 tgl Exp $ * * *------------------------------------------------------------------------- @@ -79,6 +79,7 @@ ReverifyMyDatabase(const char *name) HeapScanDesc pgdbscan; ScanKeyData key; HeapTuple tup; + Form_pg_database dbform; /* * Because we grab AccessShareLock here, we can be sure that destroydb @@ -106,25 +107,24 @@ ReverifyMyDatabase(const char *name) */ DropBuffers(MyDatabaseId); /* Now I can commit hara-kiri with a clear conscience... */ - elog(FATAL, "Database '%s', OID %u, has disappeared from pg_database", + 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. Note we also set the - * "template encoding", which is the default encoding for any CREATE - * DATABASE commands executed in this backend; essentially, you get - * the same encoding of the database you connected to as the default. - * (This replaces code that unreliably grabbed template1's encoding - * out of pg_database. We could do an extra scan to find template1's - * tuple, but for 99.99% of all backend startups it'd be wasted cycles - * --- and the 'createdb' script connects to template1 anyway, so - * there's no difference.) + * encoding info out of the pg_database tuple. */ #ifdef MULTIBYTE - SetDatabaseEncoding(((Form_pg_database) GETSTRUCT(tup))->encoding); - SetTemplateEncoding(((Form_pg_database) GETSTRUCT(tup))->encoding); + SetDatabaseEncoding(dbform->encoding); #endif heap_endscan(pgdbscan); diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c index 7cf082dc0c..ad1322fe52 100644 --- a/src/backend/utils/mb/mbutils.c +++ b/src/backend/utils/mb/mbutils.c @@ -3,7 +3,7 @@ * client encoding and server internal encoding. * (currently mule internal code (mic) is used) * Tatsuo Ishii - * $Id: mbutils.c,v 1.13 2000/10/30 10:40:28 ishii Exp $ */ + * $Id: mbutils.c,v 1.14 2000/11/14 18:37:44 tgl Exp $ */ #include "postgres.h" @@ -271,6 +271,7 @@ pg_mbcliplen(const unsigned char *mbstr, int len, int limit) * fuctions for utils/init */ static int DatabaseEncoding = MULTIBYTE; + void SetDatabaseEncoding(int encoding) { @@ -289,17 +290,3 @@ getdatabaseencoding(PG_FUNCTION_ARGS) { PG_RETURN_NAME(pg_encoding_to_char(DatabaseEncoding)); } - -/* set and get template1 database encoding */ -static int templateEncoding; -void -SetTemplateEncoding(int encoding) -{ - templateEncoding = encoding; -} - -int -GetTemplateEncoding() -{ - return (templateEncoding); -} diff --git a/src/backend/utils/misc/database.c b/src/backend/utils/misc/database.c index f415e5aee1..364075c8be 100644 --- a/src/backend/utils/misc/database.c +++ b/src/backend/utils/misc/database.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/misc/Attic/database.c,v 1.40 2000/10/16 14:52:19 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/misc/Attic/database.c,v 1.41 2000/11/14 18:37:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -186,7 +186,7 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) max = PageGetMaxOffsetNumber(pg); /* look at each tuple on the page */ - for (i = 0; i <= max; i++) + for (i = 0; i < max; i++) { int offset; @@ -221,8 +221,11 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) * database OID from a flat file, handled the same way we * handle the password relation? */ - if (TransactionIdIsValid((TransactionId) tup.t_data->t_xmax)) - continue; + if (tup.t_data->t_infomask & HEAP_XMIN_INVALID) + continue; /* inserting xact known aborted */ + if (TransactionIdIsValid((TransactionId) tup.t_data->t_xmax) && + !(tup.t_data->t_infomask & HEAP_XMAX_INVALID)) + continue; /* deleting xact happened, not known aborted */ /* * Okay, see if this is the one we want. @@ -241,6 +244,10 @@ GetRawDatabaseInfo(const char *name, Oid *db_id, char *path) } } + /* failed to find it... */ + *db_id = InvalidOid; + *path = '\0'; + done: close(dbfd); pfree(pg); diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh index dc500a6e70..2358c61b5e 100644 --- a/src/bin/initdb/initdb.sh +++ b/src/bin/initdb/initdb.sh @@ -7,15 +7,16 @@ # # To create the database cluster, we create the directory that contains # all its data, create the files that hold the global tables, create -# a few other control files for it, and create one database: the -# template database. +# a few other control files for it, and create two databases: the +# template0 and template1 databases. # -# The template database is an ordinary PostgreSQL database. Its data -# never changes, though. It exists to make it easy for PostgreSQL to -# create other databases -- it just copies. +# The template databases are ordinary PostgreSQL databases. template0 +# is never supposed to change after initdb, whereas template1 can be +# changed to add site-local standard data. Either one can be copied +# to produce a new database. # # Optionally, we can skip creating the complete database cluster and -# just create (or replace) the template database. +# just create (or replace) the template databases. # # To create all those things, we run the postgres (backend) program and # feed it data from the bki files that were installed. @@ -23,7 +24,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.113 2000/11/11 22:59:46 petere Exp $ +# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.114 2000/11/14 18:37:45 tgl Exp $ # #------------------------------------------------------------------------- @@ -203,7 +204,7 @@ do ;; --template|-t) template_only=yes - echo "Updating template1 database only." + echo "Updating template0 and template1 databases only." ;; # The sysid of the database superuser. Can be freely changed. --sysid|-i) @@ -277,7 +278,7 @@ if [ "$usage" ]; then echo " -i, --sysid SYSID Database sysid for the superuser" echo "Less commonly used options: " echo " -L DIRECTORY Where to find the input files" - echo " -t, --template Re-initialize template database only" + echo " -t, --template Re-initialize template databases only" echo " -d, --debug Generate lots of debugging output" echo " -n, --noclean Do not clean up after errors" echo @@ -451,7 +452,7 @@ fi BACKENDARGS="-boot -C -F -D$PGDATA $BACKEND_TALK_ARG" FIRSTRUN="-boot -x1 -C -F -D$PGDATA $BACKEND_TALK_ARG" -echo "Creating template database in $PGDATA/base/1" +echo "Creating template1 database in $PGDATA/base/1" [ "$debug" = yes ] && echo "Running: $PGPATH/postgres $FIRSTRUN template1" cat "$TEMPLATE1_BKI" \ @@ -465,6 +466,10 @@ echo $short_version > "$PGDATA"/base/1/PG_VERSION || exit_nicely ########################################################################## # # CREATE GLOBAL TABLES +# +# XXX --- I do not believe the "template_only" option can actually work. +# With this coding, it'll fail to make entries for pg_shadow etc. in +# template1 ... tgl 11/2000 if [ "$template_only" != yes ] then @@ -491,7 +496,7 @@ fi # # CREATE VIEWS and other things -echo +echo "Initializing pg_shadow." PGSQL_OPT="-o /dev/null -O -F -D$PGDATA" @@ -532,6 +537,7 @@ fi echo "Enabling unlimited row width for system tables." + echo "ALTER TABLE pg_attrdef CREATE TOAST TABLE" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely echo "ALTER TABLE pg_description CREATE TOAST TABLE" \ @@ -546,7 +552,8 @@ echo "ALTER TABLE pg_statistic CREATE TOAST TABLE" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "Creating view pg_user." +echo "Creating system views." + echo "CREATE VIEW pg_user AS \ SELECT \ usename, \ @@ -560,7 +567,6 @@ echo "CREATE VIEW pg_user AS \ FROM pg_shadow" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "Creating view pg_rules." echo "CREATE VIEW pg_rules AS \ SELECT \ C.relname AS tablename, \ @@ -571,7 +577,6 @@ echo "CREATE VIEW pg_rules AS \ AND C.oid = R.ev_class;" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "Creating view pg_views." echo "CREATE VIEW pg_views AS \ SELECT \ C.relname AS viewname, \ @@ -585,7 +590,6 @@ echo "CREATE VIEW pg_views AS \ )" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "Creating view pg_tables." echo "CREATE VIEW pg_tables AS \ SELECT \ C.relname AS tablename, \ @@ -601,7 +605,6 @@ echo "CREATE VIEW pg_tables AS \ )" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely -echo "Creating view pg_indexes." echo "CREATE VIEW pg_indexes AS \ SELECT \ C.relname AS tablename, \ @@ -622,14 +625,27 @@ cat $TEMPFILE \ rm -f "$TEMPFILE" || exit_nicely echo "Setting lastsysoid." -echo "Update pg_database Set datlastsysoid = (Select max(oid) From pg_description) \ - Where datname = 'template1'" \ +echo "UPDATE pg_database SET \ + datistemplate = 't', \ + datlastsysoid = (SELECT max(oid) FROM pg_description) \ + WHERE datname = 'template1'" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely echo "Vacuuming database." echo "VACUUM ANALYZE" \ | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely +echo "Copying template1 to template0." +echo "CREATE DATABASE template0" \ + | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely +echo "UPDATE pg_database SET \ + datistemplate = 't', \ + datallowconn = 'f' \ + WHERE datname = 'template0'" \ + | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely +echo "VACUUM pg_database" \ + | "$PGPATH"/postgres $PGSQL_OPT template1 > /dev/null || exit_nicely + ########################################################################## # diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index a7c1ca9e8a..0d0fba26a2 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.179 2000/11/13 23:37:52 momjian Exp $ + * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.180 2000/11/14 18:37:45 tgl Exp $ * * Modifications - 6/10/96 - dave@bensoft.com - version 1.13.dhb * @@ -129,6 +129,8 @@ #include "pg_dump.h" #include "pg_backup.h" +#define atooid(x) ((Oid) strtoul((x), NULL, 10)) + static void dumpComment(Archive *outfile, const char *target, const char *oid); static void dumpSequence(Archive *fout, TableInfo tbinfo); static void dumpACL(Archive *fout, TableInfo tbinfo); @@ -140,7 +142,7 @@ static char *checkForQuote(const char *s); static void clearTableInfo(TableInfo *, int); static void dumpOneFunc(Archive *fout, FuncInfo *finfo, int i, TypeInfo *tinfo, int numTypes); -static int findLastBuiltinOid(const char*); +static Oid findLastBuiltinOid(const char*); static void setMaxOid(Archive *fout); static void AddAcl(char *aclbuf, const char *keyword); @@ -156,7 +158,7 @@ extern int optind, /* global decls */ bool g_verbose; /* User wants verbose narration of our * activities. */ -int g_last_builtin_oid; /* value of the last builtin oid */ +Oid g_last_builtin_oid; /* value of the last builtin oid */ Archive *g_fout; /* the script file */ PGconn *g_conn; /* the database connection */ @@ -2784,7 +2786,7 @@ dumpTypes(Archive *fout, FuncInfo *finfo, int numFuncs, { /* skip all the builtin types */ - if (atoi(tinfo[i].oid) < g_last_builtin_oid) + if (atooid(tinfo[i].oid) <= g_last_builtin_oid) continue; /* skip relation types */ @@ -2899,7 +2901,7 @@ dumpProcLangs(Archive *fout, FuncInfo *finfo, int numFuncs, for (i = 0; i < ntups; i++) { - lanoid = atoi(PQgetvalue(res, i, i_oid)); + lanoid = atooid(PQgetvalue(res, i, i_oid)); if (lanoid <= g_last_builtin_oid) continue; @@ -3127,7 +3129,7 @@ dumpOprs(Archive *fout, OprInfo *oprinfo, int numOperators, resetPQExpBuffer(sort2); /* skip all the builtin oids */ - if (atoi(oprinfo[i].oid) < g_last_builtin_oid) + if (atooid(oprinfo[i].oid) <= g_last_builtin_oid) continue; /* @@ -3222,7 +3224,7 @@ dumpAggs(Archive *fout, AggInfo *agginfo, int numAggs, resetPQExpBuffer(details); /* skip all the builtin oids */ - if (atoi(agginfo[i].oid) < g_last_builtin_oid) + if (atooid(agginfo[i].oid) <= g_last_builtin_oid) continue; appendPQExpBuffer(details, @@ -3907,12 +3909,12 @@ setMaxOid(Archive *fout) * we do this by retrieving datlastsysoid from the pg_database entry for this database, */ -static int +static Oid findLastBuiltinOid(const char* dbname) { PGresult *res; int ntups; - int last_oid; + Oid last_oid; PQExpBuffer query = createPQExpBuffer(); resetPQExpBuffer(query); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 07202bbf18..19bfa85c59 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_dump.h,v 1.53 2000/10/10 13:55:28 pjw Exp $ + * $Id: pg_dump.h,v 1.54 2000/11/14 18:37:46 tgl Exp $ * * Modifications - 6/12/96 - dave@bensoft.com - version 1.13.dhb.2 * @@ -177,7 +177,7 @@ typedef struct _oprInfo /* global decls */ extern bool g_force_quotes; /* double-quotes for identifiers flag */ extern bool g_verbose; /* verbose flag */ -extern int g_last_builtin_oid; /* value of the last builtin oid */ +extern Oid g_last_builtin_oid; /* value of the last builtin oid */ extern Archive *g_fout; /* the script file */ /* placeholders for comment starting and ending delimiters */ diff --git a/src/bin/pg_dump/pg_dumpall.sh b/src/bin/pg_dump/pg_dumpall.sh index e16beb4a03..0429a9c344 100644 --- a/src/bin/pg_dump/pg_dumpall.sh +++ b/src/bin/pg_dump/pg_dumpall.sh @@ -6,7 +6,7 @@ # and "pg_group" tables, which belong to the whole installation rather # than any one individual database. # -# $Header: /cvsroot/pgsql/src/bin/pg_dump/Attic/pg_dumpall.sh,v 1.7 2000/11/08 18:23:44 petere Exp $ +# $Header: /cvsroot/pgsql/src/bin/pg_dump/Attic/pg_dumpall.sh,v 1.8 2000/11/14 18:37:46 tgl Exp $ CMDNAME=`basename $0` @@ -151,7 +151,7 @@ echo "${BS}connect template1" # # Dump users (but not the user created by initdb) # -echo "DELETE FROM pg_shadow WHERE usesysid NOT IN (SELECT datdba FROM pg_database WHERE datname = 'template1');" +echo "DELETE FROM pg_shadow WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0');" echo $PSQL -d template1 -At <<__END__ @@ -163,7 +163,7 @@ SELECT || CASE WHEN valuntil IS NOT NULL THEN ' VALID UNTIL '''::text || CAST(valuntil AS TIMESTAMP) || '''' ELSE '' END || ';' FROM pg_shadow -WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template1'); +WHERE usesysid <> (SELECT datdba FROM pg_database WHERE datname = 'template0'); __END__ echo @@ -187,46 +187,33 @@ done test "$accounts_only" = yes && exit 0 -# First we dump the template in case there are local extensions. - -echo -echo "--" -echo "-- Database template1" -echo "--" -echo "${BS}connect template1" -$PGDUMP "template1" -if [ "$?" -ne 0 ] ; then - echo "pg_dump failed on template1, exiting" 1>&2 - exit 1 -fi - - # For each database, run pg_dump to dump the contents of that database. +# We skip databases marked not datallowconn, since we'd be unable to +# connect to them anyway (and besides, we don't want to dump template0). $PSQL -d template1 -At -F ' ' \ - -c "SELECT d.datname, u.usename, pg_encoding_to_char(d.encoding), d.datpath FROM pg_database d, pg_shadow u WHERE d.datdba = u.usesysid AND datname <> 'template1';" | \ -while read DATABASE DBOWNER ENCODING DBPATH; do + -c "SELECT datname, usename, pg_encoding_to_char(d.encoding), datistemplate, datpath FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) WHERE datallowconn;" | \ +while read DATABASE DBOWNER ENCODING ISTEMPLATE DBPATH; do echo echo "--" echo "-- Database $DATABASE" echo "--" echo "${BS}connect template1 $DBOWNER" - if [ "$cleanschema" = yes ] ; then + if [ "$cleanschema" = yes -a "$DATABASE" != template1 ] ; then echo "DROP DATABASE \"$DATABASE\";" fi - createdbcmd="CREATE DATABASE \"$DATABASE\"" - if [ x"$DBPATH" != x"" ] || [ x"$MULTIBYTE" != x"" ]; then - createdbcmd="$createdbcmd WITH" + if [ "$DATABASE" != template1 ] ; then + createdbcmd="CREATE DATABASE \"$DATABASE\" WITH TEMPLATE = template0" + if [ x"$DBPATH" != x"" ] ; then + createdbcmd="$createdbcmd LOCATION = '$DBPATH'" + fi + if [ x"$MULTIBYTE" != x"" ] ; then + createdbcmd="$createdbcmd ENCODING = '$ENCODING'" + fi + echo "$createdbcmd;" fi - if [ x"$DBPATH" != x"" ] ; then - createdbcmd="$createdbcmd LOCATION = '$DBPATH'" - fi - if [ x"$MULTIBYTE" != x"" ] ; then - createdbcmd="$createdbcmd ENCODING = '$ENCODING'" - fi - echo "$createdbcmd;" echo "${BS}connect $DATABASE $DBOWNER" $PGDUMP "$DATABASE" @@ -234,6 +221,9 @@ while read DATABASE DBOWNER ENCODING DBPATH; do echo "pg_dump failed on $DATABASE, exiting" 1>&2 exit 1 fi + if [ x"$ISTEMPLATE" = xt ] ; then + echo "UPDATE pg_database SET datistemplate = 't' WHERE datname = '$DATABASE';" + fi done exit 0 diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 67a2beb77c..3991954620 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catversion.h,v 1.59 2000/11/12 00:37:00 tgl Exp $ + * $Id: catversion.h,v 1.60 2000/11/14 18:37:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200011112 +#define CATALOG_VERSION_NO 200011131 #endif diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index de3ccdf5d8..f71aca0f09 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_attribute.h,v 1.66 2000/10/22 17:55:49 pjw Exp $ + * $Id: pg_attribute.h,v 1.67 2000/11/14 18:37:46 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -281,8 +281,10 @@ DATA(insert OID = 0 ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p f i f f)); DATA(insert OID = 0 ( 1262 datname 19 0 NAMEDATALEN 1 0 -1 -1 f p f i f f)); DATA(insert OID = 0 ( 1262 datdba 23 0 4 2 0 -1 -1 t p f i f f)); DATA(insert OID = 0 ( 1262 encoding 23 0 4 3 0 -1 -1 t p f i f f)); -DATA(insert OID = 0 ( 1262 datlastsysoid 26 0 4 4 0 -1 -1 t p f i f f)); -DATA(insert OID = 0 ( 1262 datpath 25 0 -1 5 0 -1 -1 f x f i f f)); +DATA(insert OID = 0 ( 1262 datistemplate 16 0 1 4 0 -1 -1 t p f c f f)); +DATA(insert OID = 0 ( 1262 datallowconn 16 0 1 5 0 -1 -1 t p f c f f)); +DATA(insert OID = 0 ( 1262 datlastsysoid 26 0 4 6 0 -1 -1 t p f i f f)); +DATA(insert OID = 0 ( 1262 datpath 25 0 -1 7 0 -1 -1 f x f i f f)); DATA(insert OID = 0 ( 1262 ctid 27 0 6 -1 0 -1 -1 f p f i f f)); DATA(insert OID = 0 ( 1262 oid 26 0 4 -2 0 -1 -1 t p f i f f)); DATA(insert OID = 0 ( 1262 xmin 28 0 4 -3 0 -1 -1 t p f i f f)); diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 68db583fe3..bf9a482f8a 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_class.h,v 1.44 2000/10/24 01:38:41 tgl Exp $ + * $Id: pg_class.h,v 1.45 2000/11/14 18:37:46 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -142,7 +142,7 @@ DATA(insert OID = 1260 ( pg_shadow 86 PGUID 0 1260 0 0 0 0 f t r 8 0 0 0 0 DESCR(""); DATA(insert OID = 1261 ( pg_group 87 PGUID 0 1261 0 0 0 0 f t r 3 0 0 0 0 0 f f f _null_ )); DESCR(""); -DATA(insert OID = 1262 ( pg_database 88 PGUID 0 1262 0 0 0 0 f t r 5 0 0 0 0 0 f f f _null_ )); +DATA(insert OID = 1262 ( pg_database 88 PGUID 0 1262 0 0 0 0 f t r 7 0 0 0 0 0 f f f _null_ )); DESCR(""); DATA(insert OID = 1264 ( pg_variable 90 PGUID 0 1264 0 0 0 0 f t s 1 0 0 0 0 0 f f f _null_ )); DESCR(""); diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h index 76127094de..4f8b4329ae 100644 --- a/src/include/catalog/pg_database.h +++ b/src/include/catalog/pg_database.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_database.h,v 1.14 2000/11/08 16:59:50 petere Exp $ + * $Id: pg_database.h,v 1.15 2000/11/14 18:37:46 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -36,6 +36,8 @@ CATALOG(pg_database) BOOTSTRAP NameData datname; int4 datdba; int4 encoding; + bool datistemplate; /* allowed as template for CREATE DATABASE? */ + bool datallowconn; /* new connections allowed? */ Oid datlastsysoid; text datpath; /* VARIABLE LENGTH FIELD */ } FormData_pg_database; @@ -51,15 +53,17 @@ typedef FormData_pg_database *Form_pg_database; * compiler constants for pg_database * ---------------- */ -#define Natts_pg_database 5 +#define Natts_pg_database 7 #define Anum_pg_database_datname 1 #define Anum_pg_database_datdba 2 #define Anum_pg_database_encoding 3 -#define Anum_pg_database_datlastsysoid 4 -#define Anum_pg_database_datpath 5 +#define Anum_pg_database_datistemplate 4 +#define Anum_pg_database_datallowconn 5 +#define Anum_pg_database_datlastsysoid 6 +#define Anum_pg_database_datpath 7 -DATA(insert OID = 1 ( template1 PGUID ENCODING 0 "" )); -DESCR(""); +DATA(insert OID = 1 ( template1 PGUID ENCODING t t 0 "" )); +DESCR("Default template database"); #define TemplateDbOid 1 diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index 9443150353..903368e3f9 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -7,14 +7,15 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: dbcommands.h,v 1.13 2000/01/26 05:58:00 momjian Exp $ + * $Id: dbcommands.h,v 1.14 2000/11/14 18:37:47 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef DBCOMMANDS_H #define DBCOMMANDS_H -extern void createdb(const char *dbname, const char *dbpath, int encoding); +extern void createdb(const char *dbname, const char *dbpath, + const char *dbtemplate, int encoding); extern void dropdb(const char *dbname); #endif /* DBCOMMANDS_H */ diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h index b4b96b679d..0b0c2e3137 100644 --- a/src/include/mb/pg_wchar.h +++ b/src/include/mb/pg_wchar.h @@ -1,4 +1,4 @@ -/* $Id: pg_wchar.h,v 1.22 2000/10/30 10:41:05 ishii Exp $ */ +/* $Id: pg_wchar.h,v 1.23 2000/11/14 18:37:48 tgl Exp $ */ #ifndef PG_WCHAR_H #define PG_WCHAR_H @@ -152,8 +152,6 @@ extern int pg_char_to_encoding(const char *); extern int GetDatabaseEncoding(void); extern void SetDatabaseEncoding(int); -extern void SetTemplateEncoding(int); -extern int GetTemplateEncoding(void); extern unsigned short BIG5toCNS(unsigned short, unsigned char *); extern unsigned short CNStoBIG5(unsigned short, unsigned char); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 469230e1c6..a15d4c0ad8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parsenodes.h,v 1.120 2000/11/12 00:37:01 tgl Exp $ + * $Id: parsenodes.h,v 1.121 2000/11/14 18:37:48 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -645,9 +645,10 @@ typedef struct LoadStmt typedef struct CreatedbStmt { NodeTag type; - char *dbname; /* database to create */ - char *dbpath; /* location of database */ - int encoding; /* default encoding (see regex/pg_wchar.h) */ + char *dbname; /* name of database to create */ + char *dbpath; /* location of database (NULL = default) */ + char *dbtemplate; /* template to use (NULL = default) */ + int encoding; /* MULTIBYTE encoding (-1 = use default) */ } CreatedbStmt; /* ---------------------- diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 9fd96b2280..08ec85847b 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -482,8 +482,8 @@ WHERE p1.aggtransfn = p2.oid AND (p2.pronargs = 1 AND p1.aggbasetype = 0))); oid | aggname | oid | proname -------+---------+-----+------------- - 16996 | max | 768 | int4larger - 17010 | min | 769 | int4smaller + 16998 | max | 768 | int4larger + 17012 | min | 769 | int4smaller (2 rows) -- Cross-check finalfn (if present) against its entry in pg_proc.