|
| 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 | +Oidrelid; |
| 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 | +caseAT_AttachPartition: |
| 48 | +caseAT_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 | +boolis_pathman_related_partitioning_cmd(Node*parsetree) |
| 61 | +{ |
| 62 | +if (IsA(parsetree,AlterTableStmt)) |
| 63 | +{ |
| 64 | +ListCell*lc; |
| 65 | +AlterTableStmt*stmt= (AlterTableStmt*)parsetree; |
| 66 | +intcnt=0; |
| 67 | + |
| 68 | +foreach(lc,stmt->cmds) |
| 69 | +{ |
| 70 | +AlterTableCmd*cmd= (AlterTableCmd*)lfirst(lc); |
| 71 | +intsubtype=cmd->subtype; |
| 72 | + |
| 73 | +if (subtype<0) |
| 74 | +subtype=-subtype; |
| 75 | + |
| 76 | +switch (subtype) |
| 77 | +{ |
| 78 | +caseAT_AttachPartition: |
| 79 | +caseAT_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 | +staticFuncExpr* |
| 97 | +make_fn_expr(OidfuncOid,List*args) |
| 98 | +{ |
| 99 | +FuncExpr*fn_expr; |
| 100 | +HeapTupleprocTup; |
| 101 | +Form_pg_procprocStruct; |
| 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 | +returnfn_expr; |
| 112 | +} |
| 113 | + |
| 114 | +/* |
| 115 | + * Transform one constant in a partition bound spec |
| 116 | + */ |
| 117 | +staticConst* |
| 118 | +transform_bound_value(ParseState*pstate,A_Const*con, |
| 119 | +OidcolType,int32colTypmod) |
| 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 | +voidhandle_attach_partition(AlterTableStmt*stmt,AlterTableCmd*cmd) |
| 161 | +{ |
| 162 | +Oidparent_relid, |
| 163 | +partition_relid, |
| 164 | +proc_args[]= {REGCLASSOID,REGCLASSOID, |
| 165 | +ANYELEMENTOID,ANYELEMENTOID }; |
| 166 | + |
| 167 | +List*proc_name; |
| 168 | +FmgrInfoproc_flinfo; |
| 169 | +FunctionCallInfoDataproc_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 | +constPartRelationInfo*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 | +voidhandle_detach_partition(AlterTableStmt*stmt,AlterTableCmd*cmd) |
| 235 | +{ |
| 236 | +Assert(cmd->subtype==AT_DetachPartition); |
| 237 | +} |