Fixes permission handling

Previously, catalog tables were not fully protected from malicious
non-superusers. This PR fixes permission handling be severely
restricting permissions to the catalog and instead using SECURITY
DEFINER functions to alter the catalog when needed without giving
users permission to do those same operations outside of these functions.
In addition, these functions check for proper permissions themselves
so are safe to use.

This PR also makes sure that chunk tables have the same owner as the
hypertable and correctly handles `ALTER TABLE...OWNER TO` commands to
keep this info in sync.
This commit is contained in:
Matvey Arye 2017-06-22 19:07:32 -04:00 committed by Matvey Arye
parent aca7f326b3
commit 97681c2328
18 changed files with 351 additions and 36 deletions

View File

@ -29,6 +29,7 @@ SRCS = \
src/hypertable.c \
src/dimension.c \
src/dimension_slice.c \
src/ddl_utils.c \
src/chunk_constraint.c \
src/partitioning.c \
src/insert.c \

View File

@ -270,7 +270,9 @@ CREATE OR REPLACE FUNCTION _timescaledb_internal.chunk_create(
dimension_ids INTEGER[],
dimension_values BIGINT[]
)
RETURNS _timescaledb_catalog.chunk LANGUAGE PLPGSQL VOLATILE AS
RETURNS _timescaledb_catalog.chunk LANGUAGE PLPGSQL VOLATILE
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
chunk_row _timescaledb_catalog.chunk;

View File

@ -21,12 +21,15 @@ CREATE OR REPLACE FUNCTION create_hypertable(
create_default_indexes BOOLEAN = TRUE,
if_not_exists BOOLEAN = FALSE
)
RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
RETURNS VOID LANGUAGE PLPGSQL VOLATILE
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
hypertable_row _timescaledb_catalog.hypertable;
table_name NAME;
schema_name NAME;
table_owner NAME;
tablespace_oid OID;
tablespace_name NAME;
time_column_type REGTYPE;
@ -41,6 +44,17 @@ BEGIN
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
SELECT tableowner
INTO STRICT table_owner
FROM pg_catalog.pg_tables
WHERE schemaname = schema_name
AND tablename = table_name;
IF table_owner <> session_user THEN
RAISE 'Must be owner of relation %', table_name
USING ERRCODE = 'insufficient_privilege';
END IF;
-- tables that don't have an associated tablespace has reltablespace OID set to 0
-- in pg_class and there is no matching row in pg_tablespace
SELECT spcname

View File

@ -9,9 +9,19 @@
*/
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_is_change_owner(pg_ddl_command)
RETURNS bool IMMUTABLE STRICT
AS '$libdir/timescaledb' LANGUAGE C;
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_change_owner_to(pg_ddl_command)
RETURNS name IMMUTABLE STRICT
AS '$libdir/timescaledb' LANGUAGE C;
-- Handles ddl create index commands on hypertables
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_create_index()
RETURNS event_trigger LANGUAGE plpgsql AS
RETURNS event_trigger LANGUAGE plpgsql
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
info record;
@ -19,6 +29,7 @@ DECLARE
def TEXT;
hypertable_row _timescaledb_catalog.hypertable;
BEGIN
--NOTE: pg_event_trigger_ddl_commands prevents this SECURITY DEFINER function from being called outside trigger.
FOR info IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
-- get table oid
@ -72,12 +83,15 @@ $BODY$;
-- Handles ddl alter index commands on hypertables
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_alter_index()
RETURNS event_trigger LANGUAGE plpgsql AS
RETURNS event_trigger LANGUAGE plpgsql
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
info record;
table_oid regclass;
BEGIN
--NOTE: pg_event_trigger_ddl_commands prevents this SECURITY DEFINER function from being called outside trigger.
FOR info IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
SELECT indrelid INTO STRICT table_oid
@ -96,13 +110,16 @@ $BODY$;
-- Handles ddl drop index commands on hypertables
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_drop_index()
RETURNS event_trigger LANGUAGE plpgsql AS
RETURNS event_trigger LANGUAGE plpgsql
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
info record;
table_oid regclass;
hypertable_row _timescaledb_catalog.hypertable;
BEGIN
--NOTE: pg_event_trigger_ddl_commands prevents this SECURITY DEFINER function from being called outside trigger.
FOR info IN SELECT * FROM pg_event_trigger_dropped_objects()
LOOP
SELECT format('%I.%I', h.schema_name, h.table_name) INTO table_oid
@ -135,3 +152,43 @@ BEGIN
END LOOP;
END
$BODY$;
CREATE OR REPLACE FUNCTION _timescaledb_internal.ddl_process_alter_table()
RETURNS event_trigger LANGUAGE plpgsql
SECURITY DEFINER SET search_path = ''
AS
$BODY$
DECLARE
info record;
new_table_owner TEXT;
chunk_row _timescaledb_catalog.chunk;
hypertable_row _timescaledb_catalog.hypertable;
BEGIN
--NOTE: pg_event_trigger_ddl_commands prevents this SECURITY DEFINER function from being called outside trigger.
FOR info IN SELECT * FROM pg_event_trigger_ddl_commands() LOOP
IF NOT _timescaledb_internal.is_main_table(info.objid) THEN
RETURN;
END IF;
IF _timescaledb_internal.ddl_is_change_owner(info.command) THEN
--if change owner then change owners on all chunks
new_table_owner := _timescaledb_internal.ddl_change_owner_to(info.command);
hypertable_row := _timescaledb_internal.hypertable_from_main_table(info.objid);
FOR chunk_row IN
SELECT *
FROM _timescaledb_catalog.chunk
WHERE hypertable_id = hypertable_row.id
LOOP
EXECUTE format(
$$
ALTER TABLE %1$I.%2$I OWNER TO %3$I
$$,
chunk_row.schema_name, chunk_row.table_name,
new_table_owner
);
END LOOP;
END IF;
END LOOP;
END
$BODY$;

View File

@ -1,32 +1,11 @@
-- This sets up the permissions for entities created by this extension.
-- schema permisions
GRANT USAGE ON SCHEMA _timescaledb_catalog, _timescaledb_internal, _timescaledb_cache
TO PUBLIC;
GRANT USAGE, CREATE ON SCHEMA _timescaledb_internal TO PUBLIC;
GRANT USAGE ON SCHEMA _timescaledb_catalog, _timescaledb_internal TO PUBLIC;
-- needed for working with hypertables
GRANT SELECT ON ALL TABLES IN SCHEMA _timescaledb_catalog TO PUBLIC;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA _timescaledb_catalog TO PUBLIC;
-- Needed but dangerous. Anybody can mess up the _timescaledb_catalog.
-- MUST DOCUMENT TODO: remove these permissions. Have c-based workaround.
-- Everything below this line is suspect.
GRANT INSERT ON TABLE
_timescaledb_catalog.hypertable, _timescaledb_catalog.chunk,
_timescaledb_catalog.dimension, _timescaledb_catalog.dimension_slice, _timescaledb_catalog.chunk_constraint
TO PUBLIC;
-- needed for inserts to hypertable
GRANT UPDATE ON TABLE _timescaledb_catalog.hypertable, _timescaledb_catalog.chunk -- needed for lock
TO PUBLIC;
-- needed for ddl
GRANT INSERT, DELETE ON TABLE _timescaledb_catalog.hypertable_index, _timescaledb_catalog.chunk_index
TO PUBLIC;

View File

@ -62,6 +62,10 @@ BEGIN
WHEN tag IN ('create trigger')
EXECUTE PROCEDURE _timescaledb_internal.ddl_process_create_trigger();
CREATE EVENT TRIGGER ddl_alter_table ON ddl_command_end
WHEN tag IN ('alter table')
EXECUTE PROCEDURE _timescaledb_internal.ddl_process_alter_table();
CREATE EVENT TRIGGER ddl_check_drop_command
ON sql_drop
EXECUTE PROCEDURE _timescaledb_internal.ddl_process_drop_table();
@ -71,6 +75,7 @@ BEGIN
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_alter_index;
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_drop_index;
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_create_trigger;
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_alter_table;
ALTER EXTENSION timescaledb ADD EVENT TRIGGER ddl_check_drop_command;
END IF;

View File

@ -27,6 +27,7 @@ DECLARE
hypertable_row _timescaledb_catalog.hypertable;
tablespace_clause TEXT := '';
tablespace_name NAME;
table_owner NAME;
tablespace_oid OID;
BEGIN
SELECT * INTO STRICT chunk_row
@ -44,6 +45,12 @@ BEGIN
FROM pg_catalog.pg_tablespace t
WHERE t.spcname = tablespace_name;
SELECT tableowner
INTO STRICT table_owner
FROM pg_catalog.pg_tables
WHERE schemaname = hypertable_row.schema_name
AND tablename = hypertable_row.table_name;
IF tablespace_oid IS NOT NULL THEN
tablespace_clause := format('TABLESPACE %s', tablespace_name);
ELSIF tablespace_name IS NOT NULL THEN
@ -59,6 +66,14 @@ BEGIN
hypertable_row.schema_name, hypertable_row.table_name, tablespace_clause
);
EXECUTE format(
$$
ALTER TABLE %1$I.%2$I OWNER TO %3$I
$$,
chunk_row.schema_name, chunk_row.table_name,
table_owner
);
PERFORM _timescaledb_internal.chunk_add_constraints(chunk_id);
END
$BODY$;

71
src/ddl_utils.c Normal file
View File

@ -0,0 +1,71 @@
#include <postgres.h>
#include <tcop/deparse_utility.h>
#include "ddl_utils.h"
enum ddl_cmd_type
{
DDL_CHANGE_OWNER, DDL_OTHER
};
static enum ddl_cmd_type
ddl_alter_table_subcmd(CollectedCommand *cmd)
{
ListCell *cell;
if (cmd->type == SCT_AlterTable)
{
foreach(cell, cmd->d.alterTable.subcmds)
{
CollectedATSubcmd *sub = lfirst(cell);
AlterTableCmd *subcmd = (AlterTableCmd *) sub->parsetree;
Assert(IsA(subcmd, AlterTableCmd));
switch (subcmd->subtype)
{
case AT_ChangeOwner:
return DDL_CHANGE_OWNER;
default:
break;
}
}
}
return DDL_OTHER;
}
Datum
ddl_change_owner_to(PG_FUNCTION_ARGS)
{
CollectedATSubcmd *sub;
AlterTableCmd *altersub;
RoleSpec *role;
Name user = palloc0(NAMEDATALEN);
CollectedCommand *cmd = (CollectedCommand *) PG_GETARG_POINTER(0);
Assert(cmd->type == SCT_AlterTable);
Assert(list_length(cmd->d.alterTable.subcmds) == 1);
sub = linitial(cmd->d.alterTable.subcmds);
Assert(IsA(sub->parsetree, AlterTableCmd));
altersub = (AlterTableCmd *) sub->parsetree;
Assert(IsA(altersub->newowner, RoleSpec));
role = (RoleSpec *) altersub->newowner;
memcpy(user->data, role->rolename, NAMEDATALEN);
PG_RETURN_NAME(user);
}
Datum
ddl_is_change_owner(PG_FUNCTION_ARGS)
{
bool ret =
DDL_CHANGE_OWNER == ddl_alter_table_subcmd(
(CollectedCommand *) PG_GETARG_POINTER(0)
);
PG_RETURN_BOOL(ret);
}

9
src/ddl_utils.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef TIMESCALEDB_DDL_UTILS_H
#define TIMESCALEDB_DDL_UTILS_H
#include <postgres.h>
#include <fmgr.h>
PG_FUNCTION_INFO_V1(ddl_is_change_owner);
PG_FUNCTION_INFO_V1(ddl_change_owner_to);
#endif /* TIMESCALEDB_DDL_UTILS_H */

View File

@ -97,14 +97,14 @@ insert_chunk_state_new(Chunk *chunk)
rel = heap_open(chunk->table_id, RowExclusiveLock);
/* permission check */
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relid = RelationGetRelid(rel);
rte->relkind = rel->rd_rel->relkind;
rte->requiredPerms = ACL_INSERT;
range_table = list_make1(rte);
ExecCheckRTPerms(range_table, true);
/* permissions NOT checked here; were checked at hypertable level */
if (check_enable_rls(rte->relid, InvalidOid, false) == RLS_ENABLED)
ereport(ERROR,

View File

@ -1,5 +1,6 @@
#include <postgres.h>
#include <utils/lsyscache.h>
#include <catalog/pg_class.h>
#include "insert_statement_state.h"
#include "insert_chunk_state.h"
@ -21,6 +22,7 @@ insert_statement_state_new(Oid relid)
InsertStatementState *state;
Hypertable *ht;
Cache *hypertable_cache;
RangeTblEntry *rte;
oldctx = MemoryContextSwitchTo(mctx);
@ -37,6 +39,14 @@ insert_statement_state_new(Oid relid)
state->hypertable = ht;
state->cache = subspace_store_init(HYPERSPACE_NUM_DIMENSIONS(ht->space), mctx);
/* permission check: check hypertable permissions */
rte = makeNode(RangeTblEntry);
rte->rtekind = RTE_RELATION;
rte->relid = relid;
rte->relkind = RELKIND_RELATION;
rte->requiredPerms = ACL_INSERT;
ExecCheckRTPerms(list_make1(rte), true);
MemoryContextSwitchTo(oldctx);
return state;

View File

@ -51,7 +51,9 @@ EXCEPTION
WHEN duplicate_object THEN
--mute error
END$$;
--needed for ddl ops:
CREATE SCHEMA IF NOT EXISTS "customSchema" AUTHORIZATION alt_usr;
--test creating and using schema as non-superuser
\c single alt_usr
\dt
List of relations
@ -445,7 +447,7 @@ $BODY$;
\set ON_ERROR_STOP 0
ALTER TABLE PUBLIC."Hypertable_1" ALTER COLUMN sensor_2_renamed SET DATA TYPE int;
ALTER INDEX "ind_humidity" RENAME TO "ind_humdity2";
psql:include/ddl_ops_2.sql:24: ERROR: ALTER INDEX not supported on hypertable "Hypertable_1"
psql:include/ddl_ops_2.sql:24: ERROR: ALTER INDEX not supported on hypertable public."Hypertable_1"
CREATE TRIGGER test_trigger BEFORE UPDATE OR DELETE ON PUBLIC."Hypertable_1"
FOR EACH STATEMENT EXECUTE PROCEDURE empty_trigger_func();
psql:include/ddl_ops_2.sql:26: ERROR: CREATE TRIGGER not supported on hypertable "Hypertable_1"
@ -455,3 +457,91 @@ 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 postgres
CREATE TABLE plain_table_su (time timestamp, temp float);
CREATE TABLE hypertable_su (time timestamp, temp float);
SELECT create_hypertable('hypertable_su', 'time');
create_hypertable
-------------------
(1 row)
CREATE INDEX "ind_1" ON hypertable_su (time);
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
\c single alt_usr
--all of the following should produce errors
\set ON_ERROR_STOP 0
SELECT create_hypertable('plain_table_su', 'time');
ERROR: Must be owner of relation plain_table_su
CREATE INDEX ON plain_table_su (time, temp);
ERROR: must be owner of relation plain_table_su
CREATE INDEX ON hypertable_su (time, temp);
ERROR: must be owner of relation hypertable_su
DROP INDEX "ind_1";
ERROR: must be owner of relation ind_1
ALTER INDEX "ind_1" RENAME TO "ind_2";
ERROR: must be owner of relation ind_1
\set ON_ERROR_STOP 1
--test that I can't do anything to a non-owned hypertable.
\set ON_ERROR_STOP 0
CREATE INDEX ON hypertable_su (time, temp);
ERROR: must be owner of relation hypertable_su
SELECT * FROM hypertable_su;
ERROR: permission denied for relation hypertable_su
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
ERROR: permission denied for relation hypertable_su
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
ERROR: must be owner of relation hypertable_su
\set ON_ERROR_STOP 1
--grant read permissions
\c single postgres
GRANT SELECT ON hypertable_su TO alt_usr;
\c single alt_usr
SELECT * FROM hypertable_su;
time | temp
--------------------------+------
Fri Jan 20 09:00:01 2017 | 22.5
(1 row)
\set ON_ERROR_STOP 0
CREATE INDEX ON hypertable_su (time, temp);
ERROR: must be owner of relation hypertable_su
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
ERROR: permission denied for relation hypertable_su
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
ERROR: must be owner of relation hypertable_su
\set ON_ERROR_STOP 1
--grant read, insert permissions
\c single postgres
GRANT SELECT, INSERT ON hypertable_su TO alt_usr;
\c single alt_usr
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
SELECT * FROM hypertable_su;
time | temp
--------------------------+------
Fri Jan 20 09:00:01 2017 | 22.5
Fri Jan 20 09:00:01 2017 | 22.5
(2 rows)
\set ON_ERROR_STOP 0
CREATE INDEX ON hypertable_su (time, temp);
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
\c single postgres
ALTER TABLE hypertable_su OWNER TO alt_usr;
\c single alt_usr
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
SELECT * FROM hypertable_su;
time | temp
--------------------------+------
Fri Jan 20 09:00:01 2017 | 22.5
Fri Jan 20 09:00:01 2017 | 22.5
Fri Jan 20 09:00:01 2017 | 22.5
(3 rows)
CREATE INDEX ON hypertable_su (time, temp);
ALTER TABLE hypertable_su ADD COLUMN val2 integer;

View File

@ -428,7 +428,7 @@ $BODY$;
\set ON_ERROR_STOP 0
ALTER TABLE PUBLIC."Hypertable_1" ALTER COLUMN sensor_2_renamed SET DATA TYPE int;
ALTER INDEX "ind_humidity" RENAME TO "ind_humdity2";
psql:include/ddl_ops_2.sql:24: ERROR: ALTER INDEX not supported on hypertable "Hypertable_1"
psql:include/ddl_ops_2.sql:24: ERROR: ALTER INDEX not supported on hypertable public."Hypertable_1"
CREATE TRIGGER test_trigger BEFORE UPDATE OR DELETE ON PUBLIC."Hypertable_1"
FOR EACH STATEMENT EXECUTE PROCEDURE empty_trigger_func();
psql:include/ddl_ops_2.sql:26: ERROR: CREATE TRIGGER not supported on hypertable "Hypertable_1"

View File

@ -36,4 +36,4 @@ SELECT * FROM create_hypertable('"public"."Hypertable_1"', 'time', 'Device_id',
\set ON_ERROR_STOP 0
SELECT * FROM create_hypertable('"public"."Hypertable_1"', 'time', 'Device_id', 2);
ERROR: hypertable "Hypertable_1" already exists
ERROR: hypertable public."Hypertable_1" already exists

View File

@ -367,7 +367,7 @@ $BODY$;
\set ON_ERROR_STOP 0
ALTER TABLE PUBLIC."Hypertable_1" ALTER COLUMN sensor_2_renamed SET DATA TYPE int;
ALTER INDEX "ind_humidity" RENAME TO "ind_humdity2";
psql:include/ddl_ops_2.sql:24: ERROR: ALTER INDEX not supported on hypertable "Hypertable_1"
psql:include/ddl_ops_2.sql:24: ERROR: ALTER INDEX not supported on hypertable public."Hypertable_1"
CREATE TRIGGER test_trigger BEFORE UPDATE OR DELETE ON PUBLIC."Hypertable_1"
FOR EACH STATEMENT EXECUTE PROCEDURE empty_trigger_func();
psql:include/ddl_ops_2.sql:26: ERROR: CREATE TRIGGER not supported on hypertable "Hypertable_1"

View File

@ -25,7 +25,7 @@ SELECT create_hypertable('should_drop', 'time');
-- Calling create hypertable again will increment hypertable ID
-- although no new hypertable is created. Make sure we can handle this.
SELECT create_hypertable('should_drop', 'time', if_not_exists => true);
NOTICE: hypertable should_drop already exists, skipping
NOTICE: hypertable public.should_drop already exists, skipping
create_hypertable
-------------------

View File

@ -42,7 +42,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
104
108
(1 row)
\c postgres
@ -66,7 +66,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
104
108
(1 row)
\c single

View File

@ -8,8 +8,10 @@ EXCEPTION
--mute error
END$$;
--needed for ddl ops:
CREATE SCHEMA IF NOT EXISTS "customSchema" AUTHORIZATION alt_usr;
--test creating and using schema as non-superuser
\c single alt_usr
\dt
@ -27,3 +29,63 @@ SELECT * FROM "1dim";
\ir include/ddl_ops_1.sql
\ir include/ddl_ops_2.sql
--test proper denials for all security definer functions:
\c single postgres
CREATE TABLE plain_table_su (time timestamp, temp float);
CREATE TABLE hypertable_su (time timestamp, temp float);
SELECT create_hypertable('hypertable_su', 'time');
CREATE INDEX "ind_1" ON hypertable_su (time);
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
\c single alt_usr
--all of the following should produce errors
\set ON_ERROR_STOP 0
SELECT create_hypertable('plain_table_su', 'time');
CREATE INDEX ON plain_table_su (time, temp);
CREATE INDEX ON hypertable_su (time, temp);
DROP INDEX "ind_1";
ALTER INDEX "ind_1" RENAME TO "ind_2";
\set ON_ERROR_STOP 1
--test that I can't do anything to a non-owned hypertable.
\set ON_ERROR_STOP 0
CREATE INDEX ON hypertable_su (time, temp);
SELECT * FROM hypertable_su;
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
\set ON_ERROR_STOP 1
--grant read permissions
\c single postgres
GRANT SELECT ON hypertable_su TO alt_usr;
\c single alt_usr
SELECT * FROM hypertable_su;
\set ON_ERROR_STOP 0
CREATE INDEX ON hypertable_su (time, temp);
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
\set ON_ERROR_STOP 1
--grant read, insert permissions
\c single postgres
GRANT SELECT, INSERT ON hypertable_su TO alt_usr;
\c single alt_usr
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
SELECT * FROM hypertable_su;
\set ON_ERROR_STOP 0
CREATE INDEX ON hypertable_su (time, temp);
ALTER TABLE hypertable_su ADD COLUMN val2 integer;
\set ON_ERROR_STOP 1
--change owner
\c single postgres
ALTER TABLE hypertable_su OWNER TO alt_usr;
\c single alt_usr
INSERT INTO hypertable_su VALUES('2017-01-20T09:00:01', 22.5);
SELECT * FROM hypertable_su;
CREATE INDEX ON hypertable_su (time, temp);
ALTER TABLE hypertable_su ADD COLUMN val2 integer;