mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 02:23:49 +08:00
Refactor tablespace handling
Attaching tablespaces to hypertables is now handled in native code, with improved permissions checking and caching of tablespaces in the Hypertable data object.
This commit is contained in:
parent
c4a46ac8a1
commit
e593876cb0
@ -34,3 +34,7 @@ FOR EACH STATEMENT EXECUTE PROCEDURE _timescaledb_cache.invalidate_relcache_trig
|
||||
DROP TRIGGER IF EXISTS "0_cache_inval" ON _timescaledb_catalog.dimension;
|
||||
CREATE TRIGGER "0_cache_inval" AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON _timescaledb_catalog.dimension
|
||||
FOR EACH STATEMENT EXECUTE PROCEDURE _timescaledb_cache.invalidate_relcache_trigger();
|
||||
|
||||
DROP TRIGGER IF EXISTS "0_cache_inval" ON _timescaledb_catalog.tablespace;
|
||||
CREATE TRIGGER "0_cache_inval" AFTER INSERT OR UPDATE OR DELETE OR TRUNCATE ON _timescaledb_catalog.tablespace
|
||||
FOR EACH STATEMENT EXECUTE PROCEDURE _timescaledb_cache.invalidate_relcache_trigger();
|
||||
|
@ -298,33 +298,8 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION attach_tablespace(
|
||||
hypertable REGCLASS,
|
||||
tablespace NAME
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL VOLATILE
|
||||
SECURITY DEFINER SET search_path = ''
|
||||
AS
|
||||
CREATE OR REPLACE FUNCTION attach_tablespace(tablespace NAME, hypertable REGCLASS)
|
||||
RETURNS VOID LANGUAGE SQL AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
hypertable_id INTEGER;
|
||||
tablespace_oid OID;
|
||||
BEGIN
|
||||
PERFORM _timescaledb_internal.check_role(hypertable);
|
||||
|
||||
SELECT id
|
||||
FROM _timescaledb_catalog.hypertable h, pg_class c, pg_namespace n
|
||||
WHERE h.schema_name = n.nspname
|
||||
AND h.table_name = c.relname
|
||||
AND c.oid = hypertable
|
||||
AND n.oid = c.relnamespace
|
||||
INTO hypertable_id;
|
||||
|
||||
IF hypertable_id IS NULL THEN
|
||||
RAISE EXCEPTION 'No hypertable "%" exists', main_table_name
|
||||
USING ERRCODE = 'IO101';
|
||||
END IF;
|
||||
|
||||
PERFORM _timescaledb_internal.attach_tablespace(hypertable_id, tablespace);
|
||||
END
|
||||
SELECT * FROM _timescaledb_internal.attach_tablespace(tablespace, hypertable);
|
||||
$BODY$;
|
||||
|
@ -80,11 +80,6 @@ BEGIN
|
||||
)
|
||||
RETURNING * INTO hypertable_row;
|
||||
|
||||
--add default tablespace, if any
|
||||
IF tablespace IS NOT NULL THEN
|
||||
PERFORM _timescaledb_internal.attach_tablespace(hypertable_row.id, tablespace);
|
||||
END IF;
|
||||
|
||||
--create time dimension
|
||||
PERFORM _timescaledb_internal.add_dimension(main_table,
|
||||
hypertable_row,
|
||||
@ -112,6 +107,11 @@ BEGIN
|
||||
PERFORM _timescaledb_internal.create_default_indexes(hypertable_row, main_table, partitioning_column);
|
||||
END IF;
|
||||
|
||||
--add default tablespace, if any
|
||||
IF tablespace IS NOT NULL THEN
|
||||
PERFORM _timescaledb_internal.attach_tablespace(tablespace, main_table);
|
||||
END IF;
|
||||
|
||||
RETURN hypertable_row;
|
||||
END
|
||||
$BODY$;
|
||||
@ -618,6 +618,8 @@ BEGIN
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.attach_tablespace(tablespace NAME, hypertable REGCLASS) RETURNS VOID
|
||||
AS '$libdir/timescaledb', 'tablespace_attach' LANGUAGE C VOLATILE;
|
||||
|
||||
--documentation of these function located in chunk_index.h
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_index_clone(chunk_index_oid OID) RETURNS OID
|
||||
|
@ -88,34 +88,3 @@ $BODY$
|
||||
(SELECT hypertable_id FROM _timescaledb_catalog.chunk WHERE id = chunk_id),
|
||||
chunk_id);
|
||||
$BODY$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.attach_tablespace(
|
||||
hypertable_id INTEGER,
|
||||
tablespace_name NAME
|
||||
)
|
||||
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
|
||||
$BODY$
|
||||
DECLARE
|
||||
tablespace_oid OID;
|
||||
BEGIN
|
||||
SELECT oid
|
||||
FROM pg_catalog.pg_tablespace
|
||||
WHERE spcname = tablespace_name
|
||||
INTO tablespace_oid;
|
||||
|
||||
IF tablespace_oid IS NULL THEN
|
||||
RAISE EXCEPTION 'No tablespace "%" exists. A tablespace needs to be created before assigning it to a hypertable dimension', tablespace_name
|
||||
USING ERRCODE = 'IO101';
|
||||
END IF;
|
||||
|
||||
BEGIN
|
||||
INSERT INTO _timescaledb_catalog.tablespace (hypertable_id, tablespace_name)
|
||||
VALUES (hypertable_id, tablespace_name);
|
||||
EXCEPTION
|
||||
WHEN unique_violation THEN
|
||||
RAISE EXCEPTION 'Tablespace "%" already assigned to hypertable "%"',
|
||||
tablespace_name, (SELECT table_name FROM _timescaledb_catalog.hypertable
|
||||
WHERE id = hypertable_id);
|
||||
END;
|
||||
END
|
||||
$BODY$;
|
||||
|
@ -35,3 +35,6 @@ DROP FUNCTION create_hypertable(regclass,name,name,integer,name,name,anyelement,
|
||||
DROP FUNCTION add_dimension(regclass,name,integer,bigint);
|
||||
DROP FUNCTION _timescaledb_internal.create_hypertable_row(regclass,name,name,name,name,integer,name,name,bigint,name);
|
||||
DROP FUNCTION _timescaledb_internal.add_dimension(regclass,_timescaledb_catalog.hypertable,name,integer,bigint,boolean);
|
||||
|
||||
-- Tablespace functions
|
||||
DROP FUNCTION _timescaledb_internal.attach_tablespace(integer, name);
|
||||
|
@ -1,6 +1,11 @@
|
||||
DROP FUNCTION _timescaledb_cache.invalidate_relcache(oid);
|
||||
|
||||
|
||||
DROP FUNCTION set_chunk_time_interval(REGCLASS, BIGINT);
|
||||
DROP FUNCTION add_dimension(REGCLASS, NAME, INTEGER, BIGINT, REGPROC);
|
||||
DROP FUNCTION _timescaledb_internal.add_dimension(REGCLASS, _timescaledb_catalog.hypertable, NAME, INTEGER, BIGINT, REGPROC, BOOLEAN);
|
||||
DROP FUNCTION _timescaledb_internal.time_interval_specification_to_internal(REGTYPE, anyelement, INTERVAL, TEXT);
|
||||
|
||||
-- Tablespace changes
|
||||
DROP FUNCTION _timescaledb_internal.attach_tablespace(integer, name);
|
||||
DROP FUNCTION attach_tablespace(regclass, name);
|
||||
|
@ -63,6 +63,7 @@ set(HEADERS
|
||||
process_utility.h
|
||||
scanner.h
|
||||
subspace_store.h
|
||||
tablespace.h
|
||||
trigger.h
|
||||
utils.h)
|
||||
|
||||
@ -105,6 +106,7 @@ set(SOURCES
|
||||
scanner.c
|
||||
sort_transform.c
|
||||
subspace_store.c
|
||||
tablespace.c
|
||||
trigger.c
|
||||
utils.c
|
||||
version.c)
|
||||
|
@ -28,6 +28,7 @@ static const char *catalog_table_names[_MAX_CATALOG_TABLES + 1] = {
|
||||
[CHUNK] = CHUNK_TABLE_NAME,
|
||||
[CHUNK_CONSTRAINT] = CHUNK_CONSTRAINT_TABLE_NAME,
|
||||
[CHUNK_INDEX] = CHUNK_INDEX_TABLE_NAME,
|
||||
[TABLESPACE] = TABLESPACE_TABLE_NAME,
|
||||
[_MAX_CATALOG_TABLES] = "invalid table",
|
||||
};
|
||||
|
||||
@ -79,6 +80,13 @@ static const TableIndexDef catalog_table_index_definitions[_MAX_CATALOG_TABLES]
|
||||
[CHUNK_INDEX_CHUNK_ID_INDEX_NAME_IDX] = "chunk_index_chunk_id_index_name_key",
|
||||
[CHUNK_INDEX_HYPERTABLE_ID_HYPERTABLE_INDEX_NAME_IDX] = "chunk_index_hypertable_id_hypertable_index_name_idx",
|
||||
}
|
||||
},
|
||||
[TABLESPACE] = {
|
||||
.length = _MAX_TABLESPACE_INDEX,
|
||||
.names = (char *[]) {
|
||||
[TABLESPACE_PKEY_IDX] = "tablespace_pkey",
|
||||
[TABLESPACE_HYPERTABLE_ID_TABLESPACE_NAME_IDX] = "tablespace_hypertable_id_tablespace_name_key",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -88,6 +96,8 @@ static const char *catalog_table_serial_id_names[_MAX_CATALOG_TABLES] = {
|
||||
[DIMENSION_SLICE] = CATALOG_SCHEMA_NAME ".dimension_slice_id_seq",
|
||||
[CHUNK] = CATALOG_SCHEMA_NAME ".chunk_id_seq",
|
||||
[CHUNK_CONSTRAINT] = NULL,
|
||||
[CHUNK_INDEX] = NULL,
|
||||
[TABLESPACE] = CATALOG_SCHEMA_NAME ".tablespace_id_seq",
|
||||
};
|
||||
|
||||
typedef struct InternalFunctionDef
|
||||
|
@ -29,6 +29,7 @@ typedef enum CatalogTable
|
||||
CHUNK,
|
||||
CHUNK_CONSTRAINT,
|
||||
CHUNK_INDEX,
|
||||
TABLESPACE,
|
||||
_MAX_CATALOG_TABLES,
|
||||
} CatalogTable;
|
||||
|
||||
@ -375,7 +376,6 @@ enum Anum_chunk_index_chunk_id_index_name_idx
|
||||
_Anum_chunk_index_chunk_id_index_name_idx_max,
|
||||
};
|
||||
|
||||
|
||||
enum Anum_chunk_index_hypertable_id_hypertable_index_name_idx
|
||||
{
|
||||
Anum_chunk_index_hypertable_id_hypertable_index_name_idx_hypertable_id = 1,
|
||||
@ -383,6 +383,66 @@ enum Anum_chunk_index_hypertable_id_hypertable_index_name_idx
|
||||
Anum_chunk_index_hypertable_id_hypertable_index_name_idx_max,
|
||||
};
|
||||
|
||||
/************************************
|
||||
*
|
||||
* Tablespace table definitions
|
||||
*
|
||||
************************************/
|
||||
|
||||
#define TABLESPACE_TABLE_NAME "tablespace"
|
||||
|
||||
enum Anum_tablespace
|
||||
{
|
||||
Anum_tablespace_id = 1,
|
||||
Anum_tablespace_hypertable_id,
|
||||
Anum_tablespace_tablespace_name,
|
||||
_Anum_tablespace_max,
|
||||
};
|
||||
|
||||
#define Natts_tablespace \
|
||||
(_Anum_tablespace_max - 1)
|
||||
|
||||
typedef struct FormData_tablespace
|
||||
{
|
||||
int32 id;
|
||||
int32 hypertable_id;
|
||||
NameData tablespace_name;
|
||||
} FormData_tablespace;
|
||||
|
||||
typedef FormData_tablespace *Form_tablespace;
|
||||
|
||||
enum
|
||||
{
|
||||
TABLESPACE_PKEY_IDX = 0,
|
||||
TABLESPACE_HYPERTABLE_ID_TABLESPACE_NAME_IDX,
|
||||
_MAX_TABLESPACE_INDEX,
|
||||
};
|
||||
|
||||
enum Anum_tablespace_pkey_idx
|
||||
{
|
||||
Anum_tablespace_pkey_idx_tablespace_id = 1,
|
||||
_Anum_tablespace_pkey_idx_max,
|
||||
};
|
||||
|
||||
typedef struct FormData_tablespace_pkey_idx
|
||||
{
|
||||
int32 tablespace_id;
|
||||
} FormData_tablespace_pkey_idx;
|
||||
|
||||
enum Anum_tablespace_hypertable_id_tablespace_name_idx
|
||||
{
|
||||
Anum_tablespace_hypertable_id_tablespace_name_idx_hypertable_id = 1,
|
||||
Anum_tablespace_hypertable_id_tablespace_name_idx_tablespace_name,
|
||||
_Anum_tablespace_hypertable_id_tablespace_name_idx_max,
|
||||
};
|
||||
|
||||
typedef struct FormData_tablespace_hypertable_id_tablespace_name_idx
|
||||
{
|
||||
int32 hypertable_id;
|
||||
NameData tablespace_name;
|
||||
} FormData_tablespace_hypertable_id_tablespace_name_idx;
|
||||
|
||||
|
||||
#define MAX(a, b) \
|
||||
((long)(a) > (long)(b) ? (a) : (b))
|
||||
|
||||
@ -392,7 +452,8 @@ enum Anum_chunk_index_hypertable_id_hypertable_index_name_idx
|
||||
MAX(_MAX_DIMENSION_SLICE_INDEX, \
|
||||
MAX(_MAX_CHUNK_CONSTRAINT_INDEX, \
|
||||
MAX(_MAX_CHUNK_INDEX_INDEX, \
|
||||
_MAX_CHUNK_INDEX)))))
|
||||
MAX(_MAX_TABLESPACE_INDEX, \
|
||||
_MAX_CHUNK_INDEX))))))
|
||||
|
||||
typedef enum CacheType
|
||||
{
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define ERRCODE_IO_HYPERTABLE_EXISTS MAKE_SQLSTATE('I','O','1','1','0')
|
||||
#define ERRCODE_IO_NODE_EXISTS MAKE_SQLSTATE('I','O','1','2','0')
|
||||
#define ERRCODE_IO_USER_EXISTS MAKE_SQLSTATE('I','O','1','3','0')
|
||||
#define ERRCODE_IO_TABLESPACE_ALREADY_ATTACHED MAKE_SQLSTATE('I','O','1','4','0')
|
||||
|
||||
/*
|
||||
--IO500 - GROUP: internal error
|
||||
|
@ -28,6 +28,7 @@ hypertable_from_tuple(HeapTuple tuple)
|
||||
h->main_table_relid = get_relname_relid(NameStr(h->fd.table_name), namespace_oid);
|
||||
h->space = dimension_scan(h->fd.id, h->main_table_relid, h->fd.num_dimensions);
|
||||
h->chunk_cache = subspace_store_init(h->space->num_dimensions, CurrentMemoryContext);
|
||||
h->tablespaces = tablespace_scan(h->fd.id);
|
||||
|
||||
return h;
|
||||
}
|
||||
@ -213,6 +214,25 @@ hypertable_get_chunk(Hypertable *h, Point *point)
|
||||
return cce->chunk;
|
||||
}
|
||||
|
||||
bool
|
||||
hypertable_has_tablespace(Hypertable *ht, Oid tspc_oid)
|
||||
{
|
||||
Tablespaces *tspcs = ht->tablespaces;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tspcs->num_tablespaces; i++)
|
||||
if (tspc_oid == tspcs->tablespaces[i].tablespace_oid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Tablespace *
|
||||
hypertable_add_tablespace(Hypertable *ht, int32 tspc_id, Oid tspc_oid)
|
||||
{
|
||||
return tablespaces_add(ht->tablespaces, tspc_id, tspc_oid);
|
||||
}
|
||||
|
||||
static inline Oid
|
||||
hypertable_relid_lookup(Oid relid)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "catalog.h"
|
||||
#include "dimension.h"
|
||||
#include "tablespace.h"
|
||||
|
||||
typedef struct SubspaceStore SubspaceStore;
|
||||
typedef struct Chunk Chunk;
|
||||
@ -17,6 +18,7 @@ typedef struct Hypertable
|
||||
Oid main_table_relid;
|
||||
Hyperspace *space;
|
||||
SubspaceStore *chunk_cache;
|
||||
Tablespaces *tablespaces;
|
||||
} Hypertable;
|
||||
|
||||
extern Hypertable *hypertable_from_tuple(HeapTuple tuple);
|
||||
@ -26,5 +28,7 @@ extern Oid hypertable_id_to_relid(int32 hypertable_id);
|
||||
extern Chunk *hypertable_get_chunk(Hypertable *h, Point *point);
|
||||
extern Oid hypertable_relid(RangeVar *rv);
|
||||
extern bool is_hypertable(Oid relid);
|
||||
extern bool hypertable_has_tablespace(Hypertable *ht, Oid tspc_oid);
|
||||
extern Tablespace *hypertable_add_tablespace(Hypertable *ht, int32 tspc_id, Oid tspc_oid);
|
||||
|
||||
#endif /* TIMESCALEDB_HYPERTABLE_H */
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "utils.h"
|
||||
#include "scanner.h"
|
||||
#include "dimension.h"
|
||||
#include "tablespace.h"
|
||||
|
||||
static void *hypertable_cache_create_entry(Cache *cache, CacheQuery *query);
|
||||
|
||||
|
@ -1594,7 +1594,7 @@ timescaledb_ddl_command_start(
|
||||
ProcessUtilityContext context,
|
||||
ParamListInfo params,
|
||||
#if PG10
|
||||
QueryEnvironment *queryEnv,
|
||||
QueryEnvironment * queryEnv,
|
||||
#endif
|
||||
DestReceiver *dest,
|
||||
char *completion_tag)
|
||||
|
224
src/tablespace.c
Normal file
224
src/tablespace.c
Normal file
@ -0,0 +1,224 @@
|
||||
#include <postgres.h>
|
||||
#include <fmgr.h>
|
||||
#include <utils/lsyscache.h>
|
||||
#include <utils/syscache.h>
|
||||
#include <utils/spccache.h>
|
||||
#include <utils/acl.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <utils/fmgroids.h>
|
||||
#include <commands/tablespace.h>
|
||||
#include <miscadmin.h>
|
||||
|
||||
#include "hypertable_cache.h"
|
||||
#include "errors.h"
|
||||
#include "catalog.h"
|
||||
#include "scanner.h"
|
||||
#include "tablespace.h"
|
||||
#include "compat.h"
|
||||
|
||||
#define TABLESPACE_DEFAULT_CAPACITY 4
|
||||
|
||||
static Oid
|
||||
rel_get_owner(Oid relid)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Oid ownerid;
|
||||
|
||||
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_TABLE),
|
||||
errmsg("relation with OID %u does not exist", relid)));
|
||||
|
||||
ownerid = ((Form_pg_class) GETSTRUCT(tuple))->relowner;
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
return ownerid;
|
||||
}
|
||||
|
||||
static Tablespaces *
|
||||
tablespaces_alloc(int capacity)
|
||||
{
|
||||
Tablespaces *tspcs;
|
||||
|
||||
tspcs = palloc(sizeof(Tablespaces));
|
||||
tspcs->capacity = capacity;
|
||||
tspcs->num_tablespaces = 0;
|
||||
tspcs->tablespaces = palloc(sizeof(Tablespace) * tspcs->capacity);
|
||||
|
||||
return tspcs;
|
||||
}
|
||||
|
||||
Tablespace *
|
||||
tablespaces_add(Tablespaces *tspcs, int32 tspc_id, Oid tspc_oid)
|
||||
{
|
||||
Tablespace *tspc;
|
||||
|
||||
if (tspcs->num_tablespaces >= tspcs->capacity)
|
||||
{
|
||||
tspcs->capacity += TABLESPACE_DEFAULT_CAPACITY;
|
||||
tspcs->tablespaces = repalloc(tspcs->tablespaces, sizeof(Tablespace) * tspcs->capacity);
|
||||
}
|
||||
|
||||
tspc = &tspcs->tablespaces[tspcs->num_tablespaces++];
|
||||
tspc->tablespace_id = tspc_id;
|
||||
tspc->tablespace_oid = tspc_oid;
|
||||
|
||||
return tspc;
|
||||
}
|
||||
|
||||
static bool
|
||||
tablespace_tuple_found(TupleInfo *ti, void *data)
|
||||
{
|
||||
Tablespaces *tspcs = data;
|
||||
FormData_tablespace *form = (FormData_tablespace *) GETSTRUCT(ti->tuple);
|
||||
|
||||
tablespaces_add(tspcs,
|
||||
form->id,
|
||||
get_tablespace_oid(NameStr(form->tablespace_name), true));
|
||||
return true;
|
||||
}
|
||||
|
||||
Tablespaces *
|
||||
tablespace_scan(int32 hypertable_id)
|
||||
{
|
||||
Catalog *catalog = catalog_get();
|
||||
Tablespaces *tspcs = tablespaces_alloc(TABLESPACE_DEFAULT_CAPACITY);
|
||||
ScanKeyData scankey[1];
|
||||
ScannerCtx scanctx = {
|
||||
.table = catalog->tables[TABLESPACE].id,
|
||||
.index = catalog->tables[TABLESPACE].index_ids[TABLESPACE_HYPERTABLE_ID_TABLESPACE_NAME_IDX],
|
||||
.scantype = ScannerTypeIndex,
|
||||
.nkeys = 1,
|
||||
.scankey = scankey,
|
||||
.data = tspcs,
|
||||
.tuple_found = tablespace_tuple_found,
|
||||
.lockmode = AccessShareLock,
|
||||
.scandirection = ForwardScanDirection,
|
||||
};
|
||||
|
||||
ScanKeyInit(&scankey[0], Anum_tablespace_hypertable_id_tablespace_name_idx_hypertable_id,
|
||||
BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(hypertable_id));
|
||||
|
||||
scanner_scan(&scanctx);
|
||||
|
||||
return tspcs;
|
||||
}
|
||||
|
||||
static int32
|
||||
tablespace_insert_relation(Relation rel, int32 hypertable_id, const char *tspcname)
|
||||
{
|
||||
TupleDesc desc = RelationGetDescr(rel);
|
||||
Datum values[Natts_tablespace];
|
||||
bool nulls[Natts_tablespace] = {false};
|
||||
int32 id;
|
||||
|
||||
memset(values, 0, sizeof(values));
|
||||
id = catalog_table_next_seq_id(catalog_get(), TABLESPACE);
|
||||
values[Anum_tablespace_id - 1] = Int32GetDatum(id);
|
||||
values[Anum_tablespace_hypertable_id - 1] = Int32GetDatum(hypertable_id);
|
||||
values[Anum_tablespace_tablespace_name - 1] =
|
||||
DirectFunctionCall1(namein, CStringGetDatum(tspcname));
|
||||
|
||||
catalog_insert_values(rel, desc, values, nulls);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static int32
|
||||
tablespace_insert(int32 hypertable_id, const char *tspcname)
|
||||
{
|
||||
Catalog *catalog = catalog_get();
|
||||
Relation rel;
|
||||
int32 id;
|
||||
|
||||
rel = heap_open(catalog->tables[TABLESPACE].id, RowExclusiveLock);
|
||||
id = tablespace_insert_relation(rel, hypertable_id, tspcname);
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
TS_FUNCTION_INFO_V1(tablespace_attach);
|
||||
|
||||
Datum
|
||||
tablespace_attach(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid hypertable_oid;
|
||||
Name tspcname;
|
||||
Cache *hcache;
|
||||
Hypertable *ht;
|
||||
Oid tspc_oid;
|
||||
int32 tspc_id;
|
||||
Oid user_oid = GetUserId();
|
||||
Oid ownerid;
|
||||
AclResult aclresult;
|
||||
MemoryContext old;
|
||||
CatalogSecurityContext sec_ctx;
|
||||
|
||||
if (PG_NARGS() != 2)
|
||||
elog(ERROR, "Invalid number of arguments");
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
elog(ERROR, "Invalid tablespace name");
|
||||
|
||||
if (PG_ARGISNULL(1))
|
||||
elog(ERROR, "Invalid hypertable");
|
||||
|
||||
tspcname = PG_GETARG_NAME(0);
|
||||
hypertable_oid = PG_GETARG_OID(1);
|
||||
|
||||
tspc_oid = get_tablespace_oid(NameStr(*tspcname), true);
|
||||
|
||||
if (!OidIsValid(tspc_oid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("Tablespace \"%s\" does not exist", NameStr(*tspcname)),
|
||||
errhint("A tablespace needs to be created"
|
||||
" before attaching it to a hypertable")));
|
||||
|
||||
ownerid = rel_get_owner(hypertable_oid);
|
||||
|
||||
if (!has_privs_of_role(user_oid, ownerid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("User \"%s\" lacks permissions on table \"%s\"",
|
||||
GetUserNameFromId(user_oid, true), get_rel_name(hypertable_oid))));
|
||||
|
||||
aclresult = pg_tablespace_aclcheck(tspc_oid, ownerid, ACL_CREATE);
|
||||
|
||||
if (aclresult != ACLCHECK_OK)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("Table owner \"%s\" lacks permissions for tablespace \"%s\"",
|
||||
GetUserNameFromId(ownerid, true), NameStr(*tspcname))));
|
||||
|
||||
hcache = hypertable_cache_pin();
|
||||
ht = hypertable_cache_get_entry(hcache, hypertable_oid);
|
||||
|
||||
if (NULL == ht)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IO_HYPERTABLE_NOT_EXIST),
|
||||
errmsg("Table \"%s\" is not a hypertable", get_rel_name(hypertable_oid))));
|
||||
|
||||
if (hypertable_has_tablespace(ht, tspc_oid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IO_TABLESPACE_ALREADY_ATTACHED),
|
||||
errmsg("Tablespace \"%s\" is already attached to hypertable \"%s\"",
|
||||
NameStr(*tspcname), get_rel_name(hypertable_oid))));
|
||||
|
||||
catalog_become_owner(catalog_get(), &sec_ctx);
|
||||
tspc_id = tablespace_insert(ht->fd.id, NameStr(*tspcname));
|
||||
catalog_restore_user(&sec_ctx);
|
||||
|
||||
/* Add the tablespace to the hypertable */
|
||||
old = cache_switch_to_memory_context(hcache);
|
||||
hypertable_add_tablespace(ht, tspc_id, tspc_oid);
|
||||
MemoryContextSwitchTo(old);
|
||||
|
||||
cache_release(hcache);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
22
src/tablespace.h
Normal file
22
src/tablespace.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef TIMESCALEDB_TABLESPACE_H
|
||||
#define TIMESCALEDB_TABLESPACE_H
|
||||
|
||||
#include <postgres.h>
|
||||
|
||||
typedef struct Tablespace
|
||||
{
|
||||
int32 tablespace_id;
|
||||
Oid tablespace_oid;
|
||||
} Tablespace;
|
||||
|
||||
typedef struct Tablespaces
|
||||
{
|
||||
int capacity;
|
||||
int num_tablespaces;
|
||||
Tablespace *tablespaces;
|
||||
} Tablespaces;
|
||||
|
||||
extern Tablespace *tablespaces_add(Tablespaces *tablespaces, int32 tspc_id, Oid tspc_oid);
|
||||
extern Tablespaces *tablespace_scan(int32 hypertable_id);
|
||||
|
||||
#endif /* TIMESCALEDB_TABLESPACE_H */
|
@ -55,8 +55,8 @@ SELECT set_chunk_time_interval('"one_Partition"', 1::bigint);
|
||||
ERROR: Permission denied for relation public."one_Partition"
|
||||
select add_dimension('"one_Partition"', 'device_id', 2);
|
||||
ERROR: Permission denied for relation public."one_Partition"
|
||||
select attach_tablespace('"one_Partition"', 'tablespace1');
|
||||
ERROR: Permission denied for relation public."one_Partition"
|
||||
select attach_tablespace('tablespace1', '"one_Partition"');
|
||||
ERROR: Tablespace "tablespace1" does not exist
|
||||
\set ON_ERROR_STOP 1
|
||||
CREATE TABLE "1dim"(time timestamp, temp float);
|
||||
SELECT create_hypertable('"1dim"', 'time');
|
||||
@ -414,7 +414,7 @@ ALTER TABLE PUBLIC."Hypertable_1" ADD COLUMN sensor_3 BIGINT NOT NULL DEFAULT 13
|
||||
--create column with same name as previously dropped one
|
||||
ALTER TABLE PUBLIC."Hypertable_1" ADD COLUMN sensor_4 BIGINT NOT NULL DEFAULT 131;
|
||||
--test proper denials for all security definer functions:
|
||||
\c single :ROLE_SUPERUSER
|
||||
\c single :ROLE_SUPERUSER
|
||||
CREATE TABLE plain_table_su (time timestamp, temp float);
|
||||
CREATE TABLE hypertable_su (time timestamp, temp float);
|
||||
SELECT create_hypertable('hypertable_su', 'time');
|
||||
@ -487,7 +487,7 @@ ERROR: must be owner of relation hypertable_su
|
||||
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
|
||||
ERROR: must be owner of relation hypertable_su
|
||||
\set ON_ERROR_STOP 1
|
||||
--change owner
|
||||
--change owner
|
||||
\c single :ROLE_SUPERUSER
|
||||
ALTER TABLE hypertable_su OWNER TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
\c single :ROLE_DEFAULT_PERM_USER_2
|
||||
|
@ -254,3 +254,6 @@ NOTICE: hypertable test_schema.test_1dim already exists, skipping
|
||||
select create_hypertable('test_schema.test_1dim', 'time');
|
||||
ERROR: hypertable test_schema.test_1dim already exists
|
||||
\set ON_ERROR_STOP 1
|
||||
-- Reset GRANTS
|
||||
\c single :ROLE_SUPERUSER
|
||||
REVOKE :ROLE_DEFAULT_PERM_USER FROM :ROLE_DEFAULT_PERM_USER_2;
|
||||
|
@ -26,17 +26,36 @@ INNER JOIN _timescaledb_catalog.chunk ch ON (ch.table_name = c.relname);
|
||||
_hyper_1_1_chunk | tablespace1
|
||||
(1 row)
|
||||
|
||||
--check some error conditions
|
||||
SELECT attach_tablespace('tablespace2', NULL);
|
||||
ERROR: Invalid hypertable
|
||||
SELECT attach_tablespace(NULL, 'tspace_2dim');
|
||||
ERROR: Invalid tablespace name
|
||||
SELECT attach_tablespace('none_existing_tablespace', 'tspace_2dim');
|
||||
ERROR: Tablespace "none_existing_tablespace" does not exist
|
||||
SELECT attach_tablespace('tablespace2', 'none_existing_table');
|
||||
ERROR: relation "none_existing_table" does not exist at character 41
|
||||
--attach another tablespace without first creating it --> should generate error
|
||||
SELECT attach_tablespace('tspace_2dim', 'tablespace2');
|
||||
ERROR: No tablespace "tablespace2" exists. A tablespace needs to be created before assigning it to a hypertable dimension
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
ERROR: Tablespace "tablespace2" does not exist
|
||||
--attach the same tablespace twice to same table should also generate error
|
||||
SELECT attach_tablespace('tspace_2dim', 'tablespace1');
|
||||
ERROR: Tablespace "tablespace1" already assigned to hypertable "tspace_2dim"
|
||||
SELECT attach_tablespace('tablespace1', 'tspace_2dim');
|
||||
ERROR: Tablespace "tablespace1" is already attached to hypertable "tspace_2dim"
|
||||
\c single :ROLE_SUPERUSER
|
||||
CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH;
|
||||
CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER_2 LOCATION :TEST_TABLESPACE2_PATH;
|
||||
\c single :ROLE_DEFAULT_PERM_USER_2
|
||||
--attach without permissions on the table should fail
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
ERROR: User "default_perm_user_2" lacks permissions on table "tspace_2dim"
|
||||
\c single :ROLE_DEFAULT_PERM_USER
|
||||
--attach after creating --> should work
|
||||
SELECT attach_tablespace('tspace_2dim', 'tablespace2');
|
||||
--attach without permissions on the tablespace should also fail
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
ERROR: Table owner "default_perm_user" lacks permissions for tablespace "tablespace2"
|
||||
\c single :ROLE_SUPERUSER
|
||||
GRANT :ROLE_DEFAULT_PERM_USER_2 TO :ROLE_DEFAULT_PERM_USER;
|
||||
\c single :ROLE_DEFAULT_PERM_USER
|
||||
--should work with permissions on both the table and the tablespace
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
attach_tablespace
|
||||
-------------------
|
||||
|
||||
@ -46,7 +65,7 @@ SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
1 | 1 | tablespace1
|
||||
3 | 1 | tablespace2
|
||||
2 | 1 | tablespace2
|
||||
(2 rows)
|
||||
|
||||
--insert into another chunk
|
||||
@ -69,7 +88,7 @@ NOTICE: Adding NOT NULL constraint to time column time (NULL time values not al
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT attach_tablespace('tspace_1dim', 'tablespace2');
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_1dim');
|
||||
attach_tablespace
|
||||
-------------------
|
||||
|
||||
@ -93,3 +112,6 @@ DROP TABLE tspace_1dim CASCADE;
|
||||
DROP TABLE tspace_2dim CASCADE;
|
||||
DROP TABLESPACE tablespace1;
|
||||
DROP TABLESPACE tablespace2;
|
||||
-- revoke grants
|
||||
\c single :ROLE_SUPERUSER
|
||||
REVOKE :ROLE_DEFAULT_PERM_USER_2 FROM :ROLE_DEFAULT_PERM_USER;
|
||||
|
@ -13,7 +13,7 @@ CREATE SCHEMA IF NOT EXISTS "customSchema" AUTHORIZATION :ROLE_DEFAULT_PERM_USER
|
||||
SELECT * FROM "one_Partition";
|
||||
SELECT set_chunk_time_interval('"one_Partition"', 1::bigint);
|
||||
select add_dimension('"one_Partition"', 'device_id', 2);
|
||||
select attach_tablespace('"one_Partition"', 'tablespace1');
|
||||
select attach_tablespace('tablespace1', '"one_Partition"');
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
CREATE TABLE "1dim"(time timestamp, temp float);
|
||||
@ -28,7 +28,7 @@ SELECT * FROM "1dim";
|
||||
\ir include/ddl_ops_2.sql
|
||||
|
||||
--test proper denials for all security definer functions:
|
||||
\c single :ROLE_SUPERUSER
|
||||
\c single :ROLE_SUPERUSER
|
||||
CREATE TABLE plain_table_su (time timestamp, temp float);
|
||||
CREATE TABLE hypertable_su (time timestamp, temp float);
|
||||
SELECT create_hypertable('hypertable_su', 'time');
|
||||
@ -77,7 +77,7 @@ CREATE INDEX ON hypertable_su (time, temp);
|
||||
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
--change owner
|
||||
--change owner
|
||||
\c single :ROLE_SUPERUSER
|
||||
ALTER TABLE hypertable_su OWNER TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
|
||||
|
@ -121,3 +121,7 @@ select create_hypertable('test_schema.test_1dim', 'time', if_not_exists => true)
|
||||
\set ON_ERROR_STOP 0
|
||||
select create_hypertable('test_schema.test_1dim', 'time');
|
||||
\set ON_ERROR_STOP 1
|
||||
|
||||
-- Reset GRANTS
|
||||
\c single :ROLE_SUPERUSER
|
||||
REVOKE :ROLE_DEFAULT_PERM_USER FROM :ROLE_DEFAULT_PERM_USER_2;
|
||||
|
@ -21,17 +21,35 @@ SELECT relname, spcname FROM pg_class c
|
||||
INNER JOIN pg_tablespace t ON (c.reltablespace = t.oid)
|
||||
INNER JOIN _timescaledb_catalog.chunk ch ON (ch.table_name = c.relname);
|
||||
|
||||
--check some error conditions
|
||||
SELECT attach_tablespace('tablespace2', NULL);
|
||||
SELECT attach_tablespace(NULL, 'tspace_2dim');
|
||||
SELECT attach_tablespace('none_existing_tablespace', 'tspace_2dim');
|
||||
SELECT attach_tablespace('tablespace2', 'none_existing_table');
|
||||
|
||||
--attach another tablespace without first creating it --> should generate error
|
||||
SELECT attach_tablespace('tspace_2dim', 'tablespace2');
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
--attach the same tablespace twice to same table should also generate error
|
||||
SELECT attach_tablespace('tspace_2dim', 'tablespace1');
|
||||
SELECT attach_tablespace('tablespace1', 'tspace_2dim');
|
||||
|
||||
\c single :ROLE_SUPERUSER
|
||||
CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER LOCATION :TEST_TABLESPACE2_PATH;
|
||||
CREATE TABLESPACE tablespace2 OWNER :ROLE_DEFAULT_PERM_USER_2 LOCATION :TEST_TABLESPACE2_PATH;
|
||||
\c single :ROLE_DEFAULT_PERM_USER_2
|
||||
|
||||
--attach without permissions on the table should fail
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
|
||||
\c single :ROLE_DEFAULT_PERM_USER
|
||||
|
||||
--attach after creating --> should work
|
||||
SELECT attach_tablespace('tspace_2dim', 'tablespace2');
|
||||
--attach without permissions on the tablespace should also fail
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
|
||||
\c single :ROLE_SUPERUSER
|
||||
GRANT :ROLE_DEFAULT_PERM_USER_2 TO :ROLE_DEFAULT_PERM_USER;
|
||||
\c single :ROLE_DEFAULT_PERM_USER
|
||||
|
||||
--should work with permissions on both the table and the tablespace
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_2dim');
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
@ -45,7 +63,7 @@ INNER JOIN _timescaledb_catalog.chunk ch ON (ch.table_name = c.relname);
|
||||
--
|
||||
CREATE TABLE tspace_1dim(time timestamp, temp float, device text) TABLESPACE tablespace1;
|
||||
SELECT create_hypertable('tspace_1dim', 'time');
|
||||
SELECT attach_tablespace('tspace_1dim', 'tablespace2');
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_1dim');
|
||||
|
||||
INSERT INTO tspace_1dim VALUES ('2017-01-20T09:00:01', 24.3, 'blue');
|
||||
INSERT INTO tspace_1dim VALUES ('2017-03-20T09:00:01', 24.3, 'brown');
|
||||
@ -61,3 +79,6 @@ DROP TABLE tspace_2dim CASCADE;
|
||||
DROP TABLESPACE tablespace1;
|
||||
DROP TABLESPACE tablespace2;
|
||||
|
||||
-- revoke grants
|
||||
\c single :ROLE_SUPERUSER
|
||||
REVOKE :ROLE_DEFAULT_PERM_USER_2 FROM :ROLE_DEFAULT_PERM_USER;
|
||||
|
Loading…
x
Reference in New Issue
Block a user