|
| 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 | +} |
0 commit comments