Skip to content

Commit 0dead52

Browse files
committed
replace_hash_partition() function
1 parent d586756 commit 0dead52

File tree

8 files changed

+288
-23
lines changed

8 files changed

+288
-23
lines changed

expected/pathman_basic.out

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,14 +885,58 @@ SELECT * FROM test.hash_rel WHERE id = 123;
885885
123 | 456 | 789
886886
(1 row)
887887

888+
/* Test replacing hash partition */
889+
CREATE TABLE test.hash_rel_extern (LIKE test.hash_rel INCLUDING ALL);
890+
SELECT pathman.replace_hash_partition('test.hash_rel_0', 'test.hash_rel_extern');
891+
replace_hash_partition
892+
------------------------
893+
test.hash_rel_extern
894+
(1 row)
895+
896+
\d+ test.hash_rel_0
897+
Table "test.hash_rel_0"
898+
Column | Type | Modifiers | Storage | Stats target | Description
899+
--------+---------+------------------------------------------------------------+---------+--------------+-------------
900+
id | integer | not null default nextval('test.hash_rel_id_seq'::regclass) | plain | |
901+
value | integer | not null | plain | |
902+
abc | integer | | plain | |
903+
Indexes:
904+
"hash_rel_0_pkey" PRIMARY KEY, btree (id)
905+
Triggers:
906+
hash_rel_upd_trig BEFORE UPDATE ON test.hash_rel_0 FOR EACH ROW EXECUTE PROCEDURE test.hash_rel_upd_trig_func()
907+
908+
\d+ test.hash_rel_extern
909+
Table "test.hash_rel_extern"
910+
Column | Type | Modifiers | Storage | Stats target | Description
911+
--------+---------+------------------------------------------------------------+---------+--------------+-------------
912+
id | integer | not null default nextval('test.hash_rel_id_seq'::regclass) | plain | |
913+
value | integer | not null | plain | |
914+
abc | integer | | plain | |
915+
Indexes:
916+
"hash_rel_extern_pkey" PRIMARY KEY, btree (id)
917+
Check constraints:
918+
"pathman_hash_rel_extern_2_check" CHECK (pathman.get_hash_part_idx(hashint4(value), 3) = 0)
919+
Inherits: test.hash_rel
920+
921+
INSERT INTO test.hash_rel SELECT * FROM test.hash_rel_0;
922+
DROP TABLE test.hash_rel_0;
923+
EXPLAIN SELECT * FROM test.hash_rel;
924+
QUERY PLAN
925+
---------------------------------------------------------------------------------------------
926+
Append (cost=10000000000.00..30000000032.44 rows=2044 width=12)
927+
-> Seq Scan on hash_rel_extern (cost=10000000000.00..10000000030.40 rows=2040 width=12)
928+
-> Seq Scan on hash_rel_1 (cost=10000000000.00..10000000001.02 rows=2 width=12)
929+
-> Seq Scan on hash_rel_2 (cost=10000000000.00..10000000001.02 rows=2 width=12)
930+
(4 rows)
931+
888932
/*
889933
* Clean up
890934
*/
891935
SELECT pathman.drop_partitions('test.hash_rel');
892-
NOTICE: drop cascades to 3 other objects
893-
NOTICE: 2 rows copied from test.hash_rel_0
936+
NOTICE: drop cascades to 2 other objects
894937
NOTICE: 3 rows copied from test.hash_rel_1
895938
NOTICE: 2 rows copied from test.hash_rel_2
939+
NOTICE: 2 rows copied from test.hash_rel_extern
896940
drop_partitions
897941
-----------------
898942
3

hash.sql

Lines changed: 107 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ DECLARE
2222
v_child_relname TEXT;
2323
v_plain_schema TEXT;
2424
v_plain_relname TEXT;
25-
v_atttype REGTYPE;
26-
v_hashfunc REGPROC;
25+
-- v_atttype REGTYPE;
26+
-- v_hashfunc REGPROC;
2727
v_init_callback REGPROCEDURE;
2828

2929
BEGIN
@@ -41,8 +41,8 @@ BEGIN
4141
PERFORM @extschema@.common_relation_checks(parent_relid, attribute);
4242

4343
/* Fetch atttype and its hash function */
44-
v_atttype := @extschema@.get_attribute_type(parent_relid, attribute);
45-
v_hashfunc := @extschema@.get_type_hash_func(v_atttype);
44+
-- v_atttype := @extschema@.get_attribute_type(parent_relid, attribute);
45+
-- v_hashfunc := @extschema@.get_type_hash_func(v_atttype);
4646

4747
SELECT * INTO v_plain_schema, v_plain_relname
4848
FROM @extschema@.get_plain_schema_and_relname(parent_relid);
@@ -64,15 +64,23 @@ BEGIN
6464
parent_relid::TEXT,
6565
@extschema@.get_rel_tablespace_name(parent_relid));
6666

67-
EXECUTE format('ALTER TABLE %s ADD CONSTRAINT %s
68-
CHECK (@extschema@.get_hash_part_idx(%s(%s), %s) = %s)',
67+
-- EXECUTE format('ALTER TABLE %s ADD CONSTRAINT %s
68+
-- CHECK (@extschema@.get_hash_part_idx(%s(%s), %s) = %s)',
69+
-- v_child_relname,
70+
-- @extschema@.build_check_constraint_name(v_child_relname::REGCLASS,
71+
-- attribute),
72+
-- v_hashfunc::TEXT,
73+
-- attribute,
74+
-- partitions_count,
75+
-- partnum);
76+
EXECUTE format('ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s)',
6977
v_child_relname,
70-
@extschema@.build_check_constraint_name(v_child_relname::REGCLASS,
78+
@extschema@.build_check_constraint_name(v_child_relname,
7179
attribute),
72-
v_hashfunc::TEXT,
73-
attribute,
74-
partitions_count,
75-
partnum);
80+
@extschema@.build_hash_condition(v_child_relname,
81+
attribute,
82+
partitions_count,
83+
partnum));
7684

7785
PERFORM @extschema@.copy_foreign_keys(parent_relid, v_child_relname::REGCLASS);
7886

@@ -105,6 +113,94 @@ END
105113
$$ LANGUAGE plpgsql
106114
SET client_min_messages = WARNING;
107115

116+
/*
117+
* Replace hash partition with another one. It could be useful in case when
118+
* someone wants to attach foreign table as a partition
119+
*/
120+
CREATE OR REPLACE FUNCTION @extschema@.replace_hash_partition(
121+
old_partition REGCLASS,
122+
new_partition REGCLASS)
123+
RETURNS REGCLASS AS
124+
$$
125+
DECLARE
126+
v_attname TEXT;
127+
rel_persistence CHAR;
128+
v_init_callback REGPROCEDURE;
129+
v_parent_relid REGCLASS;
130+
v_part_count INT;
131+
v_part_num INT;
132+
BEGIN
133+
PERFORM @extschema@.validate_relname(old_partition);
134+
PERFORM @extschema@.validate_relname(new_partition);
135+
136+
/* Parent relation */
137+
v_parent_relid := @extschema@.get_parent_of_partition(old_partition);
138+
139+
/* Acquire lock on parent */
140+
PERFORM @extschema@.lock_partitioned_relation(v_parent_relid);
141+
142+
/* Ignore temporary tables */
143+
SELECT relpersistence FROM pg_catalog.pg_class
144+
WHERE oid = new_partition INTO rel_persistence;
145+
146+
IF rel_persistence = 't'::CHAR THEN
147+
RAISE EXCEPTION 'temporary table "%" cannot be used as a partition',
148+
new_partition::TEXT;
149+
END IF;
150+
151+
/* Check that new partition has an equal structure as parent does */
152+
IF NOT @extschema@.validate_relations_equality(v_parent_relid, new_partition) THEN
153+
RAISE EXCEPTION 'partition must have the exact same structure as parent';
154+
END IF;
155+
156+
/* Get partitioning key */
157+
v_attname := attname FROM @extschema@.pathman_config WHERE partrel = v_parent_relid;
158+
IF v_attname IS NULL THEN
159+
RAISE EXCEPTION 'table "%" is not partitioned', v_parent_relid::TEXT;
160+
END IF;
161+
162+
/* Calculate partitions count and old partition's number */
163+
v_part_count := count(*) FROM @extschema@.pathman_partition_list WHERE parent = v_parent_relid;
164+
v_part_num := @extschema@.get_partition_hash(v_parent_relid, old_partition);
165+
166+
/* Detach old partition */
167+
EXECUTE format('ALTER TABLE %s NO INHERIT %s', old_partition, v_parent_relid);
168+
EXECUTE format('ALTER TABLE %s DROP CONSTRAINT IF EXISTS %s',
169+
old_partition,
170+
@extschema@.build_check_constraint_name(old_partition::REGCLASS,
171+
v_attname));
172+
173+
/* Attach new one */
174+
EXECUTE format('ALTER TABLE %s INHERIT %s', new_partition, v_parent_relid);
175+
EXECUTE format('ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s)',
176+
new_partition,
177+
@extschema@.build_check_constraint_name(new_partition::regclass,
178+
v_attname),
179+
@extschema@.build_hash_condition(new_partition::regclass,
180+
v_attname,
181+
v_part_count,
182+
v_part_num));
183+
184+
/* Fetch init_callback from 'params' table */
185+
WITH stub_callback(stub) as (values (0))
186+
SELECT coalesce(init_callback, 0::REGPROCEDURE)
187+
FROM stub_callback
188+
LEFT JOIN @extschema@.pathman_config_params AS params
189+
ON params.partrel = v_parent_relid
190+
INTO v_init_callback;
191+
192+
PERFORM @extschema@.invoke_on_partition_created_callback(v_parent_relid,
193+
new_partition,
194+
v_init_callback);
195+
196+
/* Invalidate cache */
197+
PERFORM @extschema@.on_update_partitions(v_parent_relid);
198+
199+
RETURN new_partition;
200+
END
201+
$$
202+
LANGUAGE plpgsql;
203+
108204
/*
109205
* Creates an update trigger
110206
*/

init.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,3 +803,23 @@ CREATE OR REPLACE FUNCTION @extschema@.invoke_on_partition_created_callback(
803803
init_callback REGPROCEDURE)
804804
RETURNS VOID AS 'pg_pathman', 'invoke_on_partition_created_callback'
805805
LANGUAGE C;
806+
807+
/*
808+
* Build hash condition for a CHECK CONSTRAINT
809+
*/
810+
CREATE OR REPLACE FUNCTION @extschema@.build_hash_condition(
811+
parent_relid REGCLASS,
812+
attname TEXT,
813+
partitions_count INT,
814+
partition_number INT)
815+
RETURNS TEXT AS 'pg_pathman', 'build_hash_condition'
816+
LANGUAGE C;
817+
818+
/*
819+
* Returns hash value for specified partition (0..N)
820+
*/
821+
CREATE OR REPLACE FUNCTION @extschema@.get_partition_hash(
822+
parent_relid REGCLASS,
823+
partition REGCLASS)
824+
RETURNS INT AS 'pg_pathman', 'get_partition_hash'
825+
LANGUAGE C;

sql/pathman_basic.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,15 @@ ALTER TABLE test.hash_rel ADD COLUMN abc int;
236236
INSERT INTO test.hash_rel (id, value, abc) VALUES (123, 456, 789);
237237
SELECT * FROM test.hash_rel WHERE id = 123;
238238

239+
/* Test replacing hash partition */
240+
CREATE TABLE test.hash_rel_extern (LIKE test.hash_rel INCLUDING ALL);
241+
SELECT pathman.replace_hash_partition('test.hash_rel_0', 'test.hash_rel_extern');
242+
\d+ test.hash_rel_0
243+
\d+ test.hash_rel_extern
244+
INSERT INTO test.hash_rel SELECT * FROM test.hash_rel_0;
245+
DROP TABLE test.hash_rel_0;
246+
EXPLAIN SELECT * FROM test.hash_rel;
247+
239248
/*
240249
* Clean up
241250
*/

src/pl_funcs.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,24 +201,19 @@ Datum
201201
get_attribute_type_pl(PG_FUNCTION_ARGS)
202202
{
203203
Oid relid = PG_GETARG_OID(0);
204-
text *attname = PG_GETARG_TEXT_P(1);
204+
char *attname = text_to_cstring(PG_GETARG_TEXT_P(1));
205205
Oid result;
206-
HeapTuple tp;
207206

208-
/* NOTE: for now it's the most efficient way */
209-
tp = SearchSysCacheAttName(relid, text_to_cstring(attname));
210-
if (HeapTupleIsValid(tp))
207+
if ((result = get_attribute_type(relid, attname)) != InvalidOid)
211208
{
212-
Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp);
213-
result = att_tup->atttypid;
214-
ReleaseSysCache(tp);
215-
216209
PG_RETURN_OID(result);
217210
}
218211
else
212+
{
219213
elog(ERROR, "Cannot find type name for attribute \"%s\" "
220214
"of relation \"%s\"",
221-
text_to_cstring(attname), get_rel_name_or_relid(relid));
215+
attname, get_rel_name_or_relid(relid));
216+
}
222217

223218
PG_RETURN_NULL(); /* keep compiler happy */
224219
}

src/pl_hash_funcs.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99
*/
1010

1111
#include "pathman.h"
12+
#include "utils.h"
1213

1314
#include "utils/typcache.h"
15+
#include "utils/lsyscache.h"
16+
#include "utils/builtins.h"
1417

1518

1619
/* Function declarations */
1720

1821
PG_FUNCTION_INFO_V1( get_type_hash_func );
1922
PG_FUNCTION_INFO_V1( get_hash_part_idx );
23+
PG_FUNCTION_INFO_V1( build_hash_condition );
24+
PG_FUNCTION_INFO_V1( get_partition_hash );
2025

2126

2227
/*
@@ -44,3 +49,75 @@ get_hash_part_idx(PG_FUNCTION_ARGS)
4449

4550
PG_RETURN_UINT32(hash_to_part_index(value, part_count));
4651
}
52+
53+
/*
54+
* Build hash condition for a CHECK CONSTRAINT
55+
*/
56+
Datum
57+
build_hash_condition(PG_FUNCTION_ARGS)
58+
{
59+
TypeCacheEntry *tce;
60+
61+
Oid parent = PG_GETARG_OID(0);
62+
text *attname = PG_GETARG_TEXT_P(1);
63+
uint32 partitions_count = PG_GETARG_UINT32(2);
64+
uint32 partition_number = PG_GETARG_UINT32(3);
65+
Oid attyp;
66+
char *result;
67+
68+
if (partition_number >= partitions_count)
69+
elog(ERROR,
70+
"Partition number cannot exceed partitions count");
71+
72+
/* Get attribute type and its hash function oid */
73+
attyp = get_attribute_type(parent, text_to_cstring(attname));
74+
if (attyp == InvalidOid)
75+
elog(ERROR,
76+
"Relation '%s' has no attribute '%s'",
77+
get_rel_name(parent),
78+
text_to_cstring(attname));
79+
80+
tce = lookup_type_cache(attyp, TYPECACHE_HASH_PROC);
81+
82+
/* Create hash condition CSTRING */
83+
result = psprintf("%s.get_hash_part_idx(%s(%s), %u) = %u",
84+
get_namespace_name(get_pathman_schema()),
85+
get_func_name(tce->hash_proc),
86+
text_to_cstring(attname),
87+
partitions_count,
88+
partition_number);
89+
90+
PG_RETURN_TEXT_P(cstring_to_text(result));
91+
}
92+
93+
/*
94+
* Returns hash value for specified partition (0..N)
95+
*/
96+
Datum
97+
get_partition_hash(PG_FUNCTION_ARGS)
98+
{
99+
const PartRelationInfo *prel;
100+
Oid parent = PG_GETARG_OID(0);
101+
Oid partition = PG_GETARG_OID(1);
102+
Oid *children;
103+
int i;
104+
105+
/* Validate partition type */
106+
prel = get_pathman_relation_info(parent);
107+
if (!prel || prel->parttype != PT_HASH)
108+
elog(ERROR,
109+
"Relation '%s' isn't partitioned by hash",
110+
get_rel_name(parent));
111+
112+
/* Searching for partition */
113+
children = PrelGetChildrenArray(prel);
114+
for (i=0; i<prel->children_count; i++)
115+
if (children[i] == partition)
116+
PG_RETURN_UINT32(i);
117+
118+
/* If we get here then there is no such partition for specified parent */
119+
elog(ERROR,
120+
"Relation '%s' isn't a part of partitioned table '%s'",
121+
get_rel_name(parent),
122+
get_rel_name(partition));
123+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy