mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 10:33:27 +08:00
Add function to detach tablespaces from hypertables
Tablespaces can now be detached from hypertables using `tablespace_detach()`. This function can either detach a tablespace from all tables or only a specific table. Having the ability to detach tablespace allows more advanced storage management, for instance, one can detach tablespaces that are running low on diskspace while attaching new ones to replace the old ones.
This commit is contained in:
parent
e593876cb0
commit
6e92383592
@ -303,3 +303,17 @@ CREATE OR REPLACE FUNCTION attach_tablespace(tablespace NAME, hypertable REGCLAS
|
||||
$BODY$
|
||||
SELECT * FROM _timescaledb_internal.attach_tablespace(tablespace, hypertable);
|
||||
$BODY$;
|
||||
|
||||
-- Detach the given tablespace from a hypertable
|
||||
CREATE OR REPLACE FUNCTION detach_tablespace(tablespace NAME, hypertable REGCLASS = NULL)
|
||||
RETURNS INTEGER LANGUAGE SQL AS
|
||||
$BODY$
|
||||
SELECT * FROM _timescaledb_internal.detach_tablespace(tablespace, hypertable);
|
||||
$BODY$;
|
||||
|
||||
-- Detach all tablespaces from the a hypertable
|
||||
CREATE OR REPLACE FUNCTION detach_tablespaces(hypertable REGCLASS)
|
||||
RETURNS INTEGER LANGUAGE SQL AS
|
||||
$BODY$
|
||||
SELECT * FROM _timescaledb_internal.detach_tablespaces(hypertable);
|
||||
$BODY$;
|
||||
|
@ -65,7 +65,7 @@ BEGIN
|
||||
-- Create the schema for the hypertable data if needed
|
||||
PERFORM _timescaledb_internal.create_hypertable_schema(associated_schema_name);
|
||||
|
||||
id := nextval(pg_get_serial_sequence('_timescaledb_catalog.hypertable','id'));
|
||||
id := nextval(pg_get_serial_sequence('_timescaledb_catalog.hypertable','id'));
|
||||
|
||||
IF associated_table_prefix IS NULL THEN
|
||||
associated_table_prefix = format('_hyper_%s', id);
|
||||
@ -621,6 +621,12 @@ $BODY$;
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.attach_tablespace(tablespace NAME, hypertable REGCLASS) RETURNS VOID
|
||||
AS '$libdir/timescaledb', 'tablespace_attach' LANGUAGE C VOLATILE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.detach_tablespace(tablespace NAME, hypertable REGCLASS) RETURNS INTEGER
|
||||
AS '$libdir/timescaledb', 'tablespace_detach' LANGUAGE C VOLATILE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION _timescaledb_internal.detach_tablespaces(hypertable REGCLASS) RETURNS INTEGER
|
||||
AS '$libdir/timescaledb', 'tablespace_detach_all_from_hypertable' 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
|
||||
AS '$libdir/timescaledb', 'chunk_index_clone' LANGUAGE C VOLATILE STRICT;
|
||||
|
@ -35,6 +35,3 @@ 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);
|
||||
|
@ -467,6 +467,11 @@ catalog_delete(Relation rel, HeapTuple tuple)
|
||||
catalog_delete_tid(rel, &tuple->t_self);
|
||||
}
|
||||
|
||||
void
|
||||
catalog_delete_only(Relation rel, HeapTuple tuple)
|
||||
{
|
||||
CatalogTupleDelete(rel, &tuple->t_self);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalidate TimescaleDB catalog caches.
|
||||
@ -510,6 +515,7 @@ catalog_invalidate_cache(Oid catalog_relid, CmdType operation)
|
||||
break;
|
||||
case HYPERTABLE:
|
||||
case DIMENSION:
|
||||
case TABLESPACE:
|
||||
relid = catalog_get_cache_proxy_id(catalog, CACHE_TYPE_HYPERTABLE);
|
||||
CacheInvalidateRelcacheByRelid(relid);
|
||||
break;
|
||||
|
@ -519,4 +519,7 @@ void catalog_delete_tid(Relation rel, ItemPointer tid);
|
||||
void catalog_delete(Relation rel, HeapTuple tuple);
|
||||
void catalog_invalidate_cache(Oid catalog_relid, CmdType operation);
|
||||
|
||||
/* Delete only: do not increment command counter or invalidate caches */
|
||||
void catalog_delete_only(Relation rel, HeapTuple tuple);
|
||||
|
||||
#endif /* TIMESCALEDB_CATALOG_H */
|
||||
|
@ -28,6 +28,7 @@
|
||||
#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')
|
||||
#define ERRCODE_IO_TABLESPACE_NOT_ATTACHED MAKE_SQLSTATE('I','O','1','5','0')
|
||||
|
||||
/*
|
||||
--IO500 - GROUP: internal error
|
||||
|
@ -1,10 +1,13 @@
|
||||
#include <postgres.h>
|
||||
#include <access/htup_details.h>
|
||||
#include <utils/lsyscache.h>
|
||||
#include <utils/syscache.h>
|
||||
#include <utils/memutils.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <nodes/memnodes.h>
|
||||
#include <catalog/namespace.h>
|
||||
#include <commands/tablespace.h>
|
||||
#include <miscadmin.h>
|
||||
|
||||
#include "hypertable.h"
|
||||
#include "dimension.h"
|
||||
@ -16,6 +19,48 @@
|
||||
#include "scanner.h"
|
||||
#include "catalog.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
hypertable_has_privs_of(Oid hypertable_oid, Oid userid)
|
||||
{
|
||||
return has_privs_of_role(userid, rel_get_owner(hypertable_oid));
|
||||
}
|
||||
|
||||
Oid
|
||||
hypertable_permissions_check(Oid hypertable_oid, Oid userid)
|
||||
{
|
||||
Oid ownerid = rel_get_owner(hypertable_oid);
|
||||
|
||||
if (!has_privs_of_role(userid, ownerid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("User \"%s\" lacks permissions on table \"%s\"",
|
||||
GetUserNameFromId(userid, true),
|
||||
get_rel_name(hypertable_oid))));
|
||||
|
||||
return ownerid;
|
||||
}
|
||||
|
||||
|
||||
Hypertable *
|
||||
hypertable_from_tuple(HeapTuple tuple)
|
||||
{
|
||||
@ -230,7 +275,31 @@ hypertable_has_tablespace(Hypertable *ht, Oid tspc_oid)
|
||||
Tablespace *
|
||||
hypertable_add_tablespace(Hypertable *ht, int32 tspc_id, Oid tspc_oid)
|
||||
{
|
||||
return tablespaces_add(ht->tablespaces, tspc_id, tspc_oid);
|
||||
FormData_tablespace form = {
|
||||
.id = tspc_id,
|
||||
.hypertable_id = ht->fd.id,
|
||||
};
|
||||
|
||||
namestrcpy(&form.tablespace_name, get_tablespace_name(tspc_oid));
|
||||
return tablespaces_add(ht->tablespaces, &form, tspc_oid);
|
||||
}
|
||||
|
||||
bool
|
||||
hypertable_delete_tablespace(Hypertable *ht, Oid tspc_oid)
|
||||
{
|
||||
if (NULL == ht->tablespaces)
|
||||
return false;
|
||||
|
||||
return tablespaces_delete(ht->tablespaces, tspc_oid);
|
||||
}
|
||||
|
||||
int
|
||||
hypertable_delete_all_tablespaces(Hypertable *ht)
|
||||
{
|
||||
if (NULL == ht->tablespaces)
|
||||
return 0;
|
||||
|
||||
return tablespaces_clear(ht->tablespaces);
|
||||
}
|
||||
|
||||
static inline Oid
|
||||
|
@ -21,6 +21,8 @@ typedef struct Hypertable
|
||||
Tablespaces *tablespaces;
|
||||
} Hypertable;
|
||||
|
||||
extern bool hypertable_has_privs_of(Oid hypertable_oid, Oid userid);
|
||||
extern Oid hypertable_permissions_check(Oid hypertable_oid, Oid userid);
|
||||
extern Hypertable *hypertable_from_tuple(HeapTuple tuple);
|
||||
extern int hypertable_set_name(Hypertable *ht, const char *newname);
|
||||
extern int hypertable_set_schema(Hypertable *ht, const char *newname);
|
||||
@ -30,5 +32,7 @@ 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);
|
||||
extern bool hypertable_delete_tablespace(Hypertable *ht, Oid tspc_id);
|
||||
extern int hypertable_delete_all_tablespaces(Hypertable *ht);
|
||||
|
||||
#endif /* TIMESCALEDB_HYPERTABLE_H */
|
||||
|
@ -1594,7 +1594,7 @@ timescaledb_ddl_command_start(
|
||||
ProcessUtilityContext context,
|
||||
ParamListInfo params,
|
||||
#if PG10
|
||||
QueryEnvironment * queryEnv,
|
||||
QueryEnvironment *queryEnv,
|
||||
#endif
|
||||
DestReceiver *dest,
|
||||
char *completion_tag)
|
||||
|
317
src/tablespace.c
317
src/tablespace.c
@ -1,12 +1,12 @@
|
||||
#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 <access/xact.h>
|
||||
#include <miscadmin.h>
|
||||
|
||||
#include "hypertable_cache.h"
|
||||
@ -18,26 +18,6 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
@ -52,7 +32,7 @@ tablespaces_alloc(int capacity)
|
||||
}
|
||||
|
||||
Tablespace *
|
||||
tablespaces_add(Tablespaces *tspcs, int32 tspc_id, Oid tspc_oid)
|
||||
tablespaces_add(Tablespaces *tspcs, FormData_tablespace *form, Oid tspc_oid)
|
||||
{
|
||||
Tablespace *tspc;
|
||||
|
||||
@ -63,21 +43,51 @@ tablespaces_add(Tablespaces *tspcs, int32 tspc_id, Oid tspc_oid)
|
||||
}
|
||||
|
||||
tspc = &tspcs->tablespaces[tspcs->num_tablespaces++];
|
||||
tspc->tablespace_id = tspc_id;
|
||||
memcpy(&tspc->fd, form, sizeof(FormData_tablespace));
|
||||
tspc->tablespace_oid = tspc_oid;
|
||||
|
||||
return tspc;
|
||||
}
|
||||
|
||||
int
|
||||
tablespaces_clear(Tablespaces *tspcs)
|
||||
{
|
||||
int num = tspcs->num_tablespaces;
|
||||
|
||||
tspcs->num_tablespaces = 0;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
bool
|
||||
tablespaces_delete(Tablespaces *tspcs, Oid tspc_oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tspcs->num_tablespaces; i++)
|
||||
{
|
||||
if (tspc_oid == tspcs->tablespaces[i].tablespace_oid)
|
||||
{
|
||||
memcpy(&tspcs->tablespaces[i],
|
||||
&tspcs->tablespaces[i + 1],
|
||||
sizeof(Tablespace) * (tspcs->num_tablespaces - i - 1));
|
||||
tspcs->num_tablespaces--;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
tablespace_tuple_found(TupleInfo *ti, void *data)
|
||||
{
|
||||
Tablespaces *tspcs = data;
|
||||
FormData_tablespace *form = (FormData_tablespace *) GETSTRUCT(ti->tuple);
|
||||
Oid tspcoid = get_tablespace_oid(NameStr(form->tablespace_name), true);
|
||||
|
||||
tablespaces_add(tspcs, form, tspcoid);
|
||||
|
||||
tablespaces_add(tspcs,
|
||||
form->id,
|
||||
get_tablespace_oid(NameStr(form->tablespace_name), true));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -141,6 +151,136 @@ tablespace_insert(int32 hypertable_id, const char *tspcname)
|
||||
return id;
|
||||
}
|
||||
|
||||
typedef struct TablespaceScanInfo
|
||||
{
|
||||
Catalog *catalog;
|
||||
Cache *hcache;
|
||||
Oid userid;
|
||||
int num_filtered;
|
||||
int stopcount;
|
||||
} TablespaceScanInfo;
|
||||
|
||||
static bool
|
||||
tablespace_tuple_delete(TupleInfo *ti, void *data)
|
||||
{
|
||||
TablespaceScanInfo *info = data;
|
||||
CatalogSecurityContext sec_ctx;
|
||||
|
||||
catalog_become_owner(info->catalog, &sec_ctx);
|
||||
catalog_delete_only(ti->scanrel, ti->tuple);
|
||||
catalog_restore_user(&sec_ctx);
|
||||
|
||||
return (info->stopcount == 0 || ti->count < info->stopcount);
|
||||
}
|
||||
|
||||
static int
|
||||
tablespace_delete(int32 hypertable_id, const char *tspcname)
|
||||
{
|
||||
ScanKeyData scankey[2];
|
||||
TablespaceScanInfo info = {
|
||||
.catalog = catalog_get(),
|
||||
.stopcount = (NULL != tspcname),
|
||||
};
|
||||
ScannerCtx scanctx = {
|
||||
.table = info.catalog->tables[TABLESPACE].id,
|
||||
.index = info.catalog->tables[TABLESPACE].index_ids[TABLESPACE_HYPERTABLE_ID_TABLESPACE_NAME_IDX],
|
||||
.scantype = ScannerTypeIndex,
|
||||
.nkeys = 0,
|
||||
.scankey = scankey,
|
||||
.tuple_found = tablespace_tuple_delete,
|
||||
.data = &info,
|
||||
.lockmode = RowExclusiveLock,
|
||||
.scandirection = ForwardScanDirection,
|
||||
};
|
||||
int num_deleted;
|
||||
|
||||
ScanKeyInit(&scankey[scanctx.nkeys++],
|
||||
Anum_tablespace_hypertable_id_tablespace_name_idx_hypertable_id,
|
||||
BTEqualStrategyNumber,
|
||||
F_INT4EQ,
|
||||
Int32GetDatum(hypertable_id));
|
||||
|
||||
if (NULL != tspcname)
|
||||
ScanKeyInit(&scankey[scanctx.nkeys++],
|
||||
Anum_tablespace_hypertable_id_tablespace_name_idx_tablespace_name,
|
||||
BTEqualStrategyNumber,
|
||||
F_NAMEEQ,
|
||||
DirectFunctionCall1(namein, CStringGetDatum(tspcname)));
|
||||
|
||||
num_deleted = scanner_scan(&scanctx);
|
||||
|
||||
if (num_deleted > 0)
|
||||
{
|
||||
catalog_invalidate_cache(catalog_table_get_id(info.catalog, TABLESPACE), CMD_DELETE);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
return num_deleted;
|
||||
}
|
||||
|
||||
static bool
|
||||
tablespace_tuple_owner_filter(TupleInfo *ti, void *data)
|
||||
{
|
||||
TablespaceScanInfo *info = data;
|
||||
FormData_tablespace *form = (FormData_tablespace *) GETSTRUCT(ti->tuple);
|
||||
Hypertable *ht;
|
||||
|
||||
ht = hypertable_cache_get_entry_by_id(info->hcache, form->hypertable_id);
|
||||
|
||||
Assert(NULL != ht);
|
||||
|
||||
if (hypertable_has_privs_of(ht->main_table_relid, info->userid))
|
||||
return true;
|
||||
|
||||
info->num_filtered++;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
tablespace_delete_from_all(const char *tspcname, Oid userid)
|
||||
{
|
||||
ScanKeyData scankey[1];
|
||||
TablespaceScanInfo info = {
|
||||
.catalog = catalog_get(),
|
||||
.hcache = hypertable_cache_pin(),
|
||||
.userid = userid,
|
||||
};
|
||||
ScannerCtx scanctx = {
|
||||
.table = info.catalog->tables[TABLESPACE].id,
|
||||
.scantype = ScannerTypeHeap,
|
||||
.nkeys = 1,
|
||||
.scankey = scankey,
|
||||
.tuple_found = tablespace_tuple_delete,
|
||||
.filter = tablespace_tuple_owner_filter,
|
||||
.data = &info,
|
||||
.lockmode = RowExclusiveLock,
|
||||
.scandirection = ForwardScanDirection,
|
||||
};
|
||||
int num_deleted;
|
||||
|
||||
ScanKeyInit(&scankey[0],
|
||||
Anum_tablespace_tablespace_name,
|
||||
BTEqualStrategyNumber, F_NAMEEQ,
|
||||
DirectFunctionCall1(namein, CStringGetDatum(tspcname)));
|
||||
|
||||
cache_release(info.hcache);
|
||||
|
||||
num_deleted = scanner_scan(&scanctx);
|
||||
|
||||
if (num_deleted > 0)
|
||||
{
|
||||
catalog_invalidate_cache(catalog_table_get_id(info.catalog, TABLESPACE), CMD_DELETE);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
if (info.num_filtered > 0)
|
||||
elog(NOTICE, "Tablespace \"%s\" remains attached to %d hypertable(s) due to lack of permissions",
|
||||
tspcname, info.num_filtered);
|
||||
|
||||
return num_deleted;
|
||||
}
|
||||
|
||||
TS_FUNCTION_INFO_V1(tablespace_attach);
|
||||
|
||||
Datum
|
||||
@ -152,7 +292,6 @@ tablespace_attach(PG_FUNCTION_ARGS)
|
||||
Hypertable *ht;
|
||||
Oid tspc_oid;
|
||||
int32 tspc_id;
|
||||
Oid user_oid = GetUserId();
|
||||
Oid ownerid;
|
||||
AclResult aclresult;
|
||||
MemoryContext old;
|
||||
@ -179,13 +318,7 @@ tablespace_attach(PG_FUNCTION_ARGS)
|
||||
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))));
|
||||
ownerid = hypertable_permissions_check(hypertable_oid, GetUserId());
|
||||
|
||||
aclresult = pg_tablespace_aclcheck(tspc_oid, ownerid, ACL_CREATE);
|
||||
|
||||
@ -201,7 +334,8 @@ tablespace_attach(PG_FUNCTION_ARGS)
|
||||
if (NULL == ht)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IO_HYPERTABLE_NOT_EXIST),
|
||||
errmsg("Table \"%s\" is not a hypertable", get_rel_name(hypertable_oid))));
|
||||
errmsg("Table \"%s\" is not a hypertable",
|
||||
get_rel_name(hypertable_oid))));
|
||||
|
||||
if (hypertable_has_tablespace(ht, tspc_oid))
|
||||
ereport(ERROR,
|
||||
@ -222,3 +356,116 @@ tablespace_attach(PG_FUNCTION_ARGS)
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
static int
|
||||
tablespace_detach_one(Oid hypertable_oid, const char *tspcname, Oid tspcoid)
|
||||
{
|
||||
Cache *hcache;
|
||||
Hypertable *ht;
|
||||
int ret;
|
||||
|
||||
hypertable_permissions_check(hypertable_oid, GetUserId());
|
||||
|
||||
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, tspcoid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IO_TABLESPACE_NOT_ATTACHED),
|
||||
errmsg("Tablespace \"%s\" is not attached to hypertable \"%s\"",
|
||||
tspcname, get_rel_name(hypertable_oid))));
|
||||
|
||||
ret = tablespace_delete(ht->fd.id, tspcname);
|
||||
hypertable_delete_tablespace(ht, tspcoid);
|
||||
|
||||
cache_release(hcache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
tablespace_detach_all(Oid hypertable_oid)
|
||||
{
|
||||
Cache *hcache;
|
||||
Hypertable *ht;
|
||||
int ret;
|
||||
|
||||
hypertable_permissions_check(hypertable_oid, GetUserId());
|
||||
|
||||
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))));
|
||||
|
||||
ret = tablespace_delete(ht->fd.id, NULL);
|
||||
|
||||
cache_release(hcache);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
TS_FUNCTION_INFO_V1(tablespace_detach);
|
||||
|
||||
Datum
|
||||
tablespace_detach(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid hypertable_oid = InvalidOid;
|
||||
Name tspcname;
|
||||
Oid tspcoid;
|
||||
int ret;
|
||||
|
||||
switch (PG_NARGS())
|
||||
{
|
||||
case 1:
|
||||
tspcname = PG_GETARG_NAME(0);
|
||||
break;
|
||||
case 2:
|
||||
tspcname = PG_GETARG_NAME(0);
|
||||
hypertable_oid = PG_ARGISNULL(1) ? InvalidOid : PG_GETARG_OID(1);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Invalid number of arguments");
|
||||
}
|
||||
|
||||
if (NULL == tspcname)
|
||||
elog(ERROR, "Invalid tablespace name");
|
||||
|
||||
tspcoid = get_tablespace_oid(NameStr(*tspcname), true);
|
||||
|
||||
if (!OidIsValid(tspcoid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("No tablespace \"%s\" exists.",
|
||||
NameStr(*tspcname))));
|
||||
|
||||
if (OidIsValid(hypertable_oid))
|
||||
ret = tablespace_detach_one(hypertable_oid, NameStr(*tspcname), tspcoid);
|
||||
else
|
||||
ret = tablespace_delete_from_all(NameStr(*tspcname), GetUserId());
|
||||
|
||||
PG_RETURN_INT32(ret);
|
||||
}
|
||||
|
||||
TS_FUNCTION_INFO_V1(tablespace_detach_all_from_hypertable);
|
||||
|
||||
Datum
|
||||
tablespace_detach_all_from_hypertable(PG_FUNCTION_ARGS)
|
||||
{
|
||||
if (PG_NARGS() != 1)
|
||||
elog(ERROR, "Invalid number of arguments");
|
||||
|
||||
if (PG_ARGISNULL(0))
|
||||
elog(ERROR, "Invalid argument");
|
||||
|
||||
PG_RETURN_INT32(tablespace_detach_all(PG_GETARG_OID(0)));
|
||||
}
|
||||
|
@ -2,10 +2,11 @@
|
||||
#define TIMESCALEDB_TABLESPACE_H
|
||||
|
||||
#include <postgres.h>
|
||||
#include "catalog.h"
|
||||
|
||||
typedef struct Tablespace
|
||||
{
|
||||
int32 tablespace_id;
|
||||
FormData_tablespace fd;
|
||||
Oid tablespace_oid;
|
||||
} Tablespace;
|
||||
|
||||
@ -16,7 +17,9 @@ typedef struct Tablespaces
|
||||
Tablespace *tablespaces;
|
||||
} Tablespaces;
|
||||
|
||||
extern Tablespace *tablespaces_add(Tablespaces *tablespaces, int32 tspc_id, Oid tspc_oid);
|
||||
extern Tablespace *tablespaces_add(Tablespaces *tablespaces, FormData_tablespace *form, Oid tspc_oid);
|
||||
extern bool tablespaces_delete(Tablespaces *tspcs, Oid tspc_oid);
|
||||
extern int tablespaces_clear(Tablespaces *tspcs);
|
||||
extern Tablespaces *tablespace_scan(int32 hypertable_id);
|
||||
|
||||
#endif /* TIMESCALEDB_TABLESPACE_H */
|
||||
|
@ -16,6 +16,8 @@ ORDER BY proname;
|
||||
chunk_relation_size
|
||||
chunk_relation_size_pretty
|
||||
create_hypertable
|
||||
detach_tablespace
|
||||
detach_tablespaces
|
||||
drop_chunks
|
||||
first
|
||||
histogram
|
||||
@ -26,5 +28,5 @@ ORDER BY proname;
|
||||
last
|
||||
set_chunk_time_interval
|
||||
time_bucket
|
||||
(15 rows)
|
||||
(17 rows)
|
||||
|
||||
|
@ -49,7 +49,7 @@ SELECT count(*)
|
||||
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
|
||||
count
|
||||
-------
|
||||
119
|
||||
123
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test.show_columns('public."two_Partitions"');
|
||||
@ -235,7 +235,7 @@ SELECT count(*)
|
||||
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
|
||||
count
|
||||
-------
|
||||
119
|
||||
123
|
||||
(1 row)
|
||||
|
||||
--main table and chunk schemas should be the same
|
||||
|
@ -80,6 +80,14 @@ INNER JOIN _timescaledb_catalog.chunk ch ON (ch.table_name = c.relname);
|
||||
(2 rows)
|
||||
|
||||
--
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
||||
-- User doesn't have permission on tablespace1 --> error
|
||||
CREATE TABLE tspace_1dim(time timestamp, temp float, device text) TABLESPACE tablespace1;
|
||||
ERROR: permission denied for tablespace tablespace1
|
||||
-- Grant permission to tablespace1
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
||||
GRANT CREATE ON TABLESPACE tablespace1 TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
||||
CREATE TABLE tspace_1dim(time timestamp, temp float, device text) TABLESPACE tablespace1;
|
||||
SELECT create_hypertable('tspace_1dim', 'time');
|
||||
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
|
||||
@ -94,6 +102,15 @@ SELECT attach_tablespace('tablespace2', 'tspace_1dim');
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
1 | 1 | tablespace1
|
||||
2 | 1 | tablespace2
|
||||
3 | 2 | tablespace1
|
||||
4 | 2 | tablespace2
|
||||
(4 rows)
|
||||
|
||||
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');
|
||||
SELECT relname, spcname FROM pg_class c
|
||||
@ -107,6 +124,55 @@ INNER JOIN _timescaledb_catalog.chunk ch ON (ch.table_name = c.relname);
|
||||
_hyper_2_4_chunk | tablespace2
|
||||
(4 rows)
|
||||
|
||||
--detach tablespace1 from all tables. Due to lack of permissions,
|
||||
--should only detach from 'tspace_1dim' (1 tablespace)
|
||||
SELECT detach_tablespace('tablespace1');
|
||||
NOTICE: Tablespace "tablespace1" remains attached to 1 hypertable(s) due to lack of permissions
|
||||
detach_tablespace
|
||||
-------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
1 | 1 | tablespace1
|
||||
2 | 1 | tablespace2
|
||||
4 | 2 | tablespace2
|
||||
(3 rows)
|
||||
|
||||
--detach the other tablespace
|
||||
SELECT detach_tablespace('tablespace2', 'tspace_1dim');
|
||||
detach_tablespace
|
||||
-------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
1 | 1 | tablespace1
|
||||
2 | 1 | tablespace2
|
||||
(2 rows)
|
||||
|
||||
--detaching a tablespace from table without permissions should fail
|
||||
SELECT detach_tablespace('tablespace2', 'tspace_2dim');
|
||||
ERROR: User "default_perm_user_2" lacks permissions on table "tspace_2dim"
|
||||
SELECT detach_tablespaces('tspace_2dim');
|
||||
ERROR: User "default_perm_user_2" lacks permissions on table "tspace_2dim"
|
||||
--set other user should make detach work
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
||||
SELECT detach_tablespaces('tspace_2dim');
|
||||
detach_tablespaces
|
||||
--------------------
|
||||
2
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
id | hypertable_id | tablespace_name
|
||||
----+---------------+-----------------
|
||||
(0 rows)
|
||||
|
||||
--cleanup
|
||||
DROP TABLE tspace_1dim CASCADE;
|
||||
DROP TABLE tspace_2dim CASCADE;
|
||||
|
@ -61,10 +61,21 @@ INNER JOIN pg_tablespace t ON (c.reltablespace = t.oid)
|
||||
INNER JOIN _timescaledb_catalog.chunk ch ON (ch.table_name = c.relname);
|
||||
|
||||
--
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
||||
-- User doesn't have permission on tablespace1 --> error
|
||||
CREATE TABLE tspace_1dim(time timestamp, temp float, device text) TABLESPACE tablespace1;
|
||||
|
||||
-- Grant permission to tablespace1
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
||||
GRANT CREATE ON TABLESPACE tablespace1 TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER_2;
|
||||
CREATE TABLE tspace_1dim(time timestamp, temp float, device text) TABLESPACE tablespace1;
|
||||
|
||||
SELECT create_hypertable('tspace_1dim', 'time');
|
||||
SELECT attach_tablespace('tablespace2', 'tspace_1dim');
|
||||
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
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');
|
||||
|
||||
@ -72,6 +83,24 @@ 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);
|
||||
|
||||
--detach tablespace1 from all tables. Due to lack of permissions,
|
||||
--should only detach from 'tspace_1dim' (1 tablespace)
|
||||
SELECT detach_tablespace('tablespace1');
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
--detach the other tablespace
|
||||
SELECT detach_tablespace('tablespace2', 'tspace_1dim');
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
--detaching a tablespace from table without permissions should fail
|
||||
SELECT detach_tablespace('tablespace2', 'tspace_2dim');
|
||||
SELECT detach_tablespaces('tspace_2dim');
|
||||
|
||||
--set other user should make detach work
|
||||
SET ROLE :ROLE_DEFAULT_PERM_USER;
|
||||
SELECT detach_tablespaces('tspace_2dim');
|
||||
SELECT * FROM _timescaledb_catalog.tablespace;
|
||||
|
||||
--cleanup
|
||||
DROP TABLE tspace_1dim CASCADE;
|
||||
DROP TABLE tspace_2dim CASCADE;
|
||||
|
Loading…
x
Reference in New Issue
Block a user