Skip to content

Commit 84682ff

Browse files
committed
Add support for ALTER TABLE .. ATTACH PARTITION
1 parent 50c078b commit 84682ff

7 files changed

+324
-3
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ OBJS = src/init.o src/relation_info.o src/utils.o src/partition_filter.o \
88
src/hooks.o src/nodes_common.o src/xact_handling.o src/utility_stmt_hooking.o \
99
src/planner_tree_modification.o src/debug_print.o src/partition_creation.o \
1010
src/compat/pg_compat.o src/compat/rowmarks_fix.o src/partition_router.o \
11-
src/partition_overseer.o $(WIN32RES)
11+
src/partition_overseer.o src/declarative.o $(WIN32RES)
1212

1313
ifdef USE_PGXS
1414
override PG_CPPFLAGS += -I$(CURDIR)/src/include

src/declarative.c

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
#include "declarative.h"
2+
#include "utils.h"
3+
4+
#include "fmgr.h"
5+
#include "access/htup_details.h"
6+
#include "catalog/namespace.h"
7+
#include "catalog/pg_type.h"
8+
#include "catalog/pg_proc.h"
9+
#include "nodes/nodeFuncs.h"
10+
#include "parser/parse_func.h"
11+
#include "parser/parse_coerce.h"
12+
#include "utils/int8.h"
13+
#include "utils/lsyscache.h"
14+
#include "utils/builtins.h"
15+
#include "utils/int8.h"
16+
#include "utils/lsyscache.h"
17+
#include "utils/syscache.h"
18+
#include "utils/varbit.h"
19+
20+
/*
21+
* Modifies query of declarative partitioning commands,
22+
* There is a little hack here, ATTACH PARTITION command
23+
* expects relation with REL_PARTITIONED_TABLE relkind.
24+
* To avoid this check we negate subtype, and then after the checks
25+
* we set it back (look `is_pathman_related_partitioning_cmd`)
26+
*/
27+
void
28+
modify_declative_partitioning_query(Query *query)
29+
{
30+
if (query->commandType != CMD_UTILITY)
31+
return;
32+
33+
if (IsA(query->utilityStmt, AlterTableStmt))
34+
{
35+
ListCell *lcmd;
36+
Oid relid;
37+
38+
AlterTableStmt *stmt = (AlterTableStmt *) query->utilityStmt;
39+
relid = RangeVarGetRelid(stmt->relation, NoLock, true);
40+
if (get_pathman_relation_info(relid) != NULL)
41+
{
42+
foreach(lcmd, stmt->cmds)
43+
{
44+
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
45+
switch (cmd->subtype)
46+
{
47+
case AT_AttachPartition:
48+
case AT_DetachPartition:
49+
cmd->subtype = -cmd->subtype;
50+
break;
51+
default:
52+
break;
53+
}
54+
}
55+
}
56+
}
57+
}
58+
59+
/* is it one of declarative partitioning commands? */
60+
bool is_pathman_related_partitioning_cmd(Node *parsetree)
61+
{
62+
if (IsA(parsetree, AlterTableStmt))
63+
{
64+
ListCell *lc;
65+
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
66+
int cnt = 0;
67+
68+
foreach(lc, stmt->cmds)
69+
{
70+
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lc);
71+
int subtype = cmd->subtype;
72+
73+
if (subtype < 0)
74+
subtype = -subtype;
75+
76+
switch (subtype)
77+
{
78+
case AT_AttachPartition:
79+
case AT_DetachPartition:
80+
/*
81+
* we need to fix all subtypes,
82+
* possibly we're not going to handle this
83+
*/
84+
cmd->subtype = -(cmd->subtype);
85+
continue;
86+
default:
87+
cnt++;
88+
}
89+
}
90+
91+
return (cnt == 0);
92+
}
93+
return false;
94+
}
95+
96+
static FuncExpr *
97+
make_fn_expr(Oid funcOid, List *args)
98+
{
99+
FuncExpr *fn_expr;
100+
HeapTuple procTup;
101+
Form_pg_proc procStruct;
102+
103+
procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
104+
if (!HeapTupleIsValid(procTup))
105+
elog(ERROR, "cache lookup failed for function %u", funcOid);
106+
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
107+
108+
fn_expr = makeFuncExpr(funcOid, procStruct->prorettype, args,
109+
InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
110+
ReleaseSysCache(procTup);
111+
return fn_expr;
112+
}
113+
114+
/*
115+
* Transform one constant in a partition bound spec
116+
*/
117+
static Const *
118+
transform_bound_value(ParseState *pstate, A_Const *con,
119+
Oid colType, int32 colTypmod)
120+
{
121+
Node *value;
122+
123+
/* Make it into a Const */
124+
value = (Node *) make_const(pstate, &con->val, con->location);
125+
126+
/* Coerce to correct type */
127+
value = coerce_to_target_type(pstate,
128+
value, exprType(value),
129+
colType,
130+
colTypmod,
131+
COERCION_ASSIGNMENT,
132+
COERCE_IMPLICIT_CAST,
133+
-1);
134+
135+
if (value == NULL)
136+
ereport(ERROR,
137+
(errcode(ERRCODE_DATATYPE_MISMATCH),
138+
errmsg("specified value cannot be cast to type %s",
139+
format_type_be(colType)),
140+
parser_errposition(pstate, con->location)));
141+
142+
/* Simplify the expression, in case we had a coercion */
143+
if (!IsA(value, Const))
144+
value = (Node *) expression_planner((Expr *) value);
145+
146+
/* Fail if we don't have a constant (i.e., non-immutable coercion) */
147+
if (!IsA(value, Const))
148+
ereport(ERROR,
149+
(errcode(ERRCODE_DATATYPE_MISMATCH),
150+
errmsg("specified value cannot be cast to type %s",
151+
format_type_be(colType)),
152+
errdetail("The cast requires a non-immutable conversion."),
153+
errhint("Try putting the literal value in single quotes."),
154+
parser_errposition(pstate, con->location)));
155+
156+
return (Const *) value;
157+
}
158+
159+
/* handle ALTER TABLE .. ATTACH PARTITION command */
160+
void handle_attach_partition(AlterTableStmt *stmt, AlterTableCmd *cmd)
161+
{
162+
Oid parent_relid,
163+
partition_relid,
164+
proc_args[] = { REGCLASSOID, REGCLASSOID,
165+
ANYELEMENTOID, ANYELEMENTOID };
166+
167+
List *proc_name;
168+
FmgrInfo proc_flinfo;
169+
FunctionCallInfoData proc_fcinfo;
170+
char *pathman_schema;
171+
PartitionRangeDatum *ldatum,
172+
*rdatum;
173+
Const *lval,
174+
*rval;
175+
A_Const *con;
176+
List *fn_args;
177+
ParseState *pstate = make_parsestate(NULL);
178+
const PartRelationInfo *prel;
179+
180+
PartitionCmd *pcmd = (PartitionCmd *) cmd->def;
181+
182+
Assert(cmd->subtype == AT_AttachPartition);
183+
184+
parent_relid = RangeVarGetRelid(stmt->relation, NoLock, false);
185+
if ((prel = get_pathman_relation_info(parent_relid)) == NULL)
186+
elog(ERROR, "relation is not partitioned");
187+
188+
partition_relid = RangeVarGetRelid(pcmd->name, NoLock, false);
189+
190+
/* Fetch pg_pathman's schema */
191+
pathman_schema = get_namespace_name(get_pathman_schema());
192+
193+
/* Build function's name */
194+
proc_name = list_make2(makeString(pathman_schema),
195+
makeString(CppAsString(attach_range_partition)));
196+
197+
ldatum = (PartitionRangeDatum *) linitial(pcmd->bound->lowerdatums);
198+
con = castNode(A_Const, ldatum->value);
199+
lval = transform_bound_value(pstate, con, prel->ev_type, prel->ev_typmod);
200+
201+
rdatum = (PartitionRangeDatum *) linitial(pcmd->bound->upperdatums);
202+
con = castNode(A_Const, rdatum->value);
203+
rval = transform_bound_value(pstate, con, prel->ev_type, prel->ev_typmod);
204+
205+
/* Lookup function's Oid and get FmgrInfo */
206+
fmgr_info(LookupFuncName(proc_name, 4, proc_args, false), &proc_flinfo);
207+
208+
InitFunctionCallInfoData(proc_fcinfo, &proc_flinfo,
209+
4, InvalidOid, NULL, NULL);
210+
proc_fcinfo.arg[0] = ObjectIdGetDatum(parent_relid);
211+
proc_fcinfo.argnull[0] = false;
212+
proc_fcinfo.arg[1] = ObjectIdGetDatum(partition_relid);
213+
proc_fcinfo.argnull[1] = false;
214+
215+
/* Make function expression, we will need it to determine argument types */
216+
fn_args = list_make4(NULL, NULL, lval, rval);
217+
proc_fcinfo.flinfo->fn_expr =
218+
(Node *) make_fn_expr(proc_fcinfo.flinfo->fn_oid, fn_args);
219+
220+
if ((!list_length(pcmd->bound->lowerdatums)) ||
221+
(!list_length(pcmd->bound->upperdatums)))
222+
elog(ERROR, "provide start and end value for range partition");
223+
224+
proc_fcinfo.arg[2] = lval->constvalue;
225+
proc_fcinfo.argnull[2] = ldatum->infinite || lval->constisnull;
226+
proc_fcinfo.arg[3] = rval->constvalue;
227+
proc_fcinfo.argnull[3] = rdatum->infinite || rval->constisnull;
228+
229+
/* Invoke the callback */
230+
FunctionCallInvoke(&proc_fcinfo);
231+
}
232+
233+
/* handle ALTER TABLE .. DETACH PARTITION command */
234+
void handle_detach_partition(AlterTableStmt *stmt, AlterTableCmd *cmd)
235+
{
236+
Assert(cmd->subtype == AT_DetachPartition);
237+
}

src/hooks.c

+31
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "compat/pg_compat.h"
1414
#include "compat/rowmarks_fix.h"
1515

16+
#include "declarative.h"
1617
#include "hooks.h"
1718
#include "init.h"
1819
#include "partition_filter.h"
@@ -766,6 +767,8 @@ pathman_post_parse_analyze_hook(ParseState *pstate, Query *query)
766767
{
767768
load_config(); /* perform main cache initialization */
768769
}
770+
if (!IsPathmanReady())
771+
return;
769772

770773
/* Process inlined SQL functions (we've already entered planning stage) */
771774
if (IsPathmanReady() && get_planner_calls_count() > 0)
@@ -812,7 +815,10 @@ pathman_post_parse_analyze_hook(ParseState *pstate, Query *query)
812815

813816
/* Modify query tree if needed */
814817
pathman_transform_query(query, NULL);
818+
return;
815819
}
820+
821+
pathman_post_analyze_query(query);
816822
}
817823

818824
/*
@@ -950,6 +956,31 @@ pathman_process_utility_hook(Node *first_arg,
950956
get_attname_compat(relation_oid, attr_number),
951957
get_rel_name(relation_oid))));
952958
}
959+
else if (is_pathman_related_partitioning_cmd(parsetree))
960+
{
961+
/* we can handle all the partitioning commands */
962+
if (IsA(parsetree, AlterTableStmt))
963+
{
964+
ListCell *lc;
965+
AlterTableStmt *stmt = (AlterTableStmt *) parsetree;
966+
967+
foreach(lc, stmt->cmds)
968+
{
969+
AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lc);
970+
switch (cmd->subtype)
971+
{
972+
case AT_AttachPartition:
973+
handle_attach_partition(stmt, cmd);
974+
return;
975+
case AT_DetachPartition:
976+
handle_detach_partition(stmt, cmd);
977+
return;
978+
default:
979+
elog(ERROR, "can't handle this command");
980+
}
981+
}
982+
}
983+
}
953984
}
954985

955986
/* Finally call process_utility_hook_next or standard_ProcessUtility */

src/include/declarative.h

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef DECLARATIVE_H
2+
#define DECLARATIVE_H
3+
4+
#include "postgres.h"
5+
#include "nodes/nodes.h"
6+
#include "nodes/parsenodes.h"
7+
8+
typedef enum DeclarativeCommandType {
9+
DP_ATTACH, /* ALTER TABLE .. ATTACH PARTITION */
10+
DP_DETACH /* ALTER TABLE .. DETACH PARTITION */
11+
} DeclarativeCommandType;
12+
13+
void modify_declative_partitioning_query(Query *query);
14+
bool is_pathman_related_partitioning_cmd(Node *parsetree);
15+
16+
/* actual actions */
17+
void handle_attach_partition(AlterTableStmt *stmt, AlterTableCmd *cmd);
18+
void handle_detach_partition(AlterTableStmt *stmt, AlterTableCmd *cmd);
19+
20+
#endif /* DECLARATIVE_H */

src/include/planner_tree_modification.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ void state_tree_visitor(PlanState *state,
3434
void (*visitor) (PlanState *state, void *context),
3535
void *context);
3636

37-
/* Query tree rewriting utility */
37+
/* Query tree rewriting utilities */
3838
void pathman_transform_query(Query *parse, ParamListInfo params);
39+
void pathman_post_analyze_query(Query *parse);
3940

4041
/* These functions scribble on Plan tree */
4142
Plan *add_partition_filters(List *rtable, Plan *plan);

src/planner_tree_modification.c

+33
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "compat/rowmarks_fix.h"
1414

15+
#include "declarative.h"
1516
#include "partition_filter.h"
1617
#include "partition_router.h"
1718
#include "partition_overseer.h"
@@ -107,6 +108,7 @@ typedef struct
107108
} adjust_appendrel_varnos_cxt;
108109

109110
static bool pathman_transform_query_walker(Node *node, void *context);
111+
static bool pathman_post_analyze_query_walker(Node *node, void *context);
110112

111113
static void disable_standard_inheritance(Query *parse, transform_query_cxt *context);
112114
static void handle_modification_query(Query *parse, transform_query_cxt *context);
@@ -337,6 +339,12 @@ pathman_transform_query(Query *parse, ParamListInfo params)
337339
pathman_transform_query_walker((Node *) parse, (void *) &context);
338340
}
339341

342+
void
343+
pathman_post_analyze_query(Query *parse)
344+
{
345+
pathman_post_analyze_query_walker((Node *) parse, NULL);
346+
}
347+
340348
/* Walker for pathman_transform_query() */
341349
static bool
342350
pathman_transform_query_walker(Node *node, void *context)
@@ -410,6 +418,31 @@ pathman_transform_query_walker(Node *node, void *context)
410418
context);
411419
}
412420

421+
static bool
422+
pathman_post_analyze_query_walker(Node *node, void *context)
423+
{
424+
if (node == NULL)
425+
return false;
426+
427+
else if (IsA(node, Query))
428+
{
429+
Query *query = (Query *) node;
430+
431+
/* Make changes for declarative syntax */
432+
modify_declative_partitioning_query(query);
433+
434+
/* Handle Query node */
435+
return query_tree_walker(query,
436+
pathman_post_analyze_query_walker,
437+
context,
438+
0);
439+
}
440+
441+
/* Handle expression subtree */
442+
return expression_tree_walker(node,
443+
pathman_post_analyze_query_walker,
444+
context);
445+
}
413446

414447
/*
415448
* ----------------------

0 commit comments

Comments
 (0)