From 830c168e5ce40112433fcc4784415a1abacb3e57 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 17 Oct 2004 20:47:21 +0000 Subject: [PATCH] Give a more user-friendly error message in situation where CREATE DATABASE specifies a new default tablespace and the template database already has some tables in that tablespace. There isn't any way to solve this fully without modifying the clone database's pg_class contents, so for now the best we can do is issue a better error message. --- src/backend/commands/dbcommands.c | 44 ++++++++++++++++++++++++++++++++------- src/backend/commands/tablespace.c | 7 ++++--- src/include/commands/tablespace.h | 4 +++- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 1c9425789a..917429c2b5 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.144 2004/08/30 03:50:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.145 2004/10/17 20:47:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -281,6 +281,37 @@ createdb(const CreatedbStmt *stmt) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename); + + /* + * If we are trying to change the default tablespace of the template, + * we require that the template not have any files in the new default + * tablespace. This is necessary because otherwise the copied + * database would contain pg_class rows that refer to its default + * tablespace both explicitly (by OID) and implicitly (as zero), which + * would cause problems. For example another CREATE DATABASE using + * the copied database as template, and trying to change its default + * tablespace again, would yield outright incorrect results (it would + * improperly move tables to the new default tablespace that should + * stay in the same tablespace). + */ + if (dst_deftablespace != src_deftablespace) + { + char *srcpath; + struct stat st; + + srcpath = GetDatabasePath(src_dboid, dst_deftablespace); + + if (stat(srcpath, &st) == 0 && + S_ISDIR(st.st_mode) && + !directory_is_empty(srcpath)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot assign new default tablespace \"%s\"", + tablespacename), + errdetail("There is a conflict because database \"%s\" already has some tables in this tablespace.", + dbtemplate))); + pfree(srcpath); + } } else { @@ -311,11 +342,6 @@ createdb(const CreatedbStmt *stmt) /* * Iterate through all tablespaces of the template database, and copy * each one to the new database. - * - * If we are trying to change the default tablespace of the template, we - * require that the template not have any files in the new default - * tablespace. This avoids the need to merge two subdirectories. This - * could probably be improved later. */ rel = heap_openr(TableSpaceRelationName, AccessShareLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); @@ -333,7 +359,8 @@ createdb(const CreatedbStmt *stmt) srcpath = GetDatabasePath(src_dboid, srctablespace); - if (stat(srcpath, &st) < 0 || !S_ISDIR(st.st_mode)) + if (stat(srcpath, &st) < 0 || !S_ISDIR(st.st_mode) || + directory_is_empty(srcpath)) { /* Assume we can ignore it */ pfree(srcpath); @@ -352,7 +379,8 @@ createdb(const CreatedbStmt *stmt) remove_dbtablespaces(dboid); ereport(ERROR, (errmsg("could not initialize database directory"), - errdetail("Directory \"%s\" already exists.", dstpath))); + errdetail("Directory \"%s\" already exists.", + dstpath))); } #ifndef WIN32 diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index b9fbd2b5ea..1cbeef657a 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -45,7 +45,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.11 2004/08/30 02:54:38 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.12 2004/10/17 20:47:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -75,7 +75,6 @@ static bool remove_tablespace_directories(Oid tablespaceoid, bool redo); static void set_short_version(const char *path); -static bool directory_is_empty(const char *path); /* @@ -680,8 +679,10 @@ set_short_version(const char *path) /* * Check if a directory is empty. + * + * This probably belongs somewhere else, but not sure where... */ -static bool +bool directory_is_empty(const char *path) { DIR *dirdesc; diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h index 7e99c7e6b7..58ad23ae16 100644 --- a/src/include/commands/tablespace.h +++ b/src/include/commands/tablespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.5 2004/08/30 02:54:40 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/tablespace.h,v 1.6 2004/10/17 20:47:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,6 +42,8 @@ extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); extern Oid get_tablespace_oid(const char *tablespacename); extern char *get_tablespace_name(Oid spc_oid); +extern bool directory_is_empty(const char *path); + extern void tblspc_redo(XLogRecPtr lsn, XLogRecord *rptr); extern void tblspc_undo(XLogRecPtr lsn, XLogRecord *rptr); extern void tblspc_desc(char *buf, uint8 xl_info, char *rec); -- 2.11.0