Skip to content

Commit e7f8c5a

Browse files
committed
Remove lowering for expression, add additional checks for expression before any actions
1 parent 1192aea commit e7f8c5a

9 files changed

+83
-13
lines changed

expected/pathman_basic.out

+2-2
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,11 @@ CREATE TABLE test.range_rel (
129129
CREATE INDEX ON test.range_rel (dt);
130130
INSERT INTO test.range_rel (dt, txt)
131131
SELECT g, md5(g::TEXT) FROM generate_series('2015-01-01', '2015-04-30', '1 day'::interval) as g;
132-
SELECT pathman.create_range_partitions('test.range_rel', 'dt', '2015-01-01'::DATE, '1 month'::INTERVAL, 2);
133-
ERROR: not enough partitions to fit all values of "dt"
134132
SELECT pathman.create_range_partitions('test.range_rel', 'dt', '2015-01-01'::DATE, '1 month'::INTERVAL);
135133
ERROR: partitioning key "dt" must be marked NOT NULL
136134
ALTER TABLE test.range_rel ALTER COLUMN dt SET NOT NULL;
135+
SELECT pathman.create_range_partitions('test.range_rel', 'dt', '2015-01-01'::DATE, '1 month'::INTERVAL, 2);
136+
ERROR: not enough partitions to fit all values of "dt"
137137
SELECT pathman.create_range_partitions('test.range_rel', 'DT', '2015-01-01'::DATE, '1 month'::INTERVAL);
138138
create_range_partitions
139139
-------------------------

expected/pathman_calamity.out

+15
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,21 @@ SELECT validate_relname(1::REGCLASS);
296296
ERROR: relation "1" does not exist
297297
SELECT validate_relname(NULL);
298298
ERROR: relation should not be NULL
299+
/* check function validate_expression() */
300+
SELECT validate_expression('calamity.part_test');
301+
ERROR: function validate_expression(unknown) does not exist at character 51
302+
SELECT validate_expression('calamity.part_test', NULL);
303+
ERROR: 'expression' should not be NULL
304+
SELECT validate_expression('calamity.part_test', 'valval');
305+
ERROR: cannot find type name for attribute "valval" of relation "part_test"
306+
SELECT validate_expression('calamity.part_test', 'random()');
307+
ERROR: functions in partitioning expression must be marked IMMUTABLE
308+
SELECT validate_expression('calamity.part_test', 'val');
309+
validate_expression
310+
---------------------
311+
312+
(1 row)
313+
299314
/* check function get_number_of_partitions() */
300315
SELECT get_number_of_partitions('calamity.part_test');
301316
get_number_of_partitions

expected/pathman_expressions.out

+15-5
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,28 @@ SELECT COUNT(*) FROM test_exprs.hash_rel;
1616
5
1717
(1 row)
1818

19+
SELECT create_hash_partitions('test_exprs.hash_rel', 'random()', 4);
20+
ERROR: functions in partitioning expression must be marked IMMUTABLE
1921
\set VERBOSITY default
2022
SELECT create_hash_partitions('test_exprs.hash_rel', 'value * value2))', 4);
2123
ERROR: partitioning expression parse error
2224
DETAIL: syntax error at or near ")"
23-
QUERY: SELECT public.add_to_pathman_config(parent_relid, expression)
24-
CONTEXT: PL/pgSQL function create_hash_partitions(regclass,text,integer,boolean,text[],text[]) line 9 at PERFORM
25+
QUERY: SELECT public.validate_expression(parent_relid, expression)
26+
CONTEXT: PL/pgSQL function prepare_for_partitioning(regclass,text,boolean) line 9 at PERFORM
27+
SQL statement "SELECT public.prepare_for_partitioning(parent_relid,
28+
expression,
29+
partition_data)"
30+
PL/pgSQL function create_hash_partitions(regclass,text,integer,boolean,text[],text[]) line 4 at PERFORM
2531
SELECT create_hash_partitions('test_exprs.hash_rel', 'value * value3', 4);
2632
ERROR: partitioning expression analyze error
2733
DETAIL: column "value3" does not exist
2834
HINT: Perhaps you meant to reference the column "hash_rel.value" or the column "hash_rel.value2".
29-
QUERY: SELECT public.add_to_pathman_config(parent_relid, expression)
30-
CONTEXT: PL/pgSQL function create_hash_partitions(regclass,text,integer,boolean,text[],text[]) line 9 at PERFORM
35+
QUERY: SELECT public.validate_expression(parent_relid, expression)
36+
CONTEXT: PL/pgSQL function prepare_for_partitioning(regclass,text,boolean) line 9 at PERFORM
37+
SQL statement "SELECT public.prepare_for_partitioning(parent_relid,
38+
expression,
39+
partition_data)"
40+
PL/pgSQL function create_hash_partitions(regclass,text,integer,boolean,text[],text[]) line 4 at PERFORM
3141
\set VERBOSITY terse
3242
SELECT create_hash_partitions('test_exprs.hash_rel', 'value * value2', 4);
3343
create_hash_partitions
@@ -88,7 +98,7 @@ CREATE TABLE test_exprs.range_rel (id SERIAL PRIMARY KEY, dt TIMESTAMP, txt TEXT
8898
INSERT INTO test_exprs.range_rel (dt, txt)
8999
SELECT g, md5(g::TEXT) FROM generate_series('2015-01-01', '2020-04-30', '1 month'::interval) as g;
90100
SELECT create_range_partitions('test_exprs.range_rel', 'RANDOM()', '15 years'::INTERVAL, '1 year'::INTERVAL, 10);
91-
ERROR: start value is less than min value of "random()"
101+
ERROR: functions in partitioning expression must be marked IMMUTABLE
92102
SELECT create_range_partitions('test_exprs.range_rel', 'AGE(dt, ''2000-01-01''::DATE)',
93103
'15 years'::INTERVAL, '1 year'::INTERVAL, 10);
94104
create_range_partitions

init.sql

+10
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ DECLARE
434434

435435
BEGIN
436436
PERFORM @extschema@.validate_relname(parent_relid);
437+
PERFORM @extschema@.validate_expression(parent_relid, expression);
437438

438439
IF partition_data = true THEN
439440
/* Acquire data modification lock */
@@ -830,6 +831,15 @@ CREATE OR REPLACE FUNCTION @extschema@.validate_relname(
830831
RETURNS VOID AS 'pg_pathman', 'validate_relname'
831832
LANGUAGE C;
832833

834+
/*
835+
* Check that expression is valid
836+
*/
837+
CREATE OR REPLACE FUNCTION @extschema@.validate_expression(
838+
relid REGCLASS,
839+
expression TEXT)
840+
RETURNS VOID AS 'pg_pathman', 'validate_expression'
841+
LANGUAGE C;
842+
833843
/*
834844
* Check if regclass is date or timestamp.
835845
*/

range.sql

-5
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ DECLARE
6767
i INTEGER;
6868

6969
BEGIN
70-
expression := lower(expression);
7170
PERFORM @extschema@.prepare_for_partitioning(parent_relid,
7271
expression,
7372
partition_data);
@@ -167,7 +166,6 @@ DECLARE
167166
i INTEGER;
168167

169168
BEGIN
170-
expression := lower(expression);
171169
PERFORM @extschema@.prepare_for_partitioning(parent_relid,
172170
expression,
173171
partition_data);
@@ -268,7 +266,6 @@ BEGIN
268266
RAISE EXCEPTION 'Bounds array must have at least two values';
269267
END IF;
270268

271-
expression := lower(expression);
272269
PERFORM @extschema@.prepare_for_partitioning(parent_relid,
273270
expression,
274271
partition_data);
@@ -319,7 +316,6 @@ DECLARE
319316
part_count INTEGER := 0;
320317

321318
BEGIN
322-
expression := lower(expression);
323319
PERFORM @extschema@.prepare_for_partitioning(parent_relid,
324320
expression,
325321
partition_data);
@@ -376,7 +372,6 @@ DECLARE
376372
part_count INTEGER := 0;
377373

378374
BEGIN
379-
expression := lower(expression);
380375
PERFORM @extschema@.prepare_for_partitioning(parent_relid,
381376
expression,
382377
partition_data);

sql/pathman_basic.sql

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ CREATE TABLE test.range_rel (
3939
CREATE INDEX ON test.range_rel (dt);
4040
INSERT INTO test.range_rel (dt, txt)
4141
SELECT g, md5(g::TEXT) FROM generate_series('2015-01-01', '2015-04-30', '1 day'::interval) as g;
42-
SELECT pathman.create_range_partitions('test.range_rel', 'dt', '2015-01-01'::DATE, '1 month'::INTERVAL, 2);
4342
SELECT pathman.create_range_partitions('test.range_rel', 'dt', '2015-01-01'::DATE, '1 month'::INTERVAL);
4443
ALTER TABLE test.range_rel ALTER COLUMN dt SET NOT NULL;
44+
SELECT pathman.create_range_partitions('test.range_rel', 'dt', '2015-01-01'::DATE, '1 month'::INTERVAL, 2);
4545
SELECT pathman.create_range_partitions('test.range_rel', 'DT', '2015-01-01'::DATE, '1 month'::INTERVAL);
4646
SELECT COUNT(*) FROM test.range_rel;
4747
SELECT COUNT(*) FROM ONLY test.range_rel;

sql/pathman_calamity.sql

+7
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ SELECT validate_relname('calamity.part_test');
140140
SELECT validate_relname(1::REGCLASS);
141141
SELECT validate_relname(NULL);
142142

143+
/* check function validate_expression() */
144+
SELECT validate_expression('calamity.part_test');
145+
SELECT validate_expression('calamity.part_test', NULL);
146+
SELECT validate_expression('calamity.part_test', 'valval');
147+
SELECT validate_expression('calamity.part_test', 'random()');
148+
SELECT validate_expression('calamity.part_test', 'val');
149+
143150
/* check function get_number_of_partitions() */
144151
SELECT get_number_of_partitions('calamity.part_test');
145152
SELECT get_number_of_partitions(NULL) IS NULL;

sql/pathman_expressions.sql

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ INSERT INTO test_exprs.hash_rel (value, value2)
1515
SELECT val, val * 2 FROM generate_series(1, 5) val;
1616

1717
SELECT COUNT(*) FROM test_exprs.hash_rel;
18+
SELECT create_hash_partitions('test_exprs.hash_rel', 'random()', 4);
1819
\set VERBOSITY default
1920
SELECT create_hash_partitions('test_exprs.hash_rel', 'value * value2))', 4);
2021
SELECT create_hash_partitions('test_exprs.hash_rel', 'value * value3', 4);

src/pl_funcs.c

+32
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ PG_FUNCTION_INFO_V1( build_update_trigger_func_name );
5757
PG_FUNCTION_INFO_V1( build_check_constraint_name );
5858

5959
PG_FUNCTION_INFO_V1( validate_relname );
60+
PG_FUNCTION_INFO_V1( validate_expression );
6061
PG_FUNCTION_INFO_V1( is_date_type );
6162
PG_FUNCTION_INFO_V1( is_operator_supported );
6263
PG_FUNCTION_INFO_V1( is_tuple_convertible );
@@ -595,6 +596,37 @@ validate_relname(PG_FUNCTION_ARGS)
595596
PG_RETURN_VOID();
596597
}
597598

599+
/*
600+
* Validate a partitioning expression
601+
* We need this in range functions because we do many things
602+
* before actual partitioning.
603+
*/
604+
Datum
605+
validate_expression(PG_FUNCTION_ARGS)
606+
{
607+
Oid relid;
608+
char *expression;
609+
610+
/* Fetch relation's Oid */
611+
relid = PG_GETARG_OID(0);
612+
613+
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
614+
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
615+
errmsg("relation \"%u\" does not exist", relid),
616+
errdetail("triggered in function "
617+
CppAsString(validate_expression))));
618+
619+
if (!PG_ARGISNULL(1))
620+
{
621+
expression = TextDatumGetCString(PG_GETARG_TEXT_P(1));
622+
}
623+
else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
624+
errmsg("'expression' should not be NULL")));
625+
626+
cook_partitioning_expression(relid, expression, NULL);
627+
PG_RETURN_VOID();
628+
}
629+
598630
Datum
599631
is_date_type(PG_FUNCTION_ARGS)
600632
{

0 commit comments

Comments
 (0)