Add proper permissions handling for associated (chunk) schemas

A hypertable's associated schema is used to create and store internal
data tables (chunks). A hypertable creates tables in that schema,
typically with full superuser permissions, regardless of whether the
hypertable's owner or the current user have permissions for the schema.
If the schema doesn't exist, the hypertable will create it when
creating the first chunk, even though the user or table owner does
not have permissions to create schemas in the database.

This change adds proper permissions checks to create_hypertable() so
that users cannot create hypertables with a custom associated schema
unless they have the proper permissions on the schema or the database.

Chunks are also no longer created with internal schema permissions if
the associated schema is something different from the internal schema.
This commit is contained in:
Erik Nordström 2017-12-07 12:42:42 +01:00 committed by Erik Nordström
parent 21efcce95c
commit 4df8f287a6
14 changed files with 213 additions and 34 deletions

View File

@ -40,16 +40,21 @@ DECLARE
is_partitioned BOOLEAN;
chunk_time_interval_actual BIGINT;
time_type REGTYPE;
relowner OID;
BEGIN
-- Early abort if lacking permissions
PERFORM _timescaledb_internal.check_role(main_table);
SELECT relname, nspname, reltablespace, relkind = 'p'
INTO STRICT table_name, schema_name, tablespace_oid, is_partitioned
SELECT c.relname, n.nspname, c.reltablespace, c.relkind = 'p', c.relowner
INTO STRICT table_name, schema_name, tablespace_oid, is_partitioned, relowner
FROM pg_class c
INNER JOIN pg_namespace n ON (n.OID = c.relnamespace)
WHERE c.OID = main_table;
-- Check that the user has permissions to create chunks in the
-- associated schema
PERFORM _timescaledb_internal.check_associated_schema_permissions(associated_schema_name, relowner);
IF is_partitioned THEN
RAISE EXCEPTION 'table % is already partitioned', main_table
USING ERRCODE = 'IO110';

View File

@ -22,6 +22,9 @@ BEGIN
END
$BODY$;
CREATE OR REPLACE FUNCTION _timescaledb_internal.check_associated_schema_permissions(schema_name NAME, userid OID)
RETURNS VOID AS '$libdir/timescaledb', 'hypertable_check_associated_schema_permissions' LANGUAGE C;
-- Creates a hypertable row.
CREATE OR REPLACE FUNCTION _timescaledb_internal.create_hypertable(
main_table REGCLASS,

View File

@ -13,6 +13,7 @@
#include <utils/builtins.h>
#include <utils/lsyscache.h>
#include <utils/hsearch.h>
#include <miscadmin.h>
#include "chunk.h"
#include "chunk_index.h"
@ -26,6 +27,7 @@
#include "scanner.h"
#include "process_utility.h"
#include "trigger.h"
#include "compat.h"
typedef bool (*on_chunk_func) (ChunkScanCtx *ctx, Chunk *chunk);
@ -341,21 +343,59 @@ chunk_add_constraints(Chunk *chunk)
return num_added;
}
/*
* Create a chunk's table.
*
* A chunk inherits from the main hypertable and will have the same owner. Since
* chunks can be created either in the TimescaleDB internal schema or in a
* user-specified schema, some care has to be taken to use the right
* permissions, depending on the case:
*
* 1. if the chunk is created in the internal schema, we create it as the
* catalog/schema owner (i.e., anyone can create chunks there via inserting into
* a hypertable, but can not do it via CREATE TABLE).
*
* 2. if the chunk is created in a user-specified "associated schema", then we
* shouldn't use the catalog owner to create the table since that typically
* implies super-user permissions. If we would allow that, anyone can specify
* someone else's schema in create_hypertable() and create chunks in it without
* having the proper permissions to do so. With this logic, the hypertable owner
* must have permissions to create tables in the associated schema, or else
* table creation will fail. If the schema doesn't yet exist, the table owner
* instead needs the proper permissions on the database to create the schema.
*/
static Oid
chunk_create_table(Chunk *chunk, Hypertable *ht)
{
Catalog *catalog = catalog_get();
Relation rel;
ObjectAddress objaddr;
CatalogSecurityContext sec_ctx;
int sec_ctx;
CreateStmt stmt = {
.type = T_CreateStmt,
.relation = makeRangeVar(NameStr(chunk->fd.schema_name), NameStr(chunk->fd.table_name), 0),
.inhRelations = list_make1(makeRangeVar(NameStr(ht->fd.schema_name), NameStr(ht->fd.table_name), 0)),
.tablespacename = hypertable_select_tablespace(ht, chunk),
};
Oid uid,
saved_uid;
rel = heap_open(ht->main_table_relid, AccessShareLock);
catalog_become_owner(catalog_get(), &sec_ctx);
/*
* If the chunk is created in the internal schema, become the catalog
* owner, otherwise become the hypertable owner
*/
if (namestrcmp(&chunk->fd.schema_name, INTERNAL_SCHEMA_NAME) == 0)
uid = catalog->owner_uid;
else
uid = rel->rd_rel->relowner;
GetUserIdAndSecContext(&saved_uid, &sec_ctx);
if (uid != saved_uid)
SetUserIdAndSecContext(uid, sec_ctx | SECURITY_LOCAL_USERID_CHANGE);
objaddr = DefineRelation(&stmt,
RELKIND_RELATION,
rel->rd_rel->relowner,
@ -365,7 +405,9 @@ chunk_create_table(Chunk *chunk, Hypertable *ht)
#endif
);
catalog_restore_user(&sec_ctx);
if (uid != saved_uid)
SetUserIdAndSecContext(saved_uid, sec_ctx);
heap_close(rel, AccessShareLock);
return objaddr.objectId;

View File

@ -4,9 +4,11 @@
#include <utils/syscache.h>
#include <utils/memutils.h>
#include <utils/builtins.h>
#include <utils/acl.h>
#include <nodes/memnodes.h>
#include <catalog/namespace.h>
#include <commands/tablespace.h>
#include <commands/dbcommands.h>
#include <miscadmin.h>
#include "hypertable.h"
@ -360,3 +362,67 @@ hypertable_validate_triggers(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
TS_FUNCTION_INFO_V1(hypertable_check_associated_schema_permissions);
/*
* Check that the current user can create chunks in a hypertable's associated
* schema.
*
* This function is typically called from create_hypertable() to verify that the
* table owner has CREATE permissions for the schema (if it already exists) or
* the database (if the schema does not exist and needs to be created).
*/
Datum
hypertable_check_associated_schema_permissions(PG_FUNCTION_ARGS)
{
Name schema_name;
Oid schema_oid;
Oid user_oid;
if (PG_NARGS() != 2)
elog(ERROR, "Invalid number of arguments");
/*
* If the schema name is NULL, it implies the internal catalog schema and
* anyone should be able to create chunks there.
*/
if (PG_ARGISNULL(0))
PG_RETURN_VOID();
schema_name = PG_GETARG_NAME(0);
/* Anyone can create chunks in the internal schema */
if (namestrcmp(schema_name, INTERNAL_SCHEMA_NAME) == 0)
PG_RETURN_VOID();
if (PG_ARGISNULL(1))
user_oid = GetUserId();
else
user_oid = PG_GETARG_OID(1);
schema_oid = get_namespace_oid(NameStr(*schema_name), true);
if (!OidIsValid(schema_oid))
{
/*
* Schema does not exist, so we must check that the user has
* privileges to create the schema in the current database
*/
if (pg_database_aclcheck(MyDatabaseId, user_oid, ACL_CREATE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("User %s lacks permissions to create schema \"%s\" in database \"%s\"",
GetUserNameFromId(user_oid, false),
NameStr(*schema_name),
get_database_name(MyDatabaseId))));
}
else if (pg_namespace_aclcheck(schema_oid, user_oid, ACL_CREATE) != ACLCHECK_OK)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("User %s lacks permissions to create chunks in schema \"%s\"",
GetUserNameFromId(user_oid, false),
NameStr(*schema_name))));
PG_RETURN_VOID();
}

View File

@ -12,6 +12,9 @@ CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_0)
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_1) WHERE series_1 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL;
\c single :ROLE_SUPERUSER
CREATE SCHEMA "one_Partition" AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
\c single :ROLE_DEFAULT_PERM_USER;
SELECT * FROM create_hypertable('"public"."one_Partition"', 'timeCustom', associated_schema_name=>'one_Partition', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
create_hypertable
-------------------
@ -39,6 +42,9 @@ INSERT 0 1
\c single :ROLE_SUPERUSER
--needed for ddl ops:
CREATE SCHEMA IF NOT EXISTS "customSchema" AUTHORIZATION :ROLE_DEFAULT_PERM_USER_2;
--needed for ROLE_DEFAULT_PERM_USER_2 to write to the 'one_Partition' schema which
--is owned by ROLE_DEFAULT_PERM_USER
GRANT CREATE ON SCHEMA "one_Partition" TO :ROLE_DEFAULT_PERM_USER_2;
--test creating and using schema as non-superuser
\c single :ROLE_DEFAULT_PERM_USER_2
\dt

View File

@ -1,5 +1,6 @@
\c single :ROLE_SUPERUSER
create schema test_schema AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
create schema chunk_schema AUTHORIZATION :ROLE_DEFAULT_PERM_USER_2;
SET ROLE :ROLE_DEFAULT_PERM_USER;
create table test_schema.test_table(time BIGINT, temp float8, device_id text, device_type text, location text, id int, id2 int);
\set ON_ERROR_STOP 0
@ -71,7 +72,18 @@ SELECT * FROM test.show_columns('test_schema.test_table_no_not_null');
device_id | text | f
(2 rows)
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
RESET ROLE;
SET ROLE :ROLE_DEFAULT_PERM_USER;
\set ON_ERROR_STOP 0
-- No permissions on associated schema should fail
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'), associated_schema_name => 'chunk_schema');
ERROR: User default_perm_user lacks permissions to create chunks in schema "chunk_schema"
\set ON_ERROR_STOP 1
-- Granting permissions on chunk_schema should make things work
RESET ROLE;
GRANT CREATE ON SCHEMA chunk_schema TO :ROLE_DEFAULT_PERM_USER;
SET ROLE :ROLE_DEFAULT_PERM_USER;
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'), associated_schema_name => 'chunk_schema');
NOTICE: Adding NOT NULL constraint to time column time (NULL time values not allowed)
create_hypertable
-------------------
@ -94,7 +106,7 @@ select add_dimension('test_schema.test_table', 'location', 2);
select * from _timescaledb_catalog.hypertable where table_name = 'test_table';
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+------------+------------------------+-------------------------+----------------
2 | test_schema | test_table | _timescaledb_internal | _hyper_2 | 3
2 | test_schema | test_table | chunk_schema | _hyper_2 | 3
(1 row)
select * from _timescaledb_catalog.dimension;
@ -123,7 +135,7 @@ NOTICE: Adding NOT NULL constraint to time column id (NULL time values not allo
select * from _timescaledb_catalog.hypertable where table_name = 'test_table';
id | schema_name | table_name | associated_schema_name | associated_table_prefix | num_dimensions
----+-------------+------------+------------------------+-------------------------+----------------
2 | test_schema | test_table | _timescaledb_internal | _hyper_2 | 4
2 | test_schema | test_table | chunk_schema | _hyper_2 | 4
(1 row)
select * from _timescaledb_catalog.dimension;
@ -204,6 +216,14 @@ insert into test_schema.test_table values (123456789, 23.8, 'blue', 'type1', 'ny
select add_dimension('test_schema.test_table', 'device_type', 2);
ERROR: Cannot add new dimension to a non-empty table
\set ON_ERROR_STOP 1
--show chunks in the associated schema
\dt "chunk_schema".*
List of relations
Schema | Name | Type | Owner
--------------+------------------+-------+-------------------
chunk_schema | _hyper_2_2_chunk | table | default_perm_user
(1 row)
--test partitioning in only time dimension
create table test_schema.test_1dim(time timestamp, temp float);
select create_hypertable('test_schema.test_1dim', 'time');
@ -220,10 +240,10 @@ SELECT * FROM _timescaledb_internal.get_create_command('test_1dim');
(1 row)
\dt "test_schema".*
List of relations
Schema | Name | Type | Owner
-------------+------------------------+-------+---------------------
test_schema | test_1dim | table | default_perm_user_2
List of relations
Schema | Name | Type | Owner
-------------+------------------------+-------+-------------------
test_schema | test_1dim | table | default_perm_user
test_schema | test_table | table | default_perm_user
test_schema | test_table_no_not_null | table | default_perm_user
(3 rows)

View File

@ -12,6 +12,9 @@ CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_0)
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_1) WHERE series_1 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL;
\c single :ROLE_SUPERUSER
CREATE SCHEMA "one_Partition" AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
\c single :ROLE_DEFAULT_PERM_USER;
SELECT * FROM create_hypertable('"public"."one_Partition"', 'timeCustom', associated_schema_name=>'one_Partition', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
create_hypertable
-------------------

View File

@ -49,7 +49,7 @@ SELECT count(*)
AND refobjid = (SELECT oid FROM pg_extension WHERE extname = 'timescaledb');
count
-------
114
115
(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
-------
114
115
(1 row)
--main table and chunk schemas should be the same

View File

@ -18,6 +18,9 @@ CREATE TABLE PUBLIC."testNs" (
series_bool BOOLEAN NULL
);
CREATE INDEX ON PUBLIC."testNs" (device_id, "timeCustom" DESC NULLS LAST) WHERE device_id IS NOT NULL;
\c single :ROLE_SUPERUSER
CREATE SCHEMA "testNs" AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
\c single :ROLE_DEFAULT_PERM_USER
SELECT * FROM create_hypertable('"public"."testNs"', 'timeCustom', 'device_id', 2, associated_schema_name=>'testNs' );
create_hypertable
-------------------
@ -234,7 +237,7 @@ SELECT time_bucket(INTERVAL '1 day', TIMESTAMP '2011-01-02 01:01:01');
Sun Jan 02 00:00:00 2011
(1 row)
SELECT time, time_bucket(INTERVAL '2 day ', time)
SELECT time, time_bucket(INTERVAL '2 day ', time)
FROM unnest(ARRAY[
TIMESTAMP '2011-01-01 01:01:01',
TIMESTAMP '2011-01-02 01:01:01',
@ -249,7 +252,7 @@ FROM unnest(ARRAY[
Tue Jan 04 01:01:01 2011 | Mon Jan 03 00:00:00 2011
(4 rows)
SELECT int_def, time_bucket(int_def,TIMESTAMP '2011-01-02 01:01:01.111')
SELECT int_def, time_bucket(int_def,TIMESTAMP '2011-01-02 01:01:01.111')
FROM unnest(ARRAY[
INTERVAL '1 millisecond',
INTERVAL '1 second',
@ -282,7 +285,7 @@ ERROR: interval defined in terms of month, year, century etc. not supported
SELECT time_bucket(INTERVAL '1 month',TIMESTAMP '2011-01-02 01:01:01.111');
ERROR: interval defined in terms of month, year, century etc. not supported
\set ON_ERROR_STOP 1
SELECT time, time_bucket(INTERVAL '5 minute', time)
SELECT time, time_bucket(INTERVAL '5 minute', time)
FROM unnest(ARRAY[
TIMESTAMP '1970-01-01 00:59:59.999999',
TIMESTAMP '1970-01-01 01:01:00',
@ -297,7 +300,7 @@ FROM unnest(ARRAY[
Thu Jan 01 01:05:00 1970 | Thu Jan 01 01:05:00 1970
(4 rows)
SELECT time, time_bucket(INTERVAL '5 minute', time)
SELECT time, time_bucket(INTERVAL '5 minute', time)
FROM unnest(ARRAY[
TIMESTAMP '2011-01-02 01:04:59.999999',
TIMESTAMP '2011-01-02 01:05:00',
@ -343,7 +346,7 @@ FROM unnest(ARRAY[
Sun Jan 02 01:08:00 2011 | Sun Jan 02 01:08:00 2011
(4 rows)
--example to align with an origin
--example to align with an origin
SELECT time, time_bucket(INTERVAL '5 minute', time - (TIMESTAMP '2011-01-02 00:02:00' - TIMESTAMP 'epoch')) + (TIMESTAMP '2011-01-02 00:02:00'-TIMESTAMP 'epoch')
FROM unnest(ARRAY[
TIMESTAMP '2011-01-02 01:01:59.999999',
@ -462,7 +465,7 @@ FROM unnest(ARRAY[
Mon Jan 03 18:01:01 2011 EST | Mon Jan 03 00:00:00 2011 EST | Mon Jan 03 00:00:00 2011 EST
(3 rows)
--dst: same local hour bucketed as two different hours.
--dst: same local hour bucketed as two different hours.
SELECT time, time_bucket(INTERVAL '1 hour', time), date_trunc('hour', time)
FROM unnest(ARRAY[
TIMESTAMP WITH TIME ZONE '2017-11-05 12:05:00+07',
@ -506,7 +509,7 @@ FROM unnest(ARRAY[
Sun Nov 05 03:05:00 2017 EST | Sun Nov 05 02:00:00 2017
(4 rows)
SELECT time, time_bucket(10, time)
SELECT time, time_bucket(10, time)
FROM unnest(ARRAY[
'99',
'100',
@ -521,7 +524,7 @@ FROM unnest(ARRAY[
110 | 110
(4 rows)
SELECT time, time_bucket(10, time,2)
SELECT time, time_bucket(10, time,2)
FROM unnest(ARRAY[
'101',
'102',
@ -536,7 +539,7 @@ FROM unnest(ARRAY[
112 | 112
(4 rows)
SELECT time, time_bucket(10, time, -2)
SELECT time, time_bucket(10, time, -2)
FROM unnest(ARRAY[
'97',
'98',

View File

@ -13,6 +13,9 @@ CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_0)
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_1) WHERE series_1 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL;
\c single :ROLE_SUPERUSER
CREATE SCHEMA "one_Partition" AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
\c single :ROLE_DEFAULT_PERM_USER;
SELECT * FROM create_hypertable('"public"."one_Partition"', 'timeCustom', associated_schema_name=>'one_Partition', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
--output command tags
\set QUIET off

View File

@ -5,6 +5,10 @@
--needed for ddl ops:
CREATE SCHEMA IF NOT EXISTS "customSchema" AUTHORIZATION :ROLE_DEFAULT_PERM_USER_2;
--needed for ROLE_DEFAULT_PERM_USER_2 to write to the 'one_Partition' schema which
--is owned by ROLE_DEFAULT_PERM_USER
GRANT CREATE ON SCHEMA "one_Partition" TO :ROLE_DEFAULT_PERM_USER_2;
--test creating and using schema as non-superuser
\c single :ROLE_DEFAULT_PERM_USER_2
\dt

View File

@ -1,5 +1,6 @@
\c single :ROLE_SUPERUSER
create schema test_schema AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
create schema chunk_schema AUTHORIZATION :ROLE_DEFAULT_PERM_USER_2;
SET ROLE :ROLE_DEFAULT_PERM_USER;
create table test_schema.test_table(time BIGINT, temp float8, device_id text, device_type text, location text, id int, id2 int);
@ -43,7 +44,20 @@ SELECT * FROM test.show_columns('test_schema.test_table_no_not_null');
SELECT _timescaledb_internal.set_time_columns_not_null();
SELECT * FROM test.show_columns('test_schema.test_table_no_not_null');
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
RESET ROLE;
SET ROLE :ROLE_DEFAULT_PERM_USER;
\set ON_ERROR_STOP 0
-- No permissions on associated schema should fail
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'), associated_schema_name => 'chunk_schema');
\set ON_ERROR_STOP 1
-- Granting permissions on chunk_schema should make things work
RESET ROLE;
GRANT CREATE ON SCHEMA chunk_schema TO :ROLE_DEFAULT_PERM_USER;
SET ROLE :ROLE_DEFAULT_PERM_USER;
select * from create_hypertable('test_schema.test_table', 'time', 'device_id', 2, chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'), associated_schema_name => 'chunk_schema');
SELECT * FROM _timescaledb_internal.get_create_command('test_table');
--test adding one more closed dimension
@ -99,6 +113,9 @@ insert into test_schema.test_table values (123456789, 23.8, 'blue', 'type1', 'ny
select add_dimension('test_schema.test_table', 'device_type', 2);
\set ON_ERROR_STOP 1
--show chunks in the associated schema
\dt "chunk_schema".*
--test partitioning in only time dimension
create table test_schema.test_1dim(time timestamp, temp float);
select create_hypertable('test_schema.test_1dim', 'time');

View File

@ -12,6 +12,10 @@ CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_1)
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_2) WHERE series_2 IS NOT NULL;
CREATE INDEX ON PUBLIC."one_Partition" ("timeCustom" DESC NULLS LAST, series_bool) WHERE series_bool IS NOT NULL;
\c single :ROLE_SUPERUSER
CREATE SCHEMA "one_Partition" AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
\c single :ROLE_DEFAULT_PERM_USER;
SELECT * FROM create_hypertable('"public"."one_Partition"', 'timeCustom', associated_schema_name=>'one_Partition', chunk_time_interval=>_timescaledb_internal.interval_to_usec('1 month'));
--output command tags

View File

@ -19,8 +19,11 @@ CREATE TABLE PUBLIC."testNs" (
series_bool BOOLEAN NULL
);
CREATE INDEX ON PUBLIC."testNs" (device_id, "timeCustom" DESC NULLS LAST) WHERE device_id IS NOT NULL;
SELECT * FROM create_hypertable('"public"."testNs"', 'timeCustom', 'device_id', 2, associated_schema_name=>'testNs' );
\c single :ROLE_SUPERUSER
CREATE SCHEMA "testNs" AUTHORIZATION :ROLE_DEFAULT_PERM_USER;
\c single :ROLE_DEFAULT_PERM_USER
SELECT * FROM create_hypertable('"public"."testNs"', 'timeCustom', 'device_id', 2, associated_schema_name=>'testNs' );
\c single
INSERT INTO PUBLIC."testNs"("timeCustom", device_id, series_0, series_1) VALUES
@ -142,7 +145,7 @@ SELECT time_bucket('25 hour', DATE '2012-01-01');
SELECT time_bucket(INTERVAL '1 day', TIMESTAMP '2011-01-02 01:01:01');
SELECT time, time_bucket(INTERVAL '2 day ', time)
SELECT time, time_bucket(INTERVAL '2 day ', time)
FROM unnest(ARRAY[
TIMESTAMP '2011-01-01 01:01:01',
TIMESTAMP '2011-01-02 01:01:01',
@ -151,7 +154,7 @@ FROM unnest(ARRAY[
]) AS time;
SELECT int_def, time_bucket(int_def,TIMESTAMP '2011-01-02 01:01:01.111')
SELECT int_def, time_bucket(int_def,TIMESTAMP '2011-01-02 01:01:01.111')
FROM unnest(ARRAY[
INTERVAL '1 millisecond',
INTERVAL '1 second',
@ -170,7 +173,7 @@ SELECT time_bucket(INTERVAL '1 year',TIMESTAMP '2011-01-02 01:01:01.111');
SELECT time_bucket(INTERVAL '1 month',TIMESTAMP '2011-01-02 01:01:01.111');
\set ON_ERROR_STOP 1
SELECT time, time_bucket(INTERVAL '5 minute', time)
SELECT time, time_bucket(INTERVAL '5 minute', time)
FROM unnest(ARRAY[
TIMESTAMP '1970-01-01 00:59:59.999999',
TIMESTAMP '1970-01-01 01:01:00',
@ -179,7 +182,7 @@ FROM unnest(ARRAY[
]) AS time;
SELECT time, time_bucket(INTERVAL '5 minute', time)
SELECT time, time_bucket(INTERVAL '5 minute', time)
FROM unnest(ARRAY[
TIMESTAMP '2011-01-02 01:04:59.999999',
TIMESTAMP '2011-01-02 01:05:00',
@ -204,7 +207,7 @@ FROM unnest(ARRAY[
TIMESTAMP '2011-01-02 01:08:00'
]) AS time;
--example to align with an origin
--example to align with an origin
SELECT time, time_bucket(INTERVAL '5 minute', time - (TIMESTAMP '2011-01-02 00:02:00' - TIMESTAMP 'epoch')) + (TIMESTAMP '2011-01-02 00:02:00'-TIMESTAMP 'epoch')
FROM unnest(ARRAY[
TIMESTAMP '2011-01-02 01:01:59.999999',
@ -273,7 +276,7 @@ FROM unnest(ARRAY[
TIMESTAMP WITH TIME ZONE '2011-01-04 01:01:01+02'
]) AS time;
--dst: same local hour bucketed as two different hours.
--dst: same local hour bucketed as two different hours.
SELECT time, time_bucket(INTERVAL '1 hour', time), date_trunc('hour', time)
FROM unnest(ARRAY[
TIMESTAMP WITH TIME ZONE '2017-11-05 12:05:00+07',
@ -299,7 +302,7 @@ FROM unnest(ARRAY[
]) AS time;
SELECT time, time_bucket(10, time)
SELECT time, time_bucket(10, time)
FROM unnest(ARRAY[
'99',
'100',
@ -307,7 +310,7 @@ FROM unnest(ARRAY[
'110'
]::int[]) AS time;
SELECT time, time_bucket(10, time,2)
SELECT time, time_bucket(10, time,2)
FROM unnest(ARRAY[
'101',
'102',
@ -315,7 +318,7 @@ FROM unnest(ARRAY[
'112'
]::int[]) AS time;
SELECT time, time_bucket(10, time, -2)
SELECT time, time_bucket(10, time, -2)
FROM unnest(ARRAY[
'97',
'98',