summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorRobert Haas2017-11-21 18:06:32 +0000
committerRobert Haas2017-11-21 18:06:32 +0000
commitf3b0897a1213f46b4d3a99a7f8ef3a4b32e03572 (patch)
tree16559e0bebf8f5dd8a44a58af31abf4bd18123a6 /src/test
parentde1d042f5979bc1388e9a6d52a4d445342b04932 (diff)
Fix multiple problems with satisfies_hash_partition.
Fix the function header comment to describe the actual behavior. Check that table OID, modulus, and remainder arguments are not NULL before accessing them. Check that the modulus and remainder are sensible. If the table OID doesn't exist, return NULL instead of emitting an internal error, similar to what we do elsewhere. Check that the actual argument types match, or at least are binary coercible to, the expected argument types. Correctly handle invocation of this function using the VARIADIC syntax. Add regression tests. Robert Haas and Amul Sul, per a report by Andreas Seltenreich and subsequent followup investigation. Discussion: http://postgr.es/m/871sl4sdrv.fsf@ansel.ydns.eu
Diffstat (limited to 'src/test')
-rw-r--r--src/test/regress/expected/hash_part.out113
-rw-r--r--src/test/regress/parallel_schedule2
-rw-r--r--src/test/regress/serial_schedule1
-rw-r--r--src/test/regress/sql/hash_part.sql90
4 files changed, 205 insertions, 1 deletions
diff --git a/src/test/regress/expected/hash_part.out b/src/test/regress/expected/hash_part.out
new file mode 100644
index 00000000000..9e9e56f6fc4
--- /dev/null
+++ b/src/test/regress/expected/hash_part.out
@@ -0,0 +1,113 @@
+--
+-- Hash partitioning.
+--
+CREATE OR REPLACE FUNCTION hashint4_noop(int4, int8) RETURNS int8 AS
+$$SELECT coalesce($1,0)::int8$$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING HASH AS
+OPERATOR 1 = , FUNCTION 2 hashint4_noop(int4, int8);
+CREATE OR REPLACE FUNCTION hashtext_length(text, int8) RETURNS int8 AS
+$$SELECT length(coalesce($1,''))::int8$$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR CLASS test_text_ops FOR TYPE text USING HASH AS
+OPERATOR 1 = , FUNCTION 2 hashtext_length(text, int8);
+CREATE TABLE mchash (a int, b text, c jsonb)
+ PARTITION BY HASH (a test_int4_ops, b test_text_ops);
+CREATE TABLE mchash1
+ PARTITION OF mchash FOR VALUES WITH (MODULUS 4, REMAINDER 0);
+-- invalid OID, no such table
+SELECT satisfies_hash_partition(0, 4, 0, NULL);
+ satisfies_hash_partition
+--------------------------
+
+(1 row)
+
+-- not partitioned
+SELECT satisfies_hash_partition('tenk1'::regclass, 4, 0, NULL);
+ERROR: "tenk1" is not a hash partitioned table
+-- partition rather than the parent
+SELECT satisfies_hash_partition('mchash1'::regclass, 4, 0, NULL);
+ERROR: "mchash1" is not a hash partitioned table
+-- invalid modulus
+SELECT satisfies_hash_partition('mchash'::regclass, 0, 0, NULL);
+ERROR: modulus for hash partition must be a positive integer
+-- remainder too small
+SELECT satisfies_hash_partition('mchash'::regclass, 1, -1, NULL);
+ERROR: remainder for hash partition must be a non-negative integer
+-- remainder too large
+SELECT satisfies_hash_partition('mchash'::regclass, 1, 1, NULL);
+ERROR: remainder for hash partition must be less than modulus
+-- modulus is null
+SELECT satisfies_hash_partition('mchash'::regclass, NULL, 0, NULL);
+ satisfies_hash_partition
+--------------------------
+
+(1 row)
+
+-- remainder is null
+SELECT satisfies_hash_partition('mchash'::regclass, 4, NULL, NULL);
+ satisfies_hash_partition
+--------------------------
+
+(1 row)
+
+-- too many arguments
+SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, NULL::int, NULL::text, NULL::json);
+ERROR: number of partitioning columns (2) does not match number of partition keys provided (3)
+-- too few arguments
+SELECT satisfies_hash_partition('mchash'::regclass, 3, 1, NULL::int);
+ERROR: number of partitioning columns (2) does not match number of partition keys provided (1)
+-- wrong argument type
+SELECT satisfies_hash_partition('mchash'::regclass, 2, 1, NULL::int, NULL::int);
+ERROR: column 2 of the partition key has type "text", but supplied value is of type "integer"
+-- ok, should be false
+SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 0, ''::text);
+ satisfies_hash_partition
+--------------------------
+ f
+(1 row)
+
+-- ok, should be true
+SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 1, ''::text);
+ satisfies_hash_partition
+--------------------------
+ t
+(1 row)
+
+-- argument via variadic syntax, should fail because not all partitioning
+-- columns are of the correct type
+SELECT satisfies_hash_partition('mchash'::regclass, 2, 1,
+ variadic array[1,2]::int[]);
+ERROR: column 2 of the partition key has type "text", but supplied value is of type "integer"
+-- multiple partitioning columns of the same type
+CREATE TABLE mcinthash (a int, b int, c jsonb)
+ PARTITION BY HASH (a test_int4_ops, b test_int4_ops);
+-- now variadic should work, should be false
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[0, 0]);
+ satisfies_hash_partition
+--------------------------
+ f
+(1 row)
+
+-- should be true
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[1, 0]);
+ satisfies_hash_partition
+--------------------------
+ t
+(1 row)
+
+-- wrong length
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[]::int[]);
+ERROR: number of partitioning columns (2) does not match number of partition keys provided (0)
+-- wrong type
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[now(), now()]);
+ERROR: column 1 of the partition key has type "integer", but supplied value is of type "timestamp with time zone"
+-- cleanup
+DROP TABLE mchash;
+DROP TABLE mcinthash;
+DROP OPERATOR CLASS test_text_ops USING hash;
+DROP OPERATOR CLASS test_int4_ops USING hash;
+DROP FUNCTION hashint4_noop(int4, int8);
+DROP FUNCTION hashtext_length(text, int8);
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index aa5e6af6218..1a3ac4c1f94 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -116,7 +116,7 @@ test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid c
# ----------
# Another group of parallel tests
# ----------
-test: identity partition_join reloptions
+test: identity partition_join reloptions hash_part
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 3866314a922..a205e5d05c7 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -181,5 +181,6 @@ test: xml
test: identity
test: partition_join
test: reloptions
+test: hash_part
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/hash_part.sql b/src/test/regress/sql/hash_part.sql
new file mode 100644
index 00000000000..94c5eaab0cd
--- /dev/null
+++ b/src/test/regress/sql/hash_part.sql
@@ -0,0 +1,90 @@
+--
+-- Hash partitioning.
+--
+
+CREATE OR REPLACE FUNCTION hashint4_noop(int4, int8) RETURNS int8 AS
+$$SELECT coalesce($1,0)::int8$$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR CLASS test_int4_ops FOR TYPE int4 USING HASH AS
+OPERATOR 1 = , FUNCTION 2 hashint4_noop(int4, int8);
+
+CREATE OR REPLACE FUNCTION hashtext_length(text, int8) RETURNS int8 AS
+$$SELECT length(coalesce($1,''))::int8$$ LANGUAGE sql IMMUTABLE;
+CREATE OPERATOR CLASS test_text_ops FOR TYPE text USING HASH AS
+OPERATOR 1 = , FUNCTION 2 hashtext_length(text, int8);
+
+CREATE TABLE mchash (a int, b text, c jsonb)
+ PARTITION BY HASH (a test_int4_ops, b test_text_ops);
+CREATE TABLE mchash1
+ PARTITION OF mchash FOR VALUES WITH (MODULUS 4, REMAINDER 0);
+
+-- invalid OID, no such table
+SELECT satisfies_hash_partition(0, 4, 0, NULL);
+
+-- not partitioned
+SELECT satisfies_hash_partition('tenk1'::regclass, 4, 0, NULL);
+
+-- partition rather than the parent
+SELECT satisfies_hash_partition('mchash1'::regclass, 4, 0, NULL);
+
+-- invalid modulus
+SELECT satisfies_hash_partition('mchash'::regclass, 0, 0, NULL);
+
+-- remainder too small
+SELECT satisfies_hash_partition('mchash'::regclass, 1, -1, NULL);
+
+-- remainder too large
+SELECT satisfies_hash_partition('mchash'::regclass, 1, 1, NULL);
+
+-- modulus is null
+SELECT satisfies_hash_partition('mchash'::regclass, NULL, 0, NULL);
+
+-- remainder is null
+SELECT satisfies_hash_partition('mchash'::regclass, 4, NULL, NULL);
+
+-- too many arguments
+SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, NULL::int, NULL::text, NULL::json);
+
+-- too few arguments
+SELECT satisfies_hash_partition('mchash'::regclass, 3, 1, NULL::int);
+
+-- wrong argument type
+SELECT satisfies_hash_partition('mchash'::regclass, 2, 1, NULL::int, NULL::int);
+
+-- ok, should be false
+SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 0, ''::text);
+
+-- ok, should be true
+SELECT satisfies_hash_partition('mchash'::regclass, 4, 0, 1, ''::text);
+
+-- argument via variadic syntax, should fail because not all partitioning
+-- columns are of the correct type
+SELECT satisfies_hash_partition('mchash'::regclass, 2, 1,
+ variadic array[1,2]::int[]);
+
+-- multiple partitioning columns of the same type
+CREATE TABLE mcinthash (a int, b int, c jsonb)
+ PARTITION BY HASH (a test_int4_ops, b test_int4_ops);
+
+-- now variadic should work, should be false
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[0, 0]);
+
+-- should be true
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[1, 0]);
+
+-- wrong length
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[]::int[]);
+
+-- wrong type
+SELECT satisfies_hash_partition('mcinthash'::regclass, 4, 0,
+ variadic array[now(), now()]);
+
+-- cleanup
+DROP TABLE mchash;
+DROP TABLE mcinthash;
+DROP OPERATOR CLASS test_text_ops USING hash;
+DROP OPERATOR CLASS test_int4_ops USING hash;
+DROP FUNCTION hashint4_noop(int4, int8);
+DROP FUNCTION hashtext_length(text, int8);