Skip to content

Commit 562c856

Browse files
committed
added ALTER TABLE ... PARTITION BY ... (CONCURRENTLY) statement
1 parent dd57547 commit 562c856

File tree

10 files changed

+232
-87
lines changed

10 files changed

+232
-87
lines changed

src/backend/commands/partition.c

Lines changed: 135 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "postgres.h"
1212
#include "miscadmin.h"
1313

14+
#include "access/xact.h"
1415
#include "access/htup_details.h"
1516
#include "catalog/heap.h"
1617
#include "catalog/namespace.h"
@@ -27,13 +28,21 @@
2728
#include "utils/builtins.h"
2829
#include "utils/timestamp.h"
2930
#include "utils/lsyscache.h"
31+
#include "utils/snapmgr.h"
3032

3133

3234
static void create_hash_partitions(PartitionInfo *pinfo,
3335
Oid relid,
3436
const char* attname,
35-
bool partition_data);
36-
static void create_range_partitions(PartitionInfo *pinfo, Oid relid, const char *attname, bool partition_data);
37+
PartitionDataType partition_data);
38+
static void create_range_partitions(PartitionInfo *pinfo,
39+
Oid relid,
40+
const char *attname,
41+
PartitionDataType partition_data);
42+
static void read_interval_value(PartitionInfo *pinfo,
43+
Oid atttype,
44+
Oid *interval_type,
45+
Datum *interval_datum);
3746
static Node *cookPartitionKeyValue(Oid relid, const char *raw, Node *raw_value);
3847
static char *RangeVarGetString(const RangeVar *rangevar);
3948
static Oid RangeVarGetNamespaceId(const RangeVar *rangevar);
@@ -43,7 +52,7 @@ static Oid RangeVarGetNamespaceId(const RangeVar *rangevar);
4352

4453

4554
void
46-
create_partitions(PartitionInfo *pinfo, Oid relid, bool partition_data)
55+
create_partitions(PartitionInfo *pinfo, Oid relid, PartitionDataType partition_data)
4756
{
4857
Value *attname = (Value *) linitial(((ColumnRef *) pinfo->key)->fields);
4958

@@ -64,18 +73,38 @@ create_partitions(PartitionInfo *pinfo, Oid relid, bool partition_data)
6473
}
6574
}
6675

67-
SPI_finish(); /* close SPI connection */
76+
SPI_finish();
77+
78+
/* Start concurrent partitioning if needed */
79+
if (partition_data == PDT_CONCURRENT)
80+
{
81+
/*
82+
* We must commit current transaction to make partitions visible to the
83+
* worker
84+
*/
85+
PopActiveSnapshot();
86+
CommitTransactionCommand();
87+
StartTransactionCommand();
88+
89+
if (SPI_connect() != SPI_OK_CONNECT)
90+
elog(ERROR, "could not connect using SPI");
91+
92+
/* Start worker */
93+
pm_partition_table_concurrently(relid);
94+
95+
SPI_finish();
96+
}
6897
}
6998

7099

71100
static void
72101
create_hash_partitions(PartitionInfo *pinfo,
73102
Oid relid,
74103
const char* attname,
75-
bool partition_data)
104+
PartitionDataType partition_data)
76105
{
77-
char **relnames = NULL;
78-
char **tablespaces = NULL;
106+
char **relnames = NULL;
107+
char **tablespaces = NULL;
79108

80109
if (list_length(pinfo->partitions) > 0)
81110
{
@@ -100,7 +129,7 @@ create_hash_partitions(PartitionInfo *pinfo,
100129
pm_create_hash_partitions(relid,
101130
attname,
102131
pinfo->partitions_count,
103-
partition_data,
132+
partition_data == PDT_REGULAR,
104133
relnames,
105134
tablespaces);
106135

@@ -120,7 +149,7 @@ static void
120149
create_range_partitions(PartitionInfo *pinfo,
121150
Oid relid,
122151
const char *attname,
123-
bool partition_data)
152+
PartitionDataType partition_data)
124153
{
125154
ListCell *lc;
126155
Datum last_bound = (Datum) 0;
@@ -133,9 +162,8 @@ create_range_partitions(PartitionInfo *pinfo,
133162
Datum start_value;
134163

135164
/* parameters */
136-
Datum interval_datum = (Datum) 0;
165+
Datum interval_datum;
137166
Oid interval_type;
138-
Const *interval_const;
139167
ParseState *pstate = make_parsestate(NULL);
140168

141169
if (!attnum)
@@ -144,67 +172,14 @@ create_range_partitions(PartitionInfo *pinfo,
144172
atttype = get_atttype(relid, attnum);
145173
atttypmod = get_atttypmod(relid, attnum);
146174

147-
/* If interval is set then convert it to a suitable Datum value */
148-
if (pinfo->interval != NULL)
149-
{
150-
if (IsA(pinfo->interval, A_Const))
151-
{
152-
A_Const *con = (A_Const *) pinfo->interval;
153-
Value *val = &con->val;
154-
155-
interval_const = make_const(pstate, val, con->location);
156-
}
157-
else
158-
elog(ERROR, "Constant interval value is expected");
159-
160-
/*
161-
* If attribute is of type DATE or TIMESTAMP then convert interval to
162-
* Interval type
163-
*/
164-
switch (atttype)
165-
{
166-
case DATEOID:
167-
case TIMESTAMPOID:
168-
case TIMESTAMPTZOID:
169-
{
170-
char *interval_literal;
171-
172-
/* We should get an UNKNOWN type here */
173-
if (interval_const->consttype != UNKNOWNOID)
174-
elog(ERROR, "Expected a literal as an interval value");
175-
176-
/* Get a text representation of the interval */
177-
interval_literal = DatumGetCString(interval_const->constvalue);
178-
interval_datum = DirectFunctionCall3(interval_in,
179-
CStringGetDatum(interval_literal),
180-
ObjectIdGetDatum(InvalidOid),
181-
Int32GetDatum(-1));
182-
interval_type = INTERVALOID;
183-
}
184-
break;
185-
default:
186-
interval_datum = interval_const->constvalue;
187-
interval_type = interval_const->consttype;
188-
}
189-
}
190-
else /* If interval is not set */
191-
{
192-
switch (atttype)
193-
{
194-
case DATEOID:
195-
case TIMESTAMPOID:
196-
case TIMESTAMPTZOID:
197-
interval_type = INTERVALOID;
198-
default:
199-
interval_type = atttype;
200-
}
201-
}
175+
/* Interval */
176+
read_interval_value(pinfo, atttype, &interval_type, &interval_datum);
202177

203178
/*
204179
* Start value. It is always non-NULL whenever partition_data = True.
205180
* Otherwise the actual start value doesn't matter
206181
*/
207-
Assert( (pinfo->start_value != NULL) == partition_data );
182+
Assert(pinfo->start_value != NULL && partition_data != PDT_NONE);
208183
if (pinfo->start_value)
209184
{
210185
Node *n = cookPartitionKeyValue(relid,
@@ -225,7 +200,8 @@ create_range_partitions(PartitionInfo *pinfo,
225200
interval_datum,
226201
interval_type,
227202
pinfo->interval == NULL,
228-
partition_data);
203+
partition_data == PDT_REGULAR,
204+
partition_data == PDT_CONCURRENT);
229205

230206
/* Add partitions */
231207
foreach(lc, pinfo->partitions)
@@ -251,6 +227,97 @@ create_range_partitions(PartitionInfo *pinfo,
251227
last_bound = ((Const *) bound_expr)->constvalue;
252228
last_bound_is_null = false;
253229
}
230+
231+
/*
232+
* Add semi-infinite partition to the left (only for ALTER TABLE ...
233+
* PARTITION BY), i.e. when start value is set
234+
*/
235+
if (pinfo->start_value)
236+
{
237+
238+
pm_add_range_partition(relid,
239+
atttype,
240+
NULL,
241+
(Datum) 0, /* it doesn't matter */
242+
start_value,
243+
true,
244+
false,
245+
NULL);
246+
}
247+
}
248+
249+
250+
/*
251+
* Converts
252+
*/
253+
static void
254+
read_interval_value(PartitionInfo *pinfo,
255+
Oid atttype,
256+
Oid *interval_type,
257+
Datum *interval_datum)
258+
{
259+
ParseState *pstate = make_parsestate(NULL);
260+
261+
/* Default value */
262+
*interval_datum = (Datum) 0;
263+
264+
/* If interval is set then convert it to a suitable Datum value */
265+
if (pinfo->interval != NULL)
266+
{
267+
Const *interval_const;
268+
269+
if (IsA(pinfo->interval, A_Const))
270+
{
271+
A_Const *con = (A_Const *) pinfo->interval;
272+
Value *val = &con->val;
273+
274+
interval_const = make_const(pstate, val, con->location);
275+
}
276+
else
277+
elog(ERROR, "Constant interval value is expected");
278+
279+
/*
280+
* If attribute is of type DATE or TIMESTAMP then convert interval to
281+
* Interval type
282+
*/
283+
switch (atttype)
284+
{
285+
case DATEOID:
286+
case TIMESTAMPOID:
287+
case TIMESTAMPTZOID:
288+
{
289+
char *interval_literal;
290+
291+
/* We should get an UNKNOWN type here */
292+
if (interval_const->consttype != UNKNOWNOID)
293+
elog(ERROR, "Expected a literal as an interval value");
294+
295+
/* Get a text representation of the interval */
296+
interval_literal = DatumGetCString(interval_const->constvalue);
297+
*interval_datum = DirectFunctionCall3(interval_in,
298+
CStringGetDatum(interval_literal),
299+
ObjectIdGetDatum(InvalidOid),
300+
Int32GetDatum(-1));
301+
*interval_type = INTERVALOID;
302+
}
303+
break;
304+
default:
305+
*interval_datum = interval_const->constvalue;
306+
*interval_type = interval_const->consttype;
307+
}
308+
}
309+
else /* If interval is not set */
310+
{
311+
switch (atttype)
312+
{
313+
case DATEOID:
314+
case TIMESTAMPOID:
315+
case TIMESTAMPTZOID:
316+
*interval_type = INTERVALOID;
317+
default:
318+
*interval_type = atttype;
319+
}
320+
}
254321
}
255322

256323

src/backend/commands/pathman_wrapper.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,18 @@ construct_text_array(char **arr, int nelem)
322322

323323

324324
/*
325+
* Adds record for a RANGE partitioned table to pg_pathman's config and
326+
* optionally creates partitions (see below)
325327
*
328+
* Behaviour:
329+
* - partition_data=true
330+
* creates partitions and moves all the data;
331+
* - prepopulate_partitions=true and partition_data=false
332+
* creates partitions but no data moved. It is useful if data migration
333+
* will be done later (for example, concurrent partitioning);
334+
* - prepopulate_partitions=true and partition_data=false
335+
* only pg_pathman updated, but no partitions created. Partitions will
336+
* be created manually later by invoking pm_add_partition()
326337
*/
327338
void
328339
pm_create_range_partitions(Oid relid,
@@ -332,7 +343,8 @@ pm_create_range_partitions(Oid relid,
332343
Datum interval,
333344
Oid interval_type,
334345
bool interval_isnull,
335-
bool partition_data)
346+
bool partition_data,
347+
bool prepopulate_partitions)
336348
{
337349
FuncArgs args;
338350
int ret;
@@ -354,7 +366,7 @@ pm_create_range_partitions(Oid relid,
354366
* partitions count to zero assuming that new partitions will be added
355367
* explicitly by invoking pm_add_range_partition()
356368
*/
357-
if (partition_data)
369+
if (prepopulate_partitions || partition_data)
358370
PG_SETARG_NULL(&args, 4, INT4OID);
359371
else
360372
PG_SETARG_DATUM(&args, 4, INT4OID, Int32GetDatum(0));
@@ -560,3 +572,23 @@ pm_drop_range_partition_expand_next(Oid relid)
560572
if (!ret)
561573
elog(ERROR, "Unable to drop partition '%s'", get_rel_name(relid));
562574
}
575+
576+
577+
/*
578+
* Start concurrent data mirgration
579+
*/
580+
void
581+
pm_partition_table_concurrently(Oid relid)
582+
{
583+
FuncArgs args;
584+
bool ret;
585+
586+
InitFuncArgs(&args, 1);
587+
PG_SETARG_DATUM(&args, 0, OIDOID, relid);
588+
589+
ret = pathman_invoke("partition_table_concurrently($1)", &args);
590+
FreeFuncArgs(&args);
591+
592+
if (!ret)
593+
elog(ERROR, "Unable to start concurrent data migration");
594+
}

src/backend/commands/tablecmds.c

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
723723
/* Handle partitioning */
724724
if (stmt->partition_info)
725725
{
726-
(void) create_partitions(stmt->partition_info, rel->rd_id, false);
726+
(void) create_partitions(stmt->partition_info, rel->rd_id, PDT_NONE);
727727
}
728728

729729
return address;
@@ -3073,7 +3073,6 @@ AlterTableGetLockLevel(List *cmds)
30733073
case AT_RenamePartition:
30743074
case AT_DropPartition:
30753075
case AT_MovePartition:
3076-
case AT_PartitionBy:
30773076
break;
30783077

30793078
default: /* oops */
@@ -3408,7 +3407,6 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
34083407
case AT_RenamePartition:
34093408
case AT_DropPartition:
34103409
case AT_MovePartition:
3411-
case AT_PartitionBy:
34123410
pass = AT_PASS_MISC;
34133411
break;
34143412

@@ -3752,9 +3750,6 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
37523750
case AT_MovePartition:
37533751
move_partition(rel->rd_id, cmd);
37543752
break;
3755-
case AT_PartitionBy:
3756-
create_partitions((PartitionInfo *) cmd->def, rel->rd_id, true);
3757-
break;
37583753
default: /* oops */
37593754
elog(ERROR, "unrecognized alter table type: %d",
37603755
(int) cmd->subtype);

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