Skip to content

Commit cb5414c

Browse files
committed
Add chunk_time_interval as WITH option for create table
1 parent c5d2468 commit cb5414c

File tree

5 files changed

+204
-22
lines changed

5 files changed

+204
-22
lines changed

src/create_table_with_clause.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <postgres.h>
88
#include <catalog/pg_type.h>
9+
#include <fmgr.h>
910

1011
#include "compat/compat.h"
1112
#include "create_table_with_clause.h"
@@ -14,6 +15,7 @@
1415
static const WithClauseDefinition create_table_with_clauses_def[] = {
1516
[CreateTableFlagHypertable] = {.arg_names = {"hypertable", NULL}, .type_id = BOOLOID,},
1617
[CreateTableFlagTimeColumn] = {.arg_names = {"time_column", NULL}, .type_id = TEXTOID,},
18+
[CreateTableFlagChunkTimeInterval] = {.arg_names = {"chunk_time_interval", NULL}, .type_id = TEXTOID,},
1719
};
1820

1921
WithClauseResult *
@@ -23,3 +25,43 @@ ts_create_table_with_clause_parse(const List *defelems)
2325
create_table_with_clauses_def,
2426
TS_ARRAY_LEN(create_table_with_clauses_def));
2527
}
28+
29+
Datum
30+
ts_create_table_parse_chunk_time_interval(WithClauseResult *parsed_options, Oid column_type,
31+
Oid *interval_type)
32+
{
33+
if (parsed_options[CreateTableFlagChunkTimeInterval].is_default == false)
34+
{
35+
Datum textarg = parsed_options[CreateTableFlagChunkTimeInterval].parsed;
36+
switch (column_type)
37+
{
38+
case INT2OID:
39+
{
40+
*interval_type = INT2OID;
41+
return DirectFunctionCall1(int2in, CStringGetDatum(TextDatumGetCString(textarg)));
42+
}
43+
case INT4OID:
44+
{
45+
*interval_type = INT4OID;
46+
return DirectFunctionCall1(int4in, CStringGetDatum(TextDatumGetCString(textarg)));
47+
}
48+
case INT8OID:
49+
{
50+
*interval_type = INT8OID;
51+
return DirectFunctionCall1(int8in, CStringGetDatum(TextDatumGetCString(textarg)));
52+
}
53+
case TIMESTAMPOID:
54+
case TIMESTAMPTZOID:
55+
case DATEOID:
56+
{
57+
*interval_type = INTERVALOID;
58+
return DirectFunctionCall3(interval_in,
59+
CStringGetDatum(TextDatumGetCString(textarg)),
60+
InvalidOid,
61+
-1);
62+
}
63+
}
64+
}
65+
interval_type = InvalidOid;
66+
return -1;
67+
}

src/create_table_with_clause.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ typedef enum CreateTableFlags
1313
{
1414
CreateTableFlagHypertable = 0,
1515
CreateTableFlagTimeColumn,
16+
CreateTableFlagChunkTimeInterval,
1617
} CreateTableFlags;
1718

1819
WithClauseResult *ts_create_table_with_clause_parse(const List *defelems);
20+
21+
Datum ts_create_table_parse_chunk_time_interval(WithClauseResult *parsed_options, Oid column_type,
22+
Oid *interval_type);

src/process_utility.c

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3682,7 +3682,7 @@ process_cluster_start(ProcessUtilityArgs *args)
36823682
typedef struct CreateTableInfo
36833683
{
36843684
bool hypertable;
3685-
NameData time_column;
3685+
WithClauseResult *with_clauses;
36863686
} CreateTableInfo;
36873687

36883688
static CreateTableInfo create_table_info = { 0 };
@@ -3734,19 +3734,36 @@ process_create_table_end(Node *parsetree)
37343734
if (create_table_info.hypertable)
37353735
{
37363736
Oid table_relid = RangeVarGetRelid(stmt->relation, NoLock, true);
3737+
char *time_column =
3738+
TextDatumGetCString(create_table_info.with_clauses[CreateTableFlagTimeColumn].parsed);
3739+
NameData time_column_name;
3740+
namestrcpy(&time_column_name, time_column);
3741+
3742+
Oid interval_type = InvalidOid;
3743+
Datum interval = -1;
3744+
3745+
if (!create_table_info.with_clauses[CreateTableFlagChunkTimeInterval].is_default)
3746+
{
3747+
AttrNumber time_attno = get_attnum(table_relid, time_column);
3748+
Oid time_type = get_atttype(table_relid, time_attno);
3749+
3750+
interval = ts_create_table_parse_chunk_time_interval(create_table_info.with_clauses,
3751+
time_type,
3752+
&interval_type);
3753+
}
37373754

37383755
DimensionInfo *open_dim_info =
37393756
ts_dimension_info_create_open(table_relid,
3740-
&create_table_info.time_column, /* column name */
3741-
-1, /* interval */
3742-
InvalidOid, /* interval type */
3743-
InvalidOid /* partitioning func */
3757+
&time_column_name, /* column name */
3758+
interval, /* interval */
3759+
interval_type, /* interval type */
3760+
InvalidOid /* partitioning func */
37443761
);
37453762

37463763
ChunkSizingInfo chunk_sizing_info = {
37473764
.table_relid = table_relid,
37483765
.func = get_sizing_func_oid(),
3749-
.colname = NameStr(create_table_info.time_column),
3766+
.colname = time_column,
37503767
};
37513768

37523769
ts_hypertable_create_from_info(table_relid,
@@ -3757,7 +3774,6 @@ process_create_table_end(Node *parsetree)
37573774
NULL, /* associated_schema_name */
37583775
NULL, /* associated_table_prefix */
37593776
&chunk_sizing_info);
3760-
create_table_info.hypertable = false;
37613777
}
37623778
}
37633779

@@ -4984,6 +5000,8 @@ process_create_stmt(ProcessUtilityArgs *args)
49845000
ts_with_clause_filter(stmt->options, &hypertable_options, &pg_options);
49855001
stmt->options = pg_options;
49865002

5003+
create_table_info.hypertable = false;
5004+
create_table_info.with_clauses = NULL;
49875005
/*
49885006
* We can only convert the table into a hypertable after postgres has created
49895007
* the initial table so we store the information passed in the WITH clause
@@ -4992,28 +5010,24 @@ process_create_stmt(ProcessUtilityArgs *args)
49925010
*/
49935011
if (hypertable_options)
49945012
{
4995-
WithClauseResult *parsed_with_clauses =
4996-
ts_create_table_with_clause_parse(hypertable_options);
5013+
create_table_info.with_clauses = ts_create_table_with_clause_parse(hypertable_options);
49975014
create_table_info.hypertable =
4998-
DatumGetBool(parsed_with_clauses[CreateTableFlagHypertable].parsed);
5015+
DatumGetBool(create_table_info.with_clauses[CreateTableFlagHypertable].parsed);
49995016

5000-
if (!parsed_with_clauses[CreateTableFlagHypertable].parsed)
5017+
if (!create_table_info.with_clauses[CreateTableFlagHypertable].parsed)
50015018
ereport(ERROR,
50025019
(errcode(ERRCODE_UNDEFINED_COLUMN),
50035020
errmsg("timescaledb options requires hypertable option"),
50045021
errhint("Use \"timescaledb.hypertable\" to enable creating a hypertable.")));
50055022

50065023
if (create_table_info.hypertable)
50075024
{
5008-
if (!parsed_with_clauses[CreateTableFlagTimeColumn].parsed)
5025+
if (!create_table_info.with_clauses[CreateTableFlagTimeColumn].parsed)
50095026
ereport(ERROR,
50105027
(errcode(ERRCODE_UNDEFINED_COLUMN),
50115028
errmsg("hypertable option requires time_column"),
50125029
errhint("Use \"timescaledb.time_column\" to specify the column to use as "
50135030
"partitioning column.")));
5014-
5015-
namestrcpy(&create_table_info.time_column,
5016-
TextDatumGetCString(parsed_with_clauses[CreateTableFlagTimeColumn].parsed));
50175031
}
50185032
}
50195033

test/expected/create_table_with.out

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
-- LICENSE-APACHE for a copy of the license.
44
-- create table with non-tsdb option should not be affected
55
CREATE TABLE t1(time timestamptz, device text, value float) WITH (autovacuum_enabled);
6+
DROP TABLE t1;
67
-- test error cases
78
\set ON_ERROR_STOP 0
89
CREATE TABLE t2(time timestamptz, device text, value float) WITH (tsdb.hypertable);
@@ -19,20 +20,45 @@ CREATE TABLE t2(time timestamptz, device text, value float) WITH (tsdb.time_colu
1920
ERROR: timescaledb options requires hypertable option
2021
CREATE TABLE t2(time timestamptz, device text, value float) WITH (timescaledb.time_column='time');
2122
ERROR: timescaledb options requires hypertable option
23+
CREATE TABLE t2(time timestamptz , device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='foo');
24+
ERROR: invalid input syntax for type interval: "foo"
25+
CREATE TABLE t2(time int2 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='3 months');
26+
ERROR: invalid input syntax for type smallint: "3 months"
2227
\set ON_ERROR_STOP 1
28+
BEGIN;
2329
CREATE TABLE t3(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
2430
CREATE TABLE t4(time timestamp, device text, value float) WITH (tsdb.hypertable,timescaledb.time_column='time');
2531
WARNING: column type "timestamp without time zone" used for "time" does not follow best practices
2632
NOTICE: adding not-null constraint to column "time"
2733
CREATE TABLE t5(time date, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',autovacuum_enabled);
2834
NOTICE: adding not-null constraint to column "time"
2935
CREATE TABLE t6(time timestamptz NOT NULL, device text, value float) WITH (timescaledb.hypertable,tsdb.time_column='time');
36+
SELECT hypertable_name FROM timescaledb_information.hypertables ORDER BY 1;
37+
hypertable_name
38+
-----------------
39+
t3
40+
t4
41+
t5
42+
t6
43+
(4 rows)
44+
45+
ROLLBACK;
3046
-- IF NOT EXISTS
47+
BEGIN;
3148
CREATE TABLE IF NOT EXISTS t7(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
3249
CREATE TABLE IF NOT EXISTS t7(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
3350
NOTICE: relation "t7" already exists, skipping
3451
CREATE TABLE IF NOT EXISTS t7(time timestamptz NOT NULL, device text, value float);
3552
NOTICE: relation "t7" already exists, skipping
53+
SELECT hypertable_name FROM timescaledb_information.hypertables ORDER BY 1;
54+
hypertable_name
55+
-----------------
56+
t7
57+
(1 row)
58+
59+
ROLLBACK;
60+
-- table won't be converted to hypertable unless it is in the initial CREATE TABLE
61+
BEGIN;
3662
CREATE TABLE IF NOT EXISTS t8(time timestamptz NOT NULL, device text, value float);
3763
CREATE TABLE IF NOT EXISTS t8(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
3864
NOTICE: relation "t8" already exists, skipping
@@ -41,11 +67,62 @@ NOTICE: relation "t8" already exists, skipping
4167
SELECT hypertable_name FROM timescaledb_information.hypertables ORDER BY 1;
4268
hypertable_name
4369
-----------------
44-
t3
45-
t4
46-
t5
47-
t6
48-
t7
49-
t8
50-
(6 rows)
70+
(0 rows)
71+
72+
ROLLBACK;
73+
-- chunk_time_interval
74+
BEGIN;
75+
CREATE TABLE IF NOT EXISTS t9(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='8weeks');
76+
SELECT hypertable_name, column_name, column_type, time_interval FROM timescaledb_information.dimensions;
77+
hypertable_name | column_name | column_type | time_interval
78+
-----------------+-------------+--------------------------+---------------
79+
t9 | time | timestamp with time zone | @ 56 days
80+
(1 row)
81+
82+
ROLLBACK;
83+
BEGIN;
84+
CREATE TABLE IF NOT EXISTS t9(time timestamp NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='23 days');
85+
WARNING: column type "timestamp without time zone" used for "time" does not follow best practices
86+
SELECT hypertable_name, column_name, column_type, time_interval FROM timescaledb_information.dimensions;
87+
hypertable_name | column_name | column_type | time_interval
88+
-----------------+-------------+-----------------------------+---------------
89+
t9 | time | timestamp without time zone | @ 23 days
90+
(1 row)
91+
92+
ROLLBACK;
93+
BEGIN;
94+
CREATE TABLE IF NOT EXISTS t9(time date NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='3 months');
95+
SELECT hypertable_name, column_name, column_type, time_interval FROM timescaledb_information.dimensions;
96+
hypertable_name | column_name | column_type | time_interval
97+
-----------------+-------------+-------------+---------------
98+
t9 | time | date | @ 90 days
99+
(1 row)
100+
101+
ROLLBACK;
102+
BEGIN;
103+
CREATE TABLE IF NOT EXISTS t9(time int2 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval=12);
104+
SELECT hypertable_name, column_name, column_type, integer_interval FROM timescaledb_information.dimensions;
105+
hypertable_name | column_name | column_type | integer_interval
106+
-----------------+-------------+-------------+------------------
107+
t9 | time | smallint | 12
108+
(1 row)
109+
110+
ROLLBACK;
111+
BEGIN;
112+
CREATE TABLE IF NOT EXISTS t9(time int4 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval=3453);
113+
SELECT hypertable_name, column_name, column_type, integer_interval FROM timescaledb_information.dimensions;
114+
hypertable_name | column_name | column_type | integer_interval
115+
-----------------+-------------+-------------+------------------
116+
t9 | time | integer | 3453
117+
(1 row)
118+
119+
ROLLBACK;
120+
BEGIN;
121+
CREATE TABLE IF NOT EXISTS t9(time int8 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval=32768);
122+
SELECT hypertable_name, column_name, column_type, integer_interval FROM timescaledb_information.dimensions;
123+
hypertable_name | column_name | column_type | integer_interval
124+
-----------------+-------------+-------------+------------------
125+
t9 | time | bigint | 32768
126+
(1 row)
51127

128+
ROLLBACK;

test/sql/create_table_with.sql

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
-- create table with non-tsdb option should not be affected
66
CREATE TABLE t1(time timestamptz, device text, value float) WITH (autovacuum_enabled);
7+
DROP TABLE t1;
78

89
-- test error cases
910
\set ON_ERROR_STOP 0
@@ -14,22 +15,66 @@ CREATE TABLE t2(time timestamptz, device text, value float) WITH (tsdb.hypertabl
1415
CREATE TABLE t2(time timestamptz, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='foo');
1516
CREATE TABLE t2(time timestamptz, device text, value float) WITH (tsdb.time_column='time');
1617
CREATE TABLE t2(time timestamptz, device text, value float) WITH (timescaledb.time_column='time');
18+
CREATE TABLE t2(time timestamptz , device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='foo');
19+
CREATE TABLE t2(time int2 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='3 months');
1720
\set ON_ERROR_STOP 1
1821

1922

23+
BEGIN;
2024
CREATE TABLE t3(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
2125
CREATE TABLE t4(time timestamp, device text, value float) WITH (tsdb.hypertable,timescaledb.time_column='time');
2226
CREATE TABLE t5(time date, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',autovacuum_enabled);
2327
CREATE TABLE t6(time timestamptz NOT NULL, device text, value float) WITH (timescaledb.hypertable,tsdb.time_column='time');
2428

29+
SELECT hypertable_name FROM timescaledb_information.hypertables ORDER BY 1;
30+
ROLLBACK;
31+
2532
-- IF NOT EXISTS
33+
BEGIN;
2634
CREATE TABLE IF NOT EXISTS t7(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
2735
CREATE TABLE IF NOT EXISTS t7(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
2836
CREATE TABLE IF NOT EXISTS t7(time timestamptz NOT NULL, device text, value float);
2937

38+
SELECT hypertable_name FROM timescaledb_information.hypertables ORDER BY 1;
39+
ROLLBACK;
40+
41+
-- table won't be converted to hypertable unless it is in the initial CREATE TABLE
42+
BEGIN;
3043
CREATE TABLE IF NOT EXISTS t8(time timestamptz NOT NULL, device text, value float);
3144
CREATE TABLE IF NOT EXISTS t8(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time');
3245
CREATE TABLE IF NOT EXISTS t8(time timestamptz NOT NULL, device text, value float);
3346

3447
SELECT hypertable_name FROM timescaledb_information.hypertables ORDER BY 1;
48+
ROLLBACK;
49+
50+
-- chunk_time_interval
51+
BEGIN;
52+
CREATE TABLE IF NOT EXISTS t9(time timestamptz NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='8weeks');
53+
SELECT hypertable_name, column_name, column_type, time_interval FROM timescaledb_information.dimensions;
54+
ROLLBACK;
55+
56+
BEGIN;
57+
CREATE TABLE IF NOT EXISTS t9(time timestamp NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='23 days');
58+
SELECT hypertable_name, column_name, column_type, time_interval FROM timescaledb_information.dimensions;
59+
ROLLBACK;
60+
61+
BEGIN;
62+
CREATE TABLE IF NOT EXISTS t9(time date NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval='3 months');
63+
SELECT hypertable_name, column_name, column_type, time_interval FROM timescaledb_information.dimensions;
64+
ROLLBACK;
65+
66+
BEGIN;
67+
CREATE TABLE IF NOT EXISTS t9(time int2 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval=12);
68+
SELECT hypertable_name, column_name, column_type, integer_interval FROM timescaledb_information.dimensions;
69+
ROLLBACK;
70+
71+
BEGIN;
72+
CREATE TABLE IF NOT EXISTS t9(time int4 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval=3453);
73+
SELECT hypertable_name, column_name, column_type, integer_interval FROM timescaledb_information.dimensions;
74+
ROLLBACK;
75+
76+
BEGIN;
77+
CREATE TABLE IF NOT EXISTS t9(time int8 NOT NULL, device text, value float) WITH (tsdb.hypertable,tsdb.time_column='time',tsdb.chunk_time_interval=32768);
78+
SELECT hypertable_name, column_name, column_type, integer_interval FROM timescaledb_information.dimensions;
79+
ROLLBACK;
3580

0 commit comments

Comments
 (0)