-
Notifications
You must be signed in to change notification settings - Fork 67
/
Copy pathinit.sql
354 lines (309 loc) · 9.08 KB
/
init.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/* ------------------------------------------------------------------------
*
* init.sql
* Creates config table and provides common utility functions
*
* Copyright (c) 2015-2016, Postgres Professional
*
* ------------------------------------------------------------------------
*/
/*
* Pathman config
* relname - schema qualified relation name
* attname - partitioning key
* parttype - partitioning type:
* 1 - HASH
* 2 - RANGE
* range_interval - base interval for RANGE partitioning in string representation
*/
CREATE TABLE IF NOT EXISTS @[email protected]_config (
id SERIAL PRIMARY KEY,
relname VARCHAR(127),
attname VARCHAR(127),
parttype INTEGER,
range_interval TEXT
);
SELECT pg_catalog.pg_extension_config_dump('@[email protected]_config', '');
CREATE OR REPLACE FUNCTION @[email protected]_create_partitions(relid OID)
RETURNS VOID AS 'pg_pathman', 'on_partitions_created' LANGUAGE C STRICT;
CREATE OR REPLACE FUNCTION @[email protected]_update_partitions(relid OID)
RETURNS VOID AS 'pg_pathman', 'on_partitions_updated' LANGUAGE C STRICT;
CREATE OR REPLACE FUNCTION @[email protected]_remove_partitions(relid OID)
RETURNS VOID AS 'pg_pathman', 'on_partitions_removed' LANGUAGE C STRICT;
CREATE OR REPLACE FUNCTION @[email protected]_or_create_range_partition(relid OID, value ANYELEMENT)
RETURNS OID AS 'pg_pathman', 'find_or_create_range_partition' LANGUAGE C STRICT;
/*
* Returns min and max values for specified RANGE partition.
*/
CREATE OR REPLACE FUNCTION @[email protected]_partition_range(
parent_relid OID, partition_relid OID, dummy ANYELEMENT)
RETURNS ANYARRAY AS 'pg_pathman', 'get_partition_range' LANGUAGE C STRICT;
/*
* Returns N-th range (in form of array)
*/
CREATE OR REPLACE FUNCTION @[email protected]_range_by_idx(
parent_relid OID, idx INTEGER, dummy ANYELEMENT)
RETURNS ANYARRAY AS 'pg_pathman', 'get_range_by_idx' LANGUAGE C STRICT;
/*
* Returns min value of the first range for relation
*/
CREATE OR REPLACE FUNCTION @[email protected]_min_range_value(
parent_relid OID, dummy ANYELEMENT)
RETURNS ANYELEMENT AS 'pg_pathman', 'get_min_range_value' LANGUAGE C STRICT;
/*
* Returns max value of the last range for relation
*/
CREATE OR REPLACE FUNCTION @[email protected]_max_range_value(
parent_relid OID, dummy ANYELEMENT)
RETURNS ANYELEMENT AS 'pg_pathman', 'get_max_range_value' LANGUAGE C STRICT;
/*
* Checks if range overlaps with existing partitions.
* Returns TRUE if overlaps and FALSE otherwise.
*/
CREATE OR REPLACE FUNCTION @[email protected]_overlap(
parent_relid OID, range_min ANYELEMENT, range_max ANYELEMENT)
RETURNS BOOLEAN AS 'pg_pathman', 'check_overlap' LANGUAGE C STRICT;
/*
* Copy rows to partitions
*/
CREATE OR REPLACE FUNCTION @[email protected]_data(
p_parent regclass
, p_invalidate_cache_on_error BOOLEAN DEFAULT FALSE
, OUT p_total BIGINT)
AS
$$
DECLARE
relname TEXT;
rec RECORD;
cnt BIGINT := 0;
BEGIN
relname := @[email protected]_relname(p_parent);
p_total := 0;
/* Create partitions and copy rest of the data */
RAISE NOTICE 'Copying data to partitions...';
EXECUTE format('
WITH part_data AS (
DELETE FROM ONLY %s RETURNING *)
INSERT INTO %s SELECT * FROM part_data'
, relname
, relname);
GET DIAGNOSTICS p_total = ROW_COUNT;
RETURN;
END
$$
LANGUAGE plpgsql;
/*
* Disable pathman partitioning for specified relation
*/
CREATE OR REPLACE FUNCTION @[email protected]_partitioning(IN relation TEXT)
RETURNS VOID AS
$$
DECLARE
v_parttype INTEGER;
BEGIN
relation := @[email protected]_relname(relation);
v_parttype := parttype FROM pathman_config WHERE relname = relation;
DELETE FROM @[email protected]_config WHERE relname = relation;
IF v_parttype = 1 THEN
PERFORM @[email protected]_hash_triggers(relation);
ELSIF v_parttype = 2 THEN
PERFORM @[email protected]_range_triggers(relation);
END IF;
/* Notify backend about changes */
PERFORM on_remove_partitions(relation::regclass::integer);
END
$$
LANGUAGE plpgsql;
/*
* Returns attribute type name for relation
*/
CREATE OR REPLACE FUNCTION @[email protected]_attribute_type_name(
p_relation REGCLASS
, p_attname TEXT
, OUT p_atttype TEXT)
RETURNS TEXT AS
$$
BEGIN
SELECT typname::TEXT INTO p_atttype
FROM pg_type JOIN pg_attribute on atttypid = "oid"
WHERE attrelid = p_relation::oid and attname = lower(p_attname);
END
$$
LANGUAGE plpgsql;
/*
* Checks if attribute is nullable
*/
CREATE OR REPLACE FUNCTION @[email protected]_attribute_nullable(
p_relation REGCLASS
, p_attname TEXT
, OUT p_nullable BOOLEAN)
RETURNS BOOLEAN AS
$$
BEGIN
SELECT NOT attnotnull INTO p_nullable
FROM pg_type JOIN pg_attribute on atttypid = "oid"
WHERE attrelid = p_relation::oid and attname = lower(p_attname);
END
$$
LANGUAGE plpgsql;
/*
* Aggregates several common relation checks before partitioning. Suitable for every partitioning type.
*/
CREATE OR REPLACE FUNCTION @[email protected]_relation_checks(
p_relation REGCLASS
, p_attribute TEXT)
RETURNS BOOLEAN AS
$$
DECLARE
v_rec RECORD;
is_referenced BOOLEAN;
BEGIN
IF EXISTS (SELECT * FROM @[email protected]_config WHERE relname::regclass = p_relation) THEN
RAISE EXCEPTION 'Relation "%" has already been partitioned', p_relation;
END IF;
IF @[email protected]_attribute_nullable(p_relation, p_attribute) THEN
RAISE EXCEPTION 'Partitioning key ''%'' must be NOT NULL', p_attribute;
END IF;
/* Check if there are foreign keys reference to the relation */
FOR v_rec IN (SELECT *
FROM pg_constraint WHERE confrelid = p_relation::regclass::oid)
LOOP
is_referenced := TRUE;
RAISE WARNING 'Foreign key ''%'' references to the relation ''%''', v_rec.conname, p_relation;
END LOOP;
IF is_referenced THEN
RAISE EXCEPTION 'Relation ''%'' is referenced from other relations', p_relation;
END IF;
RETURN TRUE;
END
$$
LANGUAGE plpgsql;
/*
* Returns relname without quotes or something
*/
CREATE OR REPLACE FUNCTION @[email protected]_plain_schema_and_relname(cls regclass, OUT schema TEXT, OUT relname TEXT)
AS
$$
BEGIN
SELECT relnamespace::regnamespace, pg_class.relname FROM pg_class WHERE oid = cls::oid
INTO schema, relname;
END
$$
LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION @[email protected]_plain_relname(cls regclass)
RETURNS TEXT AS
$$
BEGIN
RETURN relname FROM pg_class WHERE oid = cls::oid;
END
$$
LANGUAGE plpgsql;
/*
* Validates relation name. It must be schema qualified
*/
CREATE OR REPLACE FUNCTION @[email protected]_relname(cls regclass)
RETURNS TEXT AS
$$
BEGIN
RETURN @[email protected]_schema_qualified_name(cls, '.');
END
$$
LANGUAGE plpgsql;
/*
* Returns schema-qualified name for table
*/
CREATE OR REPLACE FUNCTION @[email protected]_schema_qualified_name(
cls REGCLASS
, delimiter TEXT DEFAULT '_'
, suffix TEXT DEFAULT '')
RETURNS TEXT AS
$$
BEGIN
RETURN (SELECT quote_ident(relnamespace::regnamespace::text) ||
delimiter ||
quote_ident(relname || suffix)
FROM pg_class
WHERE oid = cls::oid);
END
$$
LANGUAGE plpgsql;
/*
* Check if two relations have equal structures
*/
CREATE OR REPLACE FUNCTION @[email protected]_relations_equality(relation1 OID, relation2 OID)
RETURNS BOOLEAN AS
$$
DECLARE
rec RECORD;
BEGIN
FOR rec IN (
WITH
a1 AS (select * from pg_attribute where attrelid = relation1 and attnum > 0),
a2 AS (select * from pg_attribute where attrelid = relation2 and attnum > 0)
SELECT a1.attname name1, a2.attname name2, a1.atttypid type1, a2.atttypid type2
FROM a1
FULL JOIN a2 ON a1.attnum = a2.attnum
)
LOOP
IF rec.name1 IS NULL OR rec.name2 IS NULL OR rec.name1 != rec.name2 THEN
RETURN False;
END IF;
END LOOP;
RETURN True;
END
$$
LANGUAGE plpgsql;
/*
* Check if regclass if date or timestamp
*/
CREATE OR REPLACE FUNCTION @[email protected]_date(cls REGTYPE)
RETURNS BOOLEAN AS
$$
BEGIN
RETURN cls IN ('timestamp'::regtype, 'timestamptz'::regtype, 'date'::regtype);
END
$$
LANGUAGE plpgsql;
/*
* DDL trigger that deletes entry from pathman_config
*/
CREATE OR REPLACE FUNCTION @[email protected]_ddl_trigger_func()
RETURNS event_trigger AS
$$
DECLARE
obj record;
BEGIN
FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() as events
JOIN @[email protected]_config as cfg ON cfg.relname = events.object_identity
LOOP
IF obj.object_type = 'table' THEN
EXECUTE 'DELETE FROM @[email protected]_config WHERE relname = $1'
USING obj.object_identity;
END IF;
END LOOP;
END
$$
LANGUAGE plpgsql;
CREATE EVENT TRIGGER pathman_ddl_trigger
ON sql_drop
EXECUTE PROCEDURE @[email protected]_ddl_trigger_func();
/*
* Acquire partitions lock to prevent concurrent partitions creation
*/
CREATE OR REPLACE FUNCTION @[email protected]_partitions_lock()
RETURNS VOID AS 'pg_pathman', 'acquire_partitions_lock' LANGUAGE C STRICT;
/*
* Release partitions lock
*/
CREATE OR REPLACE FUNCTION @[email protected]_partitions_lock()
RETURNS VOID AS 'pg_pathman', 'release_partitions_lock' LANGUAGE C STRICT;
/*
* Returns hash function OID for specified type
*/
CREATE OR REPLACE FUNCTION @[email protected]_type_hash_func(OID)
RETURNS OID AS 'pg_pathman', 'get_type_hash_func' LANGUAGE C STRICT;
/*
* Calculates hash for integer value
*/
CREATE OR REPLACE FUNCTION @[email protected]_hash(INTEGER, INTEGER)
RETURNS INTEGER AS 'pg_pathman', 'get_hash' LANGUAGE C STRICT;