Hi Hackers,
I have worked on that patch a little more. So now I have functional patch
(although still WIP) attached. The feature works as following:
- Added a boolean parameter "only_temp_files" to pg_tablespace.spcoptions;
- This parameter can be set to true only during CREATE TABLESPACE, not on
ALTER TABLESPACE (I have thought of ways of implementing the latter, and
I'd like to discuss it more latter);
- On the creation of relations, it is checked if it is a
temporary-tablespace, and an error occurs when it is and the relation is
not temporary (temp table or index on a temp table);
- When a temporary file (either relation file or sort/agg file) is created
inside a temporary-tablespace, the entire directories structure is created
on-demand (e.g. if pg_tblspc/<oid>/<TABLESPACE_VERSION_DIRECTORY> is
missing, it is created on demand) it is done on
OpenTemporaryFileInTablespace, at fd.c (I wonder if shouldn't we do that
for any tablespace) and on TablespaceCreateDbspace, at tablespace.c.
I still haven't change documentation, as I think I need some insights about
the changes. I have some more thoughts about the syntax and I still think
that "TEMP LOCATION" syntax is better suited for this patch. First because
of the nature of the changes I made, it seems more suitable to a column on
pg_tablespace rather than an option. Second because no ALTER is available
(so far) and I think it is odd to have an option that can't be changed.
Third, I think "TEMP" keyword is more clear and users can be more used to
it.
Thoughts?
I'm going to add the CF app entry next. Could I get some review now or
after discussion about how things are going (remember I'm a newbie on this,
so I'm a little lost)?
Regards,
--
Matheus de Oliveira
Analista de Banco de Dados
Dextra Sistemas - MPS.Br nĂvel F!
www.dextra.com.br/postgres
*** a/src/backend/access/common/reloptions.c
--- b/src/backend/access/common/reloptions.c
***************
*** 71,76 **** static relopt_bool boolRelOpts[] =
--- 71,84 ----
},
{
{
+ "only_temp_files",
+ "Allow only temporary files to be created on this tablespace",
+ RELOPT_KIND_TABLESPACE
+ },
+ false
+ },
+ {
+ {
"fastupdate",
"Enables \"fast update\" feature for this GIN index",
RELOPT_KIND_GIN
***************
*** 1337,1343 **** tablespace_reloptions(Datum reloptions, bool validate)
int numoptions;
static const relopt_parse_elt tab[] = {
{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
! {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)}
};
options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
--- 1345,1352 ----
int numoptions;
static const relopt_parse_elt tab[] = {
{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
! {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
! {"only_temp_files", RELOPT_TYPE_BOOL, offsetof(TableSpaceOpts, only_temp_files)}
};
options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
*** a/src/backend/commands/dbcommands.c
--- b/src/backend/commands/dbcommands.c
***************
*** 394,399 **** createdb(const CreatedbStmt *stmt)
--- 394,405 ----
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_global cannot be used as default tablespace")));
+ /* can't create a database on temporary tablespace */
+ if (is_tablespace_temp_only(dst_deftablespace))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this tablespace only allows temporary files")));
+
/*
* 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
***************
*** 1083,1088 **** movedb(const char *dbname, const char *tblspcname)
--- 1089,1100 ----
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_global cannot be used as default tablespace")));
+ /* can't create a database on temporary tablespace */
+ if (is_tablespace_temp_only(dst_tblspcoid))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this tablespace only allows temporary files")));
+
/*
* No-op if same tablespace
*/
*** a/src/backend/commands/indexcmds.c
--- b/src/backend/commands/indexcmds.c
***************
*** 432,437 **** DefineIndex(Oid relationId,
--- 432,446 ----
get_tablespace_name(tablespaceId));
}
+ /* Can't save relations on temporary tablespace */
+ if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+ is_tablespace_temp_only(OidIsValid(tablespaceId) ? tablespaceId : MyDatabaseTableSpace))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this tablespace only allows temporary files")));
+ }
+
/*
* Force shared indexes into the pg_global tablespace. This is a bit of a
* hack but seems simpler than marking them in the BKI commands. On the
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 523,528 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
--- 523,537 ----
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("only shared relations can be placed in pg_global tablespace")));
+ /* Can't save relations on temporary tablespace */
+ if (stmt->relation->relpersistence != RELPERSISTENCE_TEMP &&
+ is_tablespace_temp_only(OidIsValid(tablespaceId) ? tablespaceId : MyDatabaseTableSpace))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this tablespace only allows temporary files")));
+ }
+
/* Identify user ID that will own the table */
if (!OidIsValid(ownerId))
ownerId = GetUserId();
***************
*** 8824,8829 **** ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, L
--- 8833,8847 ----
aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename);
}
+ /* Can't save relations on temporary tablespace */
+ if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
+ is_tablespace_temp_only(OidIsValid(tablespaceId) ? tablespaceId : MyDatabaseTableSpace))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("this tablespace only allows temporary files")));
+ }
+
/* Save info for Phase 3 to do the real work */
if (OidIsValid(tab->newTableSpace))
ereport(ERROR,
*** a/src/backend/commands/tablespace.c
--- b/src/backend/commands/tablespace.c
***************
*** 81,86 ****
--- 81,87 ----
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/tqual.h"
+ #include "utils/spccache.h"
/* GUC variables */
***************
*** 154,170 **** TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo)
{
char *parentdir;
! /* Failure other than not exists or not in WAL replay? */
! if (errno != ENOENT || !isRedo)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m",
dir)));
/*
! * Parent directories are missing during WAL replay, so
! * continue by creating simple parent directories rather
! * than a symlink.
*/
/* create two parents up if not exist */
--- 155,172 ----
{
char *parentdir;
! /* Failure other than not exists or not in WAL replay with a non-temp tablespace? */
! if (errno != ENOENT || !( isRedo || is_tablespace_temp_only(spcNode) ) )
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m",
dir)));
/*
! * Parent directories are missing during WAL replay, and
! * they can be missing for temp tablespaces, so continue
! * by creating simple parent directories rather than a
! * symlink.
*/
/* create two parents up if not exist */
***************
*** 929,934 **** AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
--- 931,938 ----
Oid tablespaceoid;
Datum datum;
Datum newOptions;
+ TableSpaceOpts *tsopts;
+ TableSpaceOpts *tsoptsOld;
Datum repl_val[Natts_pg_tablespace];
bool isnull;
bool repl_null[Natts_pg_tablespace];
***************
*** 960,969 **** AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
/* Generate new proposed spcoptions (text array) */
datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
RelationGetDescr(rel), &isnull);
newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
stmt->options, NULL, NULL, false,
stmt->isReset);
! (void) tablespace_reloptions(newOptions, true);
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
--- 964,983 ----
/* Generate new proposed spcoptions (text array) */
datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
RelationGetDescr(rel), &isnull);
+ tsoptsOld = (TableSpaceOpts *) tablespace_reloptions(datum, false);
newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
stmt->options, NULL, NULL, false,
stmt->isReset);
! tsopts = (TableSpaceOpts *) tablespace_reloptions(newOptions, true);
!
! /* Can't save relations on temporary tablespace */
! if (tsopts->only_temp_files)
! {
! if (!tsoptsOld->only_temp_files)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("cannot alter a tablespace to become temporary")));
! }
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
*** a/src/backend/storage/file/fd.c
--- b/src/backend/storage/file/fd.c
***************
*** 76,81 ****
--- 76,82 ----
#include "storage/ipc.h"
#include "utils/guc.h"
#include "utils/resowner_private.h"
+ #include "utils/spccache.h"
/*
***************
*** 1132,1137 **** OpenTemporaryFileInTablespace(Oid tblspcOid, bool rejectError)
--- 1133,1156 ----
file = PathNameOpenFile(tempfilepath,
O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
0600);
+ /* On a temporary tablespace, we need to recreate its structure */
+ if (file <= 0 && is_tablespace_temp_only(tblspcOid))
+ {
+ /*
+ * XXX: Should we only do that for temp tablespace? Or blindly do for
+ * any tablespace?
+ */
+ char *parentdir;
+ parentdir = pstrdup(tempdirpath);
+ get_parent_directory(parentdir);
+ /* As above, don't check error for mkdir */
+ mkdir(parentdir, S_IRWXU);
+ pfree(parentdir);
+
+ file = PathNameOpenFile(tempfilepath,
+ O_RDWR | O_CREAT | O_TRUNC | PG_BINARY,
+ 0600);
+ }
if (file <= 0 && rejectError)
elog(ERROR, "could not create temporary file \"%s\": %m",
tempfilepath);
*** a/src/backend/utils/cache/spccache.c
--- b/src/backend/utils/cache/spccache.c
***************
*** 199,201 **** get_tablespace_page_costs(Oid spcid,
--- 199,230 ----
*spc_seq_page_cost = spc->opts->seq_page_cost;
}
}
+
+ /*
+ * is_tablespace_temp_only
+ * Return true if the tablespace only allows temporary files
+ */
+ bool
+ is_tablespace_temp_only(Oid spcid)
+ {
+ TableSpaceCacheEntry *spc;
+
+ /*
+ * pg_global and pg_default are never temporary, so no need to
+ * check the cache
+ */
+ if (spcid == GLOBALTABLESPACE_OID || spcid == DEFAULTTABLESPACE_OID)
+ return false;
+
+ spc = get_tablespace(spcid);
+
+ Assert(spc != NULL);
+
+ if (spc->opts == NULL)
+ {
+ /* no options, so this tablespace can't be considered temporary */
+ return false;
+ }
+
+ return spc->opts->only_temp_files;
+ }
*** a/src/include/commands/tablespace.h
--- b/src/include/commands/tablespace.h
***************
*** 37,42 **** typedef struct TableSpaceOpts
--- 37,43 ----
int32 vl_len_; /* varlena header (do not touch directly!) */
float8 random_page_cost;
float8 seq_page_cost;
+ bool only_temp_files;
} TableSpaceOpts;
extern Oid CreateTableSpace(CreateTableSpaceStmt *stmt);
*** a/src/include/utils/spccache.h
--- b/src/include/utils/spccache.h
***************
*** 15,19 ****
--- 15,20 ----
void get_tablespace_page_costs(Oid spcid, float8 *spc_random_page_cost,
float8 *spc_seq_page_cost);
+ bool is_tablespace_temp_only(Oid spcid);
#endif /* SPCCACHE_H */
*** a/src/test/regress/input/tablespace.source
--- b/src/test/regress/input/tablespace.source
***************
*** 81,89 **** ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
--- 81,133 ----
-- Should show notice that nothing was done
ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
+ -- Try changing only_temp_files
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = true); --fail
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = on); --fail
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = false); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = off); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ ALTER TABLESPACE testspace_renamed RESET (only_temp_files); --ok
+
-- Should succeed
DROP TABLESPACE testspace_renamed;
+ -- Now, let's create temporary tablespace
+ CREATE TABLESPACE testspace LOCATION '@testtablespace@' WITH (only_temp_files = true); -- ok
+ CREATE TABLE testschema.nontemp(a int) TABLESPACE testspace; -- fail
+ CREATE TABLE testschema.nontemp(a int); -- ok
+ ALTER TABLE testschema.nontemp SET TABLESPACE testspace; -- fail
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a) TABLESPACE testspace; -- fail
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a); -- ok
+ ALTER INDEX testschema.nontemp_idx SET TABLESPACE testspace; -- fail
+
+ -- Explicit created (on CREATE and ALTER)
+ CREATE TEMP TABLE temptbl(a int) TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl(a);
+ CREATE TEMP TABLE temptbl2(a int);
+ CREATE INDEX temptbl2_idx ON temptbl2(a);
+ ALTER TABLE temptbl2 SET TABLESPACE testspace;
+ ALTER INDEX temptbl2_idx SET TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx', 'temptbl2', 'temptbl2_idx');
+ DROP TABLE temptbl;
+ DROP TABLE temptbl2;
+
+ -- Use temp_tablespaces
+ SET temp_tablespaces TO 'testspace';
+ CREATE TEMP TABLE temptbl(a int);
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx');
+
+ -- Use this tablespace in a sort operation, and check if any files on pgsql_tmp has been used
+ SET work_mem TO '1MB';
+ WITH o AS (SELECT i, md5(random()::text) FROM generate_series(1, 100000) i ORDER BY md5) SELECT count(*) > 0 FROM (SELECT pg_ls_dir('pg_tblspc/' || oid || '/' || pg_ls_dir('pg_tblspc/' || oid || '/') || '/pgsql_tmp/') FROM pg_tablespace, (SELECT count(*) FROM o) t1 WHERE spcname = 'testspace') t2;
+
+ -- Should succeed
DROP SCHEMA testschema CASCADE;
DROP ROLE tablespace_testuser1;
*** a/src/test/regress/output/tablespace.source
--- b/src/test/regress/output/tablespace.source
***************
*** 97,109 **** ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
-- Should show notice that nothing was done
ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
NOTICE: no matching relations in tablespace "testspace_renamed" found
-- Should succeed
DROP TABLESPACE testspace_renamed;
DROP SCHEMA testschema CASCADE;
! NOTICE: drop cascades to 4 other objects
DETAIL: drop cascades to table testschema.foo
drop cascades to table testschema.asselect
drop cascades to table testschema.asexecute
drop cascades to table testschema.atable
DROP ROLE tablespace_testuser1;
DROP ROLE tablespace_testuser2;
--- 97,189 ----
-- Should show notice that nothing was done
ALTER TABLESPACE testspace_renamed MOVE ALL TO pg_default;
NOTICE: no matching relations in tablespace "testspace_renamed" found
+ -- Try changing only_temp_files
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = true); --fail
+ ERROR: cannot alter a tablespace to become temporary
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = on); --fail
+ ERROR: cannot alter a tablespace to become temporary
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ spcoptions
+ ------------
+
+ (1 row)
+
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = false); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ spcoptions
+ -------------------------
+ {only_temp_files=false}
+ (1 row)
+
+ ALTER TABLESPACE testspace_renamed SET (only_temp_files = off); --ok, already non-temporary, just explicit set
+ SELECT spcoptions FROM pg_tablespace WHERE spcname = 'testspace_renamed';
+ spcoptions
+ -----------------------
+ {only_temp_files=off}
+ (1 row)
+
+ ALTER TABLESPACE testspace_renamed RESET (only_temp_files); --ok
-- Should succeed
DROP TABLESPACE testspace_renamed;
+ -- Now, let's create temporary tablespace
+ CREATE TABLESPACE testspace LOCATION '@testtablespace@' WITH (only_temp_files = true); -- ok
+ CREATE TABLE testschema.nontemp(a int) TABLESPACE testspace; -- fail
+ ERROR: this tablespace only allows temporary files
+ CREATE TABLE testschema.nontemp(a int); -- ok
+ ALTER TABLE testschema.nontemp SET TABLESPACE testspace; -- fail
+ ERROR: this tablespace only allows temporary files
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a) TABLESPACE testspace; -- fail
+ ERROR: this tablespace only allows temporary files
+ CREATE INDEX nontemp_idx ON testschema.nontemp(a); -- ok
+ ALTER INDEX testschema.nontemp_idx SET TABLESPACE testspace; -- fail
+ ERROR: this tablespace only allows temporary files
+ -- Explicit created (on CREATE and ALTER)
+ CREATE TEMP TABLE temptbl(a int) TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl(a);
+ CREATE TEMP TABLE temptbl2(a int);
+ CREATE INDEX temptbl2_idx ON temptbl2(a);
+ ALTER TABLE temptbl2 SET TABLESPACE testspace;
+ ALTER INDEX temptbl2_idx SET TABLESPACE testspace;
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ ERROR: relation "temptbl_idx" already exists
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx', 'temptbl2', 'temptbl2_idx');
+ relname | spcname
+ --------------+-----------
+ temptbl | testspace
+ temptbl2_idx | testspace
+ temptbl2 | testspace
+ (3 rows)
+
+ DROP TABLE temptbl;
+ DROP TABLE temptbl2;
+ -- Use temp_tablespaces
+ SET temp_tablespaces TO 'testspace';
+ CREATE TEMP TABLE temptbl(a int);
+ CREATE INDEX temptbl_idx ON temptbl (a);
+ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
+ where c.reltablespace = t.oid AND c.relnamespace = pg_my_temp_schema() AND c.relname IN ('temptbl', 'temptbl_idx');
+ relname | spcname
+ -------------+-----------
+ temptbl | testspace
+ temptbl_idx | testspace
+ (2 rows)
+
+ -- Use this tablespace in a sort operation, and check if any files on pgsql_tmp has been used
+ SET work_mem TO '1MB';
+ WITH o AS (SELECT i, md5(random()::text) FROM generate_series(1, 100000) i ORDER BY md5) SELECT count(*) > 0 FROM (SELECT pg_ls_dir('pg_tblspc/' || oid || '/' || pg_ls_dir('pg_tblspc/' || oid || '/') || '/pgsql_tmp/') FROM pg_tablespace, (SELECT count(*) FROM o) t1 WHERE spcname = 'testspace') t2;
+ ?column?
+ ----------
+ t
+ (1 row)
+
+ -- Should succeed
DROP SCHEMA testschema CASCADE;
! NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table testschema.foo
drop cascades to table testschema.asselect
drop cascades to table testschema.asexecute
drop cascades to table testschema.atable
+ drop cascades to table testschema.nontemp
DROP ROLE tablespace_testuser1;
DROP ROLE tablespace_testuser2;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers