diff options
Diffstat (limited to 'src/backend/commands/tablespace.c')
-rw-r--r-- | src/backend/commands/tablespace.c | 229 |
1 files changed, 130 insertions, 99 deletions
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 8201a4f3341..d0dacf10782 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.47 2007/06/03 17:06:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablespace.c,v 1.48 2007/06/07 19:19:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -63,6 +63,7 @@ #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" /* GUC variables */ @@ -72,7 +73,6 @@ char *temp_tablespaces = NULL; static bool remove_tablespace_directories(Oid tablespaceoid, bool redo); static void set_short_version(const char *path); -static Oid getTempTablespace(void); /* @@ -921,9 +921,12 @@ GetDefaultTablespace(bool forTemp) { Oid result; - /* The temp-table case is handled by getTempTablespace() */ + /* The temp-table case is handled elsewhere */ if (forTemp) - return getTempTablespace(); + { + PrepareTempTablespaces(); + return GetNextTempTableSpace(); + } /* Fast path for default_tablespace == "" */ if (default_tablespace == NULL || default_tablespace[0] == '\0') @@ -958,7 +961,6 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source) { char *rawname; List *namelist; - ListCell *l; /* Need a modifiable copy of string */ rawname = pstrdup(newval); @@ -975,24 +977,79 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source) /* * If we aren't inside a transaction, we cannot do database access so * cannot verify the individual names. Must accept the list on faith. + * Fortunately, there's then also no need to pass the data to fd.c. */ - if (source >= PGC_S_INTERACTIVE && IsTransactionState()) + if (IsTransactionState()) { + /* + * If we error out below, or if we are called multiple times in one + * transaction, we'll leak a bit of TopTransactionContext memory. + * Doesn't seem worth worrying about. + */ + Oid *tblSpcs; + int numSpcs; + ListCell *l; + + tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext, + list_length(namelist) * sizeof(Oid)); + numSpcs = 0; foreach(l, namelist) { char *curname = (char *) lfirst(l); + Oid curoid; + AclResult aclresult; /* Allow an empty string (signifying database default) */ if (curname[0] == '\0') + { + tblSpcs[numSpcs++] = InvalidOid; continue; + } /* Else verify that name is a valid tablespace name */ - if (get_tablespace_oid(curname) == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("tablespace \"%s\" does not exist", - curname))); + curoid = get_tablespace_oid(curname); + if (curoid == InvalidOid) + { + /* + * In an interactive SET command, we ereport for bad info. + * Otherwise, silently ignore any bad list elements. + */ + if (source >= PGC_S_INTERACTIVE) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace \"%s\" does not exist", + curname))); + continue; + } + + /* + * Allow explicit specification of database's default tablespace + * in temp_tablespaces without triggering permissions checks. + */ + if (curoid == MyDatabaseTableSpace) + { + tblSpcs[numSpcs++] = InvalidOid; + continue; + } + + /* Check permissions similarly */ + aclresult = pg_tablespace_aclcheck(curoid, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + { + if (source >= PGC_S_INTERACTIVE) + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, curname); + continue; + } + + tblSpcs[numSpcs++] = curoid; } + + /* If actively "doing it", give the new list to fd.c */ + if (doit) + SetTempTablespaces(tblSpcs, numSpcs); + else + pfree(tblSpcs); } pfree(rawname); @@ -1002,69 +1059,34 @@ assign_temp_tablespaces(const char *newval, bool doit, GucSource source) } /* - * GetTempTablespace -- get the OID of the next temp tablespace to use - * - * May return InvalidOid to indicate "use the database's default tablespace". + * PrepareTempTablespaces -- prepare to use temp tablespaces * - * This is different from GetDefaultTablespace(true) in just two ways: - * 1. We check privileges here instead of leaving it to the caller. - * 2. It's safe to call this outside a transaction (we just return InvalidOid). - * The transaction state check is used so that this can be called from - * low-level places that might conceivably run outside a transaction. + * If we have not already done so in the current transaction, parse the + * temp_tablespaces GUC variable and tell fd.c which tablespace(s) to use + * for temp files. */ -Oid -GetTempTablespace(void) -{ - Oid result; - - /* Can't do catalog access unless within a transaction */ - if (!IsTransactionState()) - return InvalidOid; - - /* OK, select a temp tablespace */ - result = getTempTablespace(); - - /* Check permissions except when using database's default */ - if (OidIsValid(result)) - { - AclResult aclresult; - - aclresult = pg_tablespace_aclcheck(result, GetUserId(), - ACL_CREATE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_TABLESPACE, - get_tablespace_name(result)); - } - - return result; -} - -/* - * getTempTablespace -- get the OID of the next temp tablespace to use - * - * This has exactly the API defined for GetDefaultTablespace(true), - * in particular that caller is responsible for permissions checks. - * - * This exists to hide (and possibly optimize the use of) the - * temp_tablespaces GUC variable. - */ -static Oid -getTempTablespace(void) +void +PrepareTempTablespaces(void) { - Oid result; char *rawname; List *namelist; - int nnames; - char *curname; + Oid *tblSpcs; + int numSpcs; + ListCell *l; - if (temp_tablespaces == NULL) - return InvalidOid; + /* No work if already done in current transaction */ + if (TempTablespacesAreSet()) + return; /* - * We re-parse the string on each call; this is a bit expensive, but - * we don't expect this function will be called many times per query, - * so it's probably not worth being tenser. + * Can't do catalog access unless within a transaction. This is just + * a safety check in case this function is called by low-level code that + * could conceivably execute outside a transaction. Note that in such + * a scenario, fd.c will fall back to using the current database's default + * tablespace, which should always be OK. */ + if (!IsTransactionState()) + return; /* Need a modifiable copy of string */ rawname = pstrdup(temp_tablespaces); @@ -1073,51 +1095,60 @@ getTempTablespace(void) if (!SplitIdentifierString(rawname, ',', &namelist)) { /* syntax error in name list */ + SetTempTablespaces(NULL, 0); pfree(rawname); list_free(namelist); - return InvalidOid; + return; } - nnames = list_length(namelist); - /* Fast path for temp_tablespaces == "" */ - if (nnames == 0) + /* Store tablespace OIDs in an array in TopTransactionContext */ + tblSpcs = (Oid *) MemoryContextAlloc(TopTransactionContext, + list_length(namelist) * sizeof(Oid)); + numSpcs = 0; + foreach(l, namelist) { - pfree(rawname); - list_free(namelist); - return InvalidOid; - } + char *curname = (char *) lfirst(l); + Oid curoid; + AclResult aclresult; - /* Select a random element */ - if (nnames == 1) /* no need for a random() call */ - curname = (char *) linitial(namelist); - else - curname = (char *) list_nth(namelist, random() % nnames); + /* Allow an empty string (signifying database default) */ + if (curname[0] == '\0') + { + tblSpcs[numSpcs++] = InvalidOid; + continue; + } - /* - * Empty string means "database's default", else look up the tablespace. - * - * It is tempting to cache this lookup for more speed, but then we would - * fail to detect the case where the tablespace was dropped since the GUC - * variable was set. Note also that we don't complain if the value fails - * to refer to an existing tablespace; we just silently return InvalidOid, - * causing the new object to be created in the database's tablespace. - */ - if (curname[0] == '\0') - result = InvalidOid; - else - result = get_tablespace_oid(curname); + /* Else verify that name is a valid tablespace name */ + curoid = get_tablespace_oid(curname); + if (curoid == InvalidOid) + { + /* Silently ignore any bad list elements */ + continue; + } - /* - * Allow explicit specification of database's default tablespace in - * temp_tablespaces without triggering permissions checks. - */ - if (result == MyDatabaseTableSpace) - result = InvalidOid; + /* + * Allow explicit specification of database's default tablespace + * in temp_tablespaces without triggering permissions checks. + */ + if (curoid == MyDatabaseTableSpace) + { + tblSpcs[numSpcs++] = InvalidOid; + continue; + } + + /* Check permissions similarly */ + aclresult = pg_tablespace_aclcheck(curoid, GetUserId(), + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + continue; + + tblSpcs[numSpcs++] = curoid; + } + + SetTempTablespaces(tblSpcs, numSpcs); pfree(rawname); list_free(namelist); - - return result; } |