本篇内容主要讲解“怎么使用PostgreSQL中ExecInitExprRec函数”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么使用PostgreSQL中ExecInitExprRec函数”吧!
EEO_XXX宏定义
opcode分发器宏定义
/*
* Macros for opcode dispatch.
* opcode分发器宏定义
*
* EEO_SWITCH - just hides the switch if not in use.
* EEO_SWITCH - 如未使用,则隐藏switch
*
* EEO_CASE - labels the implementation of named expression step type.
* EEO_CASE - 标签化已命名的表达式步骤类型的实现
*
* EEO_DISPATCH - jump to the implementation of the step type for 'op'.
* EEO_DISPATCH - 跳到'op'指定的步骤类型的实现
*
* EEO_OPCODE - compute opcode required by used expression evaluation method.
* - 通过请求的表达式解析方法计算opcode
*
* EEO_NEXT - increment 'op' and jump to correct next step type.
* - 'op'++并跳转到下一个步骤类型
*
* EEO_JUMP - jump to the specified step number within the current expression.
* EEO_JUMP - 在当前表达式中跳转到指定的步骤编号
*/
#if defined(EEO_USE_COMPUTED_GOTO)
//--------------- 定义了EEO_USE_COMPUTED_GOTO
/* struct for jump target -> opcode lookup table */
//跳转target -> opcode搜索表结构体
typedef struct ExprEvalOpLookup
{
const void *opcode;
ExprEvalOp op;
} ExprEvalOpLookup;
/* to make dispatch_table accessible outside ExecInterpExpr() */
static const void **dispatch_table = NULL;
/* jump target -> opcode lookup table */
static ExprEvalOpLookup reverse_dispatch_table[EEOP_LAST];
#define EEO_SWITCH()
#define EEO_CASE(name) CASE_##name:
#define EEO_DISPATCH() goto *((void *) op->opcode)
#define EEO_OPCODE(opcode) ((intptr_t) dispatch_table[opcode])
#else /* !EEO_USE_COMPUTED_GOTO */
//--------------- 没有定义EEO_USE_COMPUTED_GOTO
#define EEO_SWITCH() starteval: switch ((ExprEvalOp) op->opcode)
#define EEO_CASE(name) case name:
#define EEO_DISPATCH() goto starteval
#define EEO_OPCODE(opcode) (opcode)
#endif /* EEO_USE_COMPUTED_GOTO */
#define EEO_NEXT() \
do { \
op++; \
EEO_DISPATCH(); \
} while (0)
#define EEO_JUMP(stepno) \
do { \
op = &state->steps[stepno]; \
EEO_DISPATCH(); \
} while (0)
ExprState
解析表达式中运行期状态节点
/* Bits in ExprState->flags (see also execExpr.h for private flag bits): */
/* expression is for use with ExecQual() */
#define EEO_FLAG_IS_QUAL (1 << 0)
typedef struct ExprState
{
//节点tag
Node tag;
//EEO_FLAG_IS_QUAL
uint8 flags; /* bitmask of EEO_FLAG_* bits, see above */
/*
* Storage for result value of a scalar expression, or for individual
* column results within expressions built by ExecBuildProjectionInfo().
* 存储scalar expression表达式
* 和通过ExecBuildProjectionInfo()函数创建的expressions单列的结果.
*/
#define FIELDNO_EXPRSTATE_RESNULL 2
bool resnull;
#define FIELDNO_EXPRSTATE_RESVALUE 3
Datum resvalue;
/*
* If projecting a tuple result, this slot holds the result; else NULL.
* 如果投影元组结果,该slot存储结果,或者为NULL.
*/
#define FIELDNO_EXPRSTATE_RESULTSLOT 4
TupleTableSlot *resultslot;
/*
* Instructions to compute expression's return value.
* 计算表达式返回结果的基础"架构"
*/
struct ExprEvalStep *steps;
/*
* Function that actually evaluates the expression. This can be set to
* different values depending on the complexity of the expression.
* 实际解析表达式的函数.
* 根据表达式的复杂程度,可以设置为不同的值.
*/
ExprStateEvalFunc evalfunc;
/* original expression tree, for debugging only */
//原始的表达式树,仅用于debugging
Expr *expr;
/* private state for an evalfunc */
//evalfunc的私有状态
void *evalfunc_private;
/*
* XXX: following fields only needed during "compilation" (ExecInitExpr);
* could be thrown away afterwards.
* XXX: 接下来的字段在"compilation" (ExecInitExpr)期间需要,之后可被"扔掉".
*/
//当前的步数
int steps_len; /* number of steps currently */
//steps数组已分配的长度
int steps_alloc; /* allocated length of steps array */
//父PlanState节点(如存在)
struct PlanState *parent; /* parent PlanState node, if any */
//用于编译PARAM_EXTERN节点
ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
//
Datum *innermost_caseval;
bool *innermost_casenull;
Datum *innermost_domainval;
bool *innermost_domainnull;
} ExprState;
ExprEvalStep
表达式解析步骤结构体
typedef struct ExprEvalStep
{
/*
* Instruction to be executed. During instruction preparation this is an
* enum ExprEvalOp, but later it can be changed to some other type, e.g. a
* pointer for computed goto (that's why it's an intptr_t).
* 待执行指令.
* 在指令准备期间这是枚举型的ExprEvalOp,
* 但后续会被改变为某些其他类型,比如用于goto的指针,因此被定义为intprt_t类型
*/
intptr_t opcode;
/* where to store the result of this step */
//存储该步骤的结果
Datum *resvalue;
bool *resnull;
/*
* Inline data for the operation. Inline data is faster to access, but
* also bloats the size of all instructions. The union should be kept to
* no more than 40 bytes on 64-bit systems (so that the entire struct is
* no more than 64 bytes, a single cacheline on common systems).
* 操作的内联数据.
* 内联数据用于更快的访问,但同时会导致指令的盘膨胀.
* 联合体在64-bit系统上应保持在40字节范围内
* (因此整个结构体不应大于64字节,普通系统上的单个缓存线大小)
*/
union
{
/* for EEOP_INNER/OUTER/SCAN_FETCHSOME */
//用于EEOP_INNER/OUTER/SCAN_FETCHSOME
struct
{
/* attribute number up to which to fetch (inclusive) */
//获取到的属性编号
int last_var;
TupleDesc known_desc;
} fetch;
/* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */
struct
{
/* attnum is attr number - 1 for regular VAR ... */
//attnum是常规VAR的attr number - 1
/* but it's just the normal (negative) attr number for SYSVAR */
//对于SYSVAR,该值是常规的attr number
int attnum;
Oid vartype; /* type OID of variable */
} var;
/* for EEOP_WHOLEROW */
struct
{
Var *var; /* original Var node in plan tree */
bool first; /* first time through, need to initialize? */
bool slow; /* need runtime check for nulls? */
TupleDesc tupdesc; /* descriptor for resulting tuples */
JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */
} wholerow;
/* for EEOP_ASSIGN_*_VAR */
struct
{
/* target index in ExprState->resultslot->tts_values/nulls */
int resultnum;
/* source attribute number - 1 */
int attnum;
} assign_var;
/* for EEOP_ASSIGN_TMP[_MAKE_RO] */
struct
{
/* target index in ExprState->resultslot->tts_values/nulls */
int resultnum;
} assign_tmp;
/* for EEOP_CONST */
struct
{
/* constant's value */
Datum value;
bool isnull;
} constval;
/* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
//对于EEOP_FUNCEXPR_* / NULLIF / DISTINCT
struct
{
//函数的检索数据
FmgrInfo *finfo; /* function's lookup data */
//参数信息等
FunctionCallInfo fcinfo_data; /* arguments etc */
/* faster to access without additional indirection: */
//无需额外的指向,更快速的访问
PGFunction fn_addr; /* actual call address */
int nargs; /* number of arguments */
} func;
/* for EEOP_BOOL_*_STEP */
struct
{
bool *anynull; /* track if any input was NULL */
int jumpdone; /* jump here if result determined */
} boolexpr;
/* for EEOP_QUAL */
struct
{
int jumpdone; /* jump here on false or null */
} qualexpr;
/* for EEOP_JUMP[_CONDITION] */
struct
{
int jumpdone; /* target instruction's index */
} jump;
/* for EEOP_NULLTEST_ROWIS[NOT]NULL */
struct
{
/* cached tupdesc pointer - filled at runtime */
TupleDesc argdesc;
} nulltest_row;
/* for EEOP_PARAM_EXEC/EXTERN */
struct
{
int paramid; /* numeric ID for parameter */
Oid paramtype; /* OID of parameter's datatype */
} param;
/* for EEOP_PARAM_CALLBACK */
struct
{
ExecEvalSubroutine paramfunc; /* add-on evaluation subroutine */
void *paramarg; /* private data for same */
int paramid; /* numeric ID for parameter */
Oid paramtype; /* OID of parameter's datatype */
} cparam;
/* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
struct
{
Datum *value; /* value to return */
bool *isnull;
} casetest;
/* for EEOP_MAKE_READONLY */
struct
{
Datum *value; /* value to coerce to read-only */
bool *isnull;
} make_readonly;
/* for EEOP_IOCOERCE */
struct
{
/* lookup and call info for source type's output function */
FmgrInfo *finfo_out;
FunctionCallInfo fcinfo_data_out;
/* lookup and call info for result type's input function */
FmgrInfo *finfo_in;
FunctionCallInfo fcinfo_data_in;
} iocoerce;
/* for EEOP_SQLVALUEFUNCTION */
struct
{
SQLValueFunction *svf;
} sqlvaluefunction;
/* for EEOP_NEXTVALUEEXPR */
//EEOP_NEXTVALUEEXPR
struct
{
Oid seqid;
Oid seqtypid;
} nextvalueexpr;
/* for EEOP_ARRAYEXPR */
struct
{
Datum *elemvalues; /* element values get stored here */
bool *elemnulls;
int nelems; /* length of the above arrays */
Oid elemtype; /* array element type */
int16 elemlength; /* typlen of the array element type */
bool elembyval; /* is the element type pass-by-value? */
char elemalign; /* typalign of the element type */
bool multidims; /* is array expression multi-D? */
} arrayexpr;
/* for EEOP_ARRAYCOERCE */
struct
{
ExprState *elemexprstate; /* null if no per-element work */
Oid resultelemtype; /* element type of result array */
struct ArrayMapState *amstate; /* workspace for array_map */
} arraycoerce;
/* for EEOP_ROW */
struct
{
TupleDesc tupdesc; /* descriptor for result tuples */
/* workspace for the values constituting the row: */
Datum *elemvalues;
bool *elemnulls;
} row;
/* for EEOP_ROWCOMPARE_STEP */
struct
{
/* lookup and call data for column comparison function */
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
PGFunction fn_addr;
/* target for comparison resulting in NULL */
int jumpnull;
/* target for comparison yielding inequality */
int jumpdone;
} rowcompare_step;
/* for EEOP_ROWCOMPARE_FINAL */
struct
{
RowCompareType rctype;
} rowcompare_final;
/* for EEOP_MINMAX */
struct
{
/* workspace for argument values */
Datum *values;
bool *nulls;
int nelems;
/* is it GREATEST or LEAST? */
MinMaxOp op;
/* lookup and call data for comparison function */
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
} minmax;
/* for EEOP_FIELDSELECT */
struct
{
AttrNumber fieldnum; /* field number to extract */
Oid resulttype; /* field's type */
/* cached tupdesc pointer - filled at runtime */
TupleDesc argdesc;
} fieldselect;
/* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */
struct
{
/* original expression node */
FieldStore *fstore;
/* cached tupdesc pointer - filled at runtime */
/* note that a DEFORM and FORM pair share the same tupdesc */
TupleDesc *argdesc;
/* workspace for column values */
Datum *values;
bool *nulls;
int ncolumns;
} fieldstore;
/* for EEOP_ARRAYREF_SUBSCRIPT */
struct
{
/* too big to have inline */
struct ArrayRefState *state;
int off; /* 0-based index of this subscript */
bool isupper; /* is it upper or lower subscript? */
int jumpdone; /* jump here on null */
} arrayref_subscript;
/* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */
struct
{
/* too big to have inline */
struct ArrayRefState *state;
} arrayref;
/* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */
struct
{
/* name of constraint */
char *constraintname;
/* where the result of a CHECK constraint will be stored */
Datum *checkvalue;
bool *checknull;
/* OID of domain type */
Oid resulttype;
} domaincheck;
/* for EEOP_CONVERT_ROWTYPE */
struct
{
ConvertRowtypeExpr *convert; /* original expression */
/* these three fields are filled at runtime: */
TupleDesc indesc; /* tupdesc for input type */
TupleDesc outdesc; /* tupdesc for output type */
TupleConversionMap *map; /* column mapping */
bool initialized; /* initialized for current types? */
} convert_rowtype;
/* for EEOP_SCALARARRAYOP */
struct
{
/* element_type/typlen/typbyval/typalign are filled at runtime */
Oid element_type; /* InvalidOid if not yet filled */
bool useOr; /* use OR or AND semantics? */
int16 typlen; /* array element type storage info */
bool typbyval;
char typalign;
FmgrInfo *finfo; /* function's lookup data */
FunctionCallInfo fcinfo_data; /* arguments etc */
/* faster to access without additional indirection: */
PGFunction fn_addr; /* actual call address */
} scalararrayop;
/* for EEOP_XMLEXPR */
struct
{
XmlExpr *xexpr; /* original expression node */
/* workspace for evaluating named args, if any */
Datum *named_argvalue;
bool *named_argnull;
/* workspace for evaluating unnamed args, if any */
Datum *argvalue;
bool *argnull;
} xmlexpr;
/* for EEOP_AGGREF */
struct
{
/* out-of-line state, modified by nodeAgg.c */
AggrefExprState *astate;
} aggref;
/* for EEOP_GROUPING_FUNC */
struct
{
AggState *parent; /* parent Agg */
List *clauses; /* integer list of column numbers */
} grouping_func;
/* for EEOP_WINDOW_FUNC */
struct
{
/* out-of-line state, modified by nodeWindowFunc.c */
WindowFuncExprState *wfstate;
} window_func;
/* for EEOP_SUBPLAN */
struct
{
/* out-of-line state, created by nodeSubplan.c */
SubPlanState *sstate;
} subplan;
/* for EEOP_ALTERNATIVE_SUBPLAN */
struct
{
/* out-of-line state, created by nodeSubplan.c */
AlternativeSubPlanState *asstate;
} alternative_subplan;
/* for EEOP_AGG_*DESERIALIZE */
struct
{
AggState *aggstate;
FunctionCallInfo fcinfo_data;
int jumpnull;
} agg_deserialize;
/* for EEOP_AGG_STRICT_INPUT_CHECK */
struct
{
bool *nulls;
int nargs;
int jumpnull;
} agg_strict_input_check;
/* for EEOP_AGG_INIT_TRANS */
struct
{
AggState *aggstate;
AggStatePerTrans pertrans;
ExprContext *aggcontext;
int setno;
int transno;
int setoff;
int jumpnull;
} agg_init_trans;
/* for EEOP_AGG_STRICT_TRANS_CHECK */
struct
{
AggState *aggstate;
int setno;
int transno;
int setoff;
int jumpnull;
} agg_strict_trans_check;
/* for EEOP_AGG_{PLAIN,ORDERED}_TRANS* */
struct
{
AggState *aggstate;
AggStatePerTrans pertrans;
ExprContext *aggcontext;
int setno;
int transno;
int setoff;
} agg_trans;
} d;
} ExprEvalStep;
ExprEvalOp
ExprEvalSteps的鉴频器,定义哪个操作将被执行并且联合体ExprEvalStep->d中的哪个struct将被使用.
/*
* Discriminator for ExprEvalSteps.
* ExprEvalSteps的鉴频器
*
* Identifies the operation to be executed and which member in the
* ExprEvalStep->d union is valid.
* 定义哪个操作将被执行并且联合体ExprEvalStep->d中的哪个struct将被使用.
*
* The order of entries needs to be kept in sync with the dispatch_table[]
* array in execExprInterp.c:ExecInterpExpr().
* 条目的排序需要与execExprInterp.c:ExecInterpExpr()中dispatch_table[]数组的元素保持一致
*/
typedef enum ExprEvalOp
{
/* entire expression has been evaluated completely, return */
//整个表达式已被解析,返回
EEOP_DONE,
/* apply slot_getsomeattrs on corresponding tuple slot */
//在相应的元组slot上应用了slot_getsomeattrs方法
EEOP_INNER_FETCHSOME,
EEOP_OUTER_FETCHSOME,
EEOP_SCAN_FETCHSOME,
/* compute non-system Var value */
//计算非系统Var变量值
EEOP_INNER_VAR,
EEOP_OUTER_VAR,
EEOP_SCAN_VAR,
/* compute system Var value */
//计算系统Var变量值
EEOP_INNER_SYSVAR,
EEOP_OUTER_SYSVAR,
EEOP_SCAN_SYSVAR,
/* compute wholerow Var */
//计算整行Var
EEOP_WHOLEROW,
/*
* Compute non-system Var value, assign it into ExprState's resultslot.
* These are not used if a CheckVarSlotCompatibility() check would be
* needed.
* 计算非系统Var值,分配到ExprState's的resultslot字段中.
* 如果CheckVarSlotCompatibility()需要时,这些都不需要.
*/
EEOP_ASSIGN_INNER_VAR,
EEOP_ASSIGN_OUTER_VAR,
EEOP_ASSIGN_SCAN_VAR,
/* assign ExprState's resvalue/resnull to a column of its resultslot */
//分配ExprState's resvalue/resnull到该列的resultslot中
EEOP_ASSIGN_TMP,
/* ditto, applying MakeExpandedObjectReadOnly() */
//同上,应用MakeExpandedObjectReadOnly()
EEOP_ASSIGN_TMP_MAKE_RO,
/* evaluate Const value */
//解析常量值
EEOP_CONST,
/*
* Evaluate function call (including OpExprs etc). For speed, we
* distinguish in the opcode whether the function is strict and/or
* requires usage stats tracking.
* 解析函数调用(包括OpExprs等等).
* 出于性能的考虑,需要区分opcode是strict函数还是非strict函数,以及是否需要统计跟踪.
*/
EEOP_FUNCEXPR,
EEOP_FUNCEXPR_STRICT,
EEOP_FUNCEXPR_FUSAGE,
EEOP_FUNCEXPR_STRICT_FUSAGE,
/*
* Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
* subexpressions are special-cased for performance. Since AND always has
* at least two subexpressions, FIRST and LAST never apply to the same
* subexpression.
* 解析布尔AND表达式,每一个子表达式一个步骤.
* FIRST/LAST子表达式是性能上的特例.
* 由于AND通常至少有两个子表达式,FIRST和LAST永远都不会应用在同一个子表达式上.
*/
EEOP_BOOL_AND_STEP_FIRST,
EEOP_BOOL_AND_STEP,
EEOP_BOOL_AND_STEP_LAST,
/* similarly for boolean OR expression */
//与布尔OR表达式类似
EEOP_BOOL_OR_STEP_FIRST,
EEOP_BOOL_OR_STEP,
EEOP_BOOL_OR_STEP_LAST,
/* evaluate boolean NOT expression */
//解析布尔NOT表达式
EEOP_BOOL_NOT_STEP,
/* simplified version of BOOL_AND_STEP for use by ExecQual() */
//用于ExecQual()中的BOOL_AND_STEP简化版本
EEOP_QUAL,
/* unconditional jump to another step */
//无条件跳转到另外一个步骤
EEOP_JUMP,
/* conditional jumps based on current result value */
//基于当前结果值的条件跳转
EEOP_JUMP_IF_NULL,
EEOP_JUMP_IF_NOT_NULL,
EEOP_JUMP_IF_NOT_TRUE,
/* perform NULL tests for scalar values */
//为scalar值执行NULL测试
EEOP_NULLTEST_ISNULL,
EEOP_NULLTEST_ISNOTNULL,
/* perform NULL tests for row values */
//为行值执行NULL测试
EEOP_NULLTEST_ROWISNULL,
EEOP_NULLTEST_ROWISNOTNULL,
/* evaluate a BooleanTest expression */
//解析BooleanTest表达式
EEOP_BOOLTEST_IS_TRUE,
EEOP_BOOLTEST_IS_NOT_TRUE,
EEOP_BOOLTEST_IS_FALSE,
EEOP_BOOLTEST_IS_NOT_FALSE,
/* evaluate PARAM_EXEC/EXTERN parameters */
//解析PARAM_EXEC/EXTERN参数
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
EEOP_PARAM_CALLBACK,
/* return CaseTestExpr value */
//返回CaseTestExpr值
EEOP_CASE_TESTVAL,
/* apply MakeExpandedObjectReadOnly() to target value */
//对目标值应用MakeExpandedObjectReadOnly()
EEOP_MAKE_READONLY,
/* evaluate assorted special-purpose expression types */
//解析各种特殊用途的表达式类型
EEOP_IOCOERCE,
EEOP_DISTINCT,
EEOP_NOT_DISTINCT,
EEOP_NULLIF,
EEOP_SQLVALUEFUNCTION,
EEOP_CURRENTOFEXPR,
EEOP_NEXTVALUEEXPR,
EEOP_ARRAYEXPR,
EEOP_ARRAYCOERCE,
EEOP_ROW,
/*
* Compare two individual elements of each of two compared ROW()
* expressions. Skip to ROWCOMPARE_FINAL if elements are not equal.
* 给出两个需要对比的ROW()表达式,两两比较行中的元素.
* 如果元素不相等,则跳转到ROWCOMPARE_FINAL
*/
EEOP_ROWCOMPARE_STEP,
/* evaluate boolean value based on previous ROWCOMPARE_STEP operations */
//基于上一步的ROWCOMPARE_STEP操作解析布尔值
EEOP_ROWCOMPARE_FINAL,
/* evaluate GREATEST() or LEAST() */
//解析GREATEST()和LEAST()
EEOP_MINMAX,
/* evaluate FieldSelect expression */
//解析FieldSelect表达式
EEOP_FIELDSELECT,
/*
* Deform tuple before evaluating new values for individual fields in a
* FieldStore expression.
* 在解析FieldStore表达式中的独立列新值前重构元组
*/
EEOP_FIELDSTORE_DEFORM,
/*
* Form the new tuple for a FieldStore expression. Individual fields will
* have been evaluated into columns of the tuple deformed by the preceding
* DEFORM step.
* 为FieldStore表达式构成新元组.
* 单独的字段会解析到元组的列中(行已被上一个步骤EEOP_FIELDSTORE_DEFORM析构)
*/
EEOP_FIELDSTORE_FORM,
/* Process an array subscript; short-circuit expression to NULL if NULL */
//处理数组子脚本.如为NULL则短路表达式为NULL
EEOP_ARRAYREF_SUBSCRIPT,
/*
* Compute old array element/slice when an ArrayRef assignment expression
* contains ArrayRef/FieldStore subexpressions. Value is accessed using
* the CaseTest mechanism.
* 在ArrayRef分配表达式包含ArrayRef/FieldStore子表达式时计算旧的数组元素/片.
* 通过CaseTest机制访问Value
*/
EEOP_ARRAYREF_OLD,
/* compute new value for ArrayRef assignment expression */
//为ArrayRef分配118
EEOP_ARRAYREF_ASSIGN,
/* compute element/slice for ArrayRef fetch expression */
//为ArrayRef提取表达式计算element/slice
EEOP_ARRAYREF_FETCH,
/* evaluate value for CoerceToDomainValue */
//为CoerceToDomainValue解析值
EEOP_DOMAIN_TESTVAL,
/* evaluate a domain's NOT NULL constraint */
//解析域 NOT NULL 约束
EEOP_DOMAIN_NOTNULL,
/* evaluate a single domain CHECK constraint */
//解析单个域CHECK约束
EEOP_DOMAIN_CHECK,
/* evaluate assorted special-purpose expression types */
//解析特殊目的的表达式类型
EEOP_CONVERT_ROWTYPE,
EEOP_SCALARARRAYOP,
EEOP_XMLEXPR,
EEOP_AGGREF,
EEOP_GROUPING_FUNC,
EEOP_WINDOW_FUNC,
EEOP_SUBPLAN,
EEOP_ALTERNATIVE_SUBPLAN,
/* aggregation related nodes */
//聚合相关节点
EEOP_AGG_STRICT_DESERIALIZE,
EEOP_AGG_DESERIALIZE,
EEOP_AGG_STRICT_INPUT_CHECK,
EEOP_AGG_INIT_TRANS,
EEOP_AGG_STRICT_TRANS_CHECK,
EEOP_AGG_PLAIN_TRANS_BYVAL,
EEOP_AGG_PLAIN_TRANS,
EEOP_AGG_ORDERED_TRANS_DATUM,
EEOP_AGG_ORDERED_TRANS_TUPLE,
/* non-existent operation, used e.g. to check array lengths */
//不存在的操作,比如用于检测数组长度
EEOP_LAST
} ExprEvalOp;
ExecInitExprRec函数,把表达式解析需要的步骤追加到ExprState->steps中,可能会递归进入到子表达式节点中.
其主要逻辑是根据节点类型,执行相应的处理逻辑,比如节点类型为OpExpr,则其逻辑为:
case T_OpExpr://操作符表达式
{
OpExpr *op = (OpExpr *) node;
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
state);
ExprEvalPushStep(state, &scratch);
break;
}
其他节点类型类似,代码虽然很长,但逻辑清晰简单.
/*
* Append the steps necessary for the evaluation of node to ExprState->steps,
* possibly recursing into sub-expressions of node.
* 把表达式解析需要的步骤追加到ExprState->steps中,可能会递归进入到子表达式节点中.
*
* node - expression to evaluate
* state - ExprState to whose ->steps to append the necessary operations
* resv / resnull - where to store the result of the node into
* node - 待解析的表达式
* state - 步骤追加到该ExprState ->steps中
* resv / resnull - 节点结果存储的位置
*/
static void
ExecInitExprRec(Expr *node, ExprState *state,
Datum *resv, bool *resnull)
{
ExprEvalStep scratch = {0};
/* Guard against stack overflow due to overly complex expressions */
//避免出现堆栈溢出
check_stack_depth();
/* Step's output location is always what the caller gave us */
//步骤的输出位置往往是调用者提供给我们的
Assert(resv != NULL && resnull != NULL);
scratch.resvalue = resv;
scratch.resnull = resnull;
/* cases should be ordered as they are in enum NodeTag */
//CASE的顺序与NodeTag枚举类型中的顺序一样
switch (nodeTag(node))
{
case T_Var://VAR
{
Var *variable = (Var *) node;
if (variable->varattno == InvalidAttrNumber)
{
/* whole-row Var */
ExecInitWholeRowVar(&scratch, variable, state);
}
else if (variable->varattno <= 0)
{
/* system column */
scratch.d.var.attnum = variable->varattno;
scratch.d.var.vartype = variable->vartype;
switch (variable->varno)
{
case INNER_VAR:
scratch.opcode = EEOP_INNER_SYSVAR;
break;
case OUTER_VAR:
scratch.opcode = EEOP_OUTER_SYSVAR;
break;
/* INDEX_VAR is handled by default case */
default:
scratch.opcode = EEOP_SCAN_SYSVAR;
break;
}
}
else
{
/* regular user column */
scratch.d.var.attnum = variable->varattno - 1;
scratch.d.var.vartype = variable->vartype;
switch (variable->varno)
{
case INNER_VAR:
scratch.opcode = EEOP_INNER_VAR;
break;
case OUTER_VAR:
scratch.opcode = EEOP_OUTER_VAR;
break;
/* INDEX_VAR is handled by default case */
default:
scratch.opcode = EEOP_SCAN_VAR;
break;
}
}
ExprEvalPushStep(state, &scratch);
break;
}
case T_Const://常量
{
Const *con = (Const *) node;
scratch.opcode = EEOP_CONST;
scratch.d.constval.value = con->constvalue;
scratch.d.constval.isnull = con->constisnull;
ExprEvalPushStep(state, &scratch);
break;
}
case T_Param://参数
{
Param *param = (Param *) node;
ParamListInfo params;
switch (param->paramkind)
{
case PARAM_EXEC:
scratch.opcode = EEOP_PARAM_EXEC;
scratch.d.param.paramid = param->paramid;
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
break;
case PARAM_EXTERN:
/*
* If we have a relevant ParamCompileHook, use it;
* otherwise compile a standard EEOP_PARAM_EXTERN
* step. ext_params, if supplied, takes precedence
* over info from the parent node's EState (if any).
*/
if (state->ext_params)
params = state->ext_params;
else if (state->parent &&
state->parent->state)
params = state->parent->state->es_param_list_info;
else
params = NULL;
if (params && params->paramCompile)
{
params->paramCompile(params, param, state,
resv, resnull);
}
else
{
scratch.opcode = EEOP_PARAM_EXTERN;
scratch.d.param.paramid = param->paramid;
scratch.d.param.paramtype = param->paramtype;
ExprEvalPushStep(state, &scratch);
}
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
(int) param->paramkind);
break;
}
break;
}
case T_Aggref://聚集
{
Aggref *aggref = (Aggref *) node;
AggrefExprState *astate = makeNode(AggrefExprState);
scratch.opcode = EEOP_AGGREF;
scratch.d.aggref.astate = astate;
astate->aggref = aggref;
if (state->parent && IsA(state->parent, AggState))
{
AggState *aggstate = (AggState *) state->parent;
aggstate->aggs = lcons(astate, aggstate->aggs);
aggstate->numaggs++;
}
else
{
/* planner messed up */
elog(ERROR, "Aggref found in non-Agg plan node");
}
ExprEvalPushStep(state, &scratch);
break;
}
case T_GroupingFunc:
{
GroupingFunc *grp_node = (GroupingFunc *) node;
Agg *agg;
if (!state->parent || !IsA(state->parent, AggState) ||
!IsA(state->parent->plan, Agg))
elog(ERROR, "GroupingFunc found in non-Agg plan node");
scratch.opcode = EEOP_GROUPING_FUNC;
scratch.d.grouping_func.parent = (AggState *) state->parent;
agg = (Agg *) (state->parent->plan);
if (agg->groupingSets)
scratch.d.grouping_func.clauses = grp_node->cols;
else
scratch.d.grouping_func.clauses = NIL;
ExprEvalPushStep(state, &scratch);
break;
}
case T_WindowFunc:
{
WindowFunc *wfunc = (WindowFunc *) node;
WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
wfstate->wfunc = wfunc;
if (state->parent && IsA(state->parent, WindowAggState))
{
WindowAggState *winstate = (WindowAggState *) state->parent;
int nfuncs;
winstate->funcs = lcons(wfstate, winstate->funcs);
nfuncs = ++winstate->numfuncs;
if (wfunc->winagg)
winstate->numaggs++;
/* for now initialize agg using old style expressions */
wfstate->args = ExecInitExprList(wfunc->args,
state->parent);
wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
state->parent);
/*
* Complain if the windowfunc's arguments contain any
* windowfuncs; nested window functions are semantically
* nonsensical. (This should have been caught earlier,
* but we defend against it here anyway.)
*/
if (nfuncs != winstate->numfuncs)
ereport(ERROR,
(errcode(ERRCODE_WINDOWING_ERROR),
errmsg("window function calls cannot be nested")));
}
else
{
/* planner messed up */
elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
}
scratch.opcode = EEOP_WINDOW_FUNC;
scratch.d.window_func.wfstate = wfstate;
ExprEvalPushStep(state, &scratch);
break;
}
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
ExecInitArrayRef(&scratch, aref, state, resv, resnull);
break;
}
case T_FuncExpr://函数表达式
{
FuncExpr *func = (FuncExpr *) node;
ExecInitFunc(&scratch, node,
func->args, func->funcid, func->inputcollid,
state);
ExprEvalPushStep(state, &scratch);
break;
}
case T_OpExpr://操作符表达式
{
OpExpr *op = (OpExpr *) node;
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
state);
ExprEvalPushStep(state, &scratch);
break;
}
case T_DistinctExpr:
{
DistinctExpr *op = (DistinctExpr *) node;
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
state);
/*
* Change opcode of call instruction to EEOP_DISTINCT.
*
* XXX: historically we've not called the function usage
* pgstat infrastructure - that seems inconsistent given that
* we do so for normal function *and* operator evaluation. If
* we decided to do that here, we'd probably want separate
* opcodes for FUSAGE or not.
*/
scratch.opcode = EEOP_DISTINCT;
ExprEvalPushStep(state, &scratch);
break;
}
case T_NullIfExpr:
{
NullIfExpr *op = (NullIfExpr *) node;
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
state);
/*
* Change opcode of call instruction to EEOP_NULLIF.
*
* XXX: historically we've not called the function usage
* pgstat infrastructure - that seems inconsistent given that
* we do so for normal function *and* operator evaluation. If
* we decided to do that here, we'd probably want separate
* opcodes for FUSAGE or not.
*/
scratch.opcode = EEOP_NULLIF;
ExprEvalPushStep(state, &scratch);
break;
}
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
Expr *scalararg;
Expr *arrayarg;
FmgrInfo *finfo;
FunctionCallInfo fcinfo;
AclResult aclresult;
Assert(list_length(opexpr->args) == 2);
scalararg = (Expr *) linitial(opexpr->args);
arrayarg = (Expr *) lsecond(opexpr->args);
/* Check permission to call function */
aclresult = pg_proc_aclcheck(opexpr->opfuncid,
GetUserId(),
ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION,
get_func_name(opexpr->opfuncid));
InvokeFunctionExecuteHook(opexpr->opfuncid);
/* Set up the primary fmgr lookup information */
finfo = palloc0(sizeof(FmgrInfo));
fcinfo = palloc0(sizeof(FunctionCallInfoData));
fmgr_info(opexpr->opfuncid, finfo);
fmgr_info_set_expr((Node *) node, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
opexpr->inputcollid, NULL, NULL);
/* Evaluate scalar directly into left function argument */
ExecInitExprRec(scalararg, state,
&fcinfo->arg[0], &fcinfo->argnull[0]);
/*
* Evaluate array argument into our return value. There's no
* danger in that, because the return value is guaranteed to
* be overwritten by EEOP_SCALARARRAYOP, and will not be
* passed to any other expression.
*/
ExecInitExprRec(arrayarg, state, resv, resnull);
/* And perform the operation */
scratch.opcode = EEOP_SCALARARRAYOP;
scratch.d.scalararrayop.element_type = InvalidOid;
scratch.d.scalararrayop.useOr = opexpr->useOr;
scratch.d.scalararrayop.finfo = finfo;
scratch.d.scalararrayop.fcinfo_data = fcinfo;
scratch.d.scalararrayop.fn_addr = finfo->fn_addr;
ExprEvalPushStep(state, &scratch);
break;
}
case T_BoolExpr:
{
BoolExpr *boolexpr = (BoolExpr *) node;
int nargs = list_length(boolexpr->args);
List *adjust_jumps = NIL;
int off;
ListCell *lc;
/* allocate scratch memory used by all steps of AND/OR */
if (boolexpr->boolop != NOT_EXPR)
scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool));
/*
* For each argument evaluate the argument itself, then
* perform the bool operation's appropriate handling.
*
* We can evaluate each argument into our result area, since
* the short-circuiting logic means we only need to remember
* previous NULL values.
*
* AND/OR is split into separate STEP_FIRST (one) / STEP (zero
* or more) / STEP_LAST (one) steps, as each of those has to
* perform different work. The FIRST/LAST split is valid
* because AND/OR have at least two arguments.
*/
off = 0;
foreach(lc, boolexpr->args)
{
Expr *arg = (Expr *) lfirst(lc);
/* Evaluate argument into our output variable */
ExecInitExprRec(arg, state, resv, resnull);
/* Perform the appropriate step type */
switch (boolexpr->boolop)
{
case AND_EXPR:
Assert(nargs >= 2);
if (off == 0)
scratch.opcode = EEOP_BOOL_AND_STEP_FIRST;
else if (off + 1 == nargs)
scratch.opcode = EEOP_BOOL_AND_STEP_LAST;
else
scratch.opcode = EEOP_BOOL_AND_STEP;
break;
case OR_EXPR:
Assert(nargs >= 2);
if (off == 0)
scratch.opcode = EEOP_BOOL_OR_STEP_FIRST;
else if (off + 1 == nargs)
scratch.opcode = EEOP_BOOL_OR_STEP_LAST;
else
scratch.opcode = EEOP_BOOL_OR_STEP;
break;
case NOT_EXPR:
Assert(nargs == 1);
scratch.opcode = EEOP_BOOL_NOT_STEP;
break;
default:
elog(ERROR, "unrecognized boolop: %d",
(int) boolexpr->boolop);
break;
}
scratch.d.boolexpr.jumpdone = -1;
ExprEvalPushStep(state, &scratch);
adjust_jumps = lappend_int(adjust_jumps,
state->steps_len - 1);
off++;
}
/* adjust jump targets */
foreach(lc, adjust_jumps)
{
ExprEvalStep *as = &state->steps[lfirst_int(lc)];
Assert(as->d.boolexpr.jumpdone == -1);
as->d.boolexpr.jumpdone = state->steps_len;
}
break;
}
case T_SubPlan:
{
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
if (!state->parent)
elog(ERROR, "SubPlan found with no parent plan");
sstate = ExecInitSubPlan(subplan, state->parent);
/* add SubPlanState nodes to state->parent->subPlan */
state->parent->subPlan = lappend(state->parent->subPlan,
sstate);
scratch.opcode = EEOP_SUBPLAN;
scratch.d.subplan.sstate = sstate;
ExprEvalPushStep(state, &scratch);
break;
}
case T_AlternativeSubPlan:
{
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
if (!state->parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
asstate = ExecInitAlternativeSubPlan(asplan, state->parent);
scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN;
scratch.d.alternative_subplan.asstate = asstate;
ExprEvalPushStep(state, &scratch);
break;
}
case T_FieldSelect:
{
FieldSelect *fselect = (FieldSelect *) node;
/* evaluate row/record argument into result area */
ExecInitExprRec(fselect->arg, state, resv, resnull);
/* and extract field */
scratch.opcode = EEOP_FIELDSELECT;
scratch.d.fieldselect.fieldnum = fselect->fieldnum;
scratch.d.fieldselect.resulttype = fselect->resulttype;
scratch.d.fieldselect.argdesc = NULL;
ExprEvalPushStep(state, &scratch);
break;
}
case T_FieldStore:
{
FieldStore *fstore = (FieldStore *) node;
TupleDesc tupDesc;
TupleDesc *descp;
Datum *values;
bool *nulls;
int ncolumns;
ListCell *l1,
*l2;
/* find out the number of columns in the composite type */
tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
ncolumns = tupDesc->natts;
DecrTupleDescRefCount(tupDesc);
/* create workspace for column values */
values = (Datum *) palloc(sizeof(Datum) * ncolumns);
nulls = (bool *) palloc(sizeof(bool) * ncolumns);
/* create workspace for runtime tupdesc cache */
descp = (TupleDesc *) palloc(sizeof(TupleDesc));
*descp = NULL;
/* emit code to evaluate the composite input value */
ExecInitExprRec(fstore->arg, state, resv, resnull);
/* next, deform the input tuple into our workspace */
scratch.opcode = EEOP_FIELDSTORE_DEFORM;
scratch.d.fieldstore.fstore = fstore;
scratch.d.fieldstore.argdesc = descp;
scratch.d.fieldstore.values = values;
scratch.d.fieldstore.nulls = nulls;
scratch.d.fieldstore.ncolumns = ncolumns;
ExprEvalPushStep(state, &scratch);
/* evaluate new field values, store in workspace columns */
forboth(l1, fstore->newvals, l2, fstore->fieldnums)
{
Expr *e = (Expr *) lfirst(l1);
AttrNumber fieldnum = lfirst_int(l2);
Datum *save_innermost_caseval;
bool *save_innermost_casenull;
if (fieldnum <= 0 || fieldnum > ncolumns)
elog(ERROR, "field number %d is out of range in FieldStore",
fieldnum);
/*
* Use the CaseTestExpr mechanism to pass down the old
* value of the field being replaced; this is needed in
* case the newval is itself a FieldStore or ArrayRef that
* has to obtain and modify the old value. It's safe to
* reuse the CASE mechanism because there cannot be a CASE
* between here and where the value would be needed, and a
* field assignment can't be within a CASE either. (So
* saving and restoring innermost_caseval is just
* paranoia, but let's do it anyway.)
*
* Another non-obvious point is that it's safe to use the
* field's values[]/nulls[] entries as both the caseval
* source and the result address for this subexpression.
* That's okay only because (1) both FieldStore and
* ArrayRef evaluate their arg or refexpr inputs first,
* and (2) any such CaseTestExpr is directly the arg or
* refexpr input. So any read of the caseval will occur
* before there's a chance to overwrite it. Also, if
* multiple entries in the newvals/fieldnums lists target
* the same field, they'll effectively be applied
* left-to-right which is what we want.
*/
save_innermost_caseval = state->innermost_caseval;
save_innermost_casenull = state->innermost_casenull;
state->innermost_caseval = &values[fieldnum - 1];
state->innermost_casenull = &nulls[fieldnum - 1];
ExecInitExprRec(e, state,
&values[fieldnum - 1],
&nulls[fieldnum - 1]);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
}
/* finally, form result tuple */
scratch.opcode = EEOP_FIELDSTORE_FORM;
scratch.d.fieldstore.fstore = fstore;
scratch.d.fieldstore.argdesc = descp;
scratch.d.fieldstore.values = values;
scratch.d.fieldstore.nulls = nulls;
scratch.d.fieldstore.ncolumns = ncolumns;
ExprEvalPushStep(state, &scratch);
break;
}
case T_RelabelType:
{
/* relabel doesn't need to do anything at runtime */
RelabelType *relabel = (RelabelType *) node;
ExecInitExprRec(relabel->arg, state, resv, resnull);
break;
}
case T_CoerceViaIO:
{
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
Oid iofunc;
bool typisvarlena;
Oid typioparam;
FunctionCallInfo fcinfo_in;
/* evaluate argument into step's result area */
ExecInitExprRec(iocoerce->arg, state, resv, resnull);
/*
* Prepare both output and input function calls, to be
* evaluated inside a single evaluation step for speed - this
* can be a very common operation.
*
* We don't check permissions here as a type's input/output
* function are assumed to be executable by everyone.
*/
scratch.opcode = EEOP_IOCOERCE;
/* lookup the source type's output function */
scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
getTypeOutputInfo(exprType((Node *) iocoerce->arg),
&iofunc, &typisvarlena);
fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
scratch.d.iocoerce.finfo_out,
1, InvalidOid, NULL, NULL);
/* lookup the result type's input function */
scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
getTypeInputInfo(iocoerce->resulttype,
&iofunc, &typioparam);
fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
scratch.d.iocoerce.finfo_in,
3, InvalidOid, NULL, NULL);
/*
* We can preload the second and third arguments for the input
* function, since they're constants.
*/
fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
fcinfo_in->argnull[1] = false;
fcinfo_in->arg[2] = Int32GetDatum(-1);
fcinfo_in->argnull[2] = false;
ExprEvalPushStep(state, &scratch);
break;
}
case T_ArrayCoerceExpr:
{
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
Oid resultelemtype;
ExprState *elemstate;
/* evaluate argument into step's result area */
ExecInitExprRec(acoerce->arg, state, resv, resnull);
resultelemtype = get_element_type(acoerce->resulttype);
if (!OidIsValid(resultelemtype))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("target type is not an array")));
/*
* Construct a sub-expression for the per-element expression;
* but don't ready it until after we check it for triviality.
* We assume it hasn't any Var references, but does have a
* CaseTestExpr representing the source array element values.
*/
elemstate = makeNode(ExprState);
elemstate->expr = acoerce->elemexpr;
elemstate->parent = state->parent;
elemstate->ext_params = state->ext_params;
elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
ExecInitExprRec(acoerce->elemexpr, elemstate,
&elemstate->resvalue, &elemstate->resnull);
if (elemstate->steps_len == 1 &&
elemstate->steps[0].opcode == EEOP_CASE_TESTVAL)
{
/* Trivial, so we need no per-element work at runtime */
elemstate = NULL;
}
else
{
/* Not trivial, so append a DONE step */
scratch.opcode = EEOP_DONE;
ExprEvalPushStep(elemstate, &scratch);
/* and ready the subexpression */
ExecReadyExpr(elemstate);
}
scratch.opcode = EEOP_ARRAYCOERCE;
scratch.d.arraycoerce.elemexprstate = elemstate;
scratch.d.arraycoerce.resultelemtype = resultelemtype;
if (elemstate)
{
/* Set up workspace for array_map */
scratch.d.arraycoerce.amstate =
(ArrayMapState *) palloc0(sizeof(ArrayMapState));
}
else
{
/* Don't need workspace if there's no subexpression */
scratch.d.arraycoerce.amstate = NULL;
}
ExprEvalPushStep(state, &scratch);
break;
}
case T_ConvertRowtypeExpr:
{
ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
/* evaluate argument into step's result area */
ExecInitExprRec(convert->arg, state, resv, resnull);
/* and push conversion step */
scratch.opcode = EEOP_CONVERT_ROWTYPE;
scratch.d.convert_rowtype.convert = convert;
scratch.d.convert_rowtype.indesc = NULL;
scratch.d.convert_rowtype.outdesc = NULL;
scratch.d.convert_rowtype.map = NULL;
scratch.d.convert_rowtype.initialized = false;
ExprEvalPushStep(state, &scratch);
break;
}
/* note that CaseWhen expressions are handled within this block */
case T_CaseExpr:
{
CaseExpr *caseExpr = (CaseExpr *) node;
List *adjust_jumps = NIL;
Datum *caseval = NULL;
bool *casenull = NULL;
ListCell *lc;
/*
* If there's a test expression, we have to evaluate it and
* save the value where the CaseTestExpr placeholders can find
* it.
*/
if (caseExpr->arg != NULL)
{
/* Evaluate testexpr into caseval/casenull workspace */
caseval = palloc(sizeof(Datum));
casenull = palloc(sizeof(bool));
ExecInitExprRec(caseExpr->arg, state,
caseval, casenull);
/*
* Since value might be read multiple times, force to R/O
* - but only if it could be an expanded datum.
*/
if (get_typlen(exprType((Node *) caseExpr->arg)) == -1)
{
/* change caseval in-place */
scratch.opcode = EEOP_MAKE_READONLY;
scratch.resvalue = caseval;
scratch.resnull = casenull;
scratch.d.make_readonly.value = caseval;
scratch.d.make_readonly.isnull = casenull;
ExprEvalPushStep(state, &scratch);
/* restore normal settings of scratch fields */
scratch.resvalue = resv;
scratch.resnull = resnull;
}
}
/*
* Prepare to evaluate each of the WHEN clauses in turn; as
* soon as one is true we return the value of the
* corresponding THEN clause. If none are true then we return
* the value of the ELSE clause, or NULL if there is none.
*/
foreach(lc, caseExpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(lc);
Datum *save_innermost_caseval;
bool *save_innermost_casenull;
int whenstep;
/*
* Make testexpr result available to CaseTestExpr nodes
* within the condition. We must save and restore prior
* setting of innermost_caseval fields, in case this node
* is itself within a larger CASE.
*
* If there's no test expression, we don't actually need
* to save and restore these fields; but it's less code to
* just do so unconditionally.
*/
save_innermost_caseval = state->innermost_caseval;
save_innermost_casenull = state->innermost_casenull;
state->innermost_caseval = caseval;
state->innermost_casenull = casenull;
/* evaluate condition into CASE's result variables */
ExecInitExprRec(when->expr, state, resv, resnull);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
/* If WHEN result isn't true, jump to next CASE arm */
scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
scratch.d.jump.jumpdone = -1; /* computed later */
ExprEvalPushStep(state, &scratch);
whenstep = state->steps_len - 1;
/*
* If WHEN result is true, evaluate THEN result, storing
* it into the CASE's result variables.
*/
ExecInitExprRec(when->result, state, resv, resnull);
/* Emit JUMP step to jump to end of CASE's code */
scratch.opcode = EEOP_JUMP;
scratch.d.jump.jumpdone = -1; /* computed later */
ExprEvalPushStep(state, &scratch);
/*
* Don't know address for that jump yet, compute once the
* whole CASE expression is built.
*/
adjust_jumps = lappend_int(adjust_jumps,
state->steps_len - 1);
/*
* But we can set WHEN test's jump target now, to make it
* jump to the next WHEN subexpression or the ELSE.
*/
state->steps[whenstep].d.jump.jumpdone = state->steps_len;
}
/* transformCaseExpr always adds a default */
Assert(caseExpr->defresult);
/* evaluate ELSE expr into CASE's result variables */
ExecInitExprRec(caseExpr->defresult, state,
resv, resnull);
/* adjust jump targets */
foreach(lc, adjust_jumps)
{
ExprEvalStep *as = &state->steps[lfirst_int(lc)];
Assert(as->opcode == EEOP_JUMP);
Assert(as->d.jump.jumpdone == -1);
as->d.jump.jumpdone = state->steps_len;
}
break;
}
case T_CaseTestExpr:
{
/*
* Read from location identified by innermost_caseval. Note
* that innermost_caseval could be NULL, if this node isn't
* actually within a CaseExpr, ArrayCoerceExpr, etc structure.
* That can happen because some parts of the system abuse
* CaseTestExpr to cause a read of a value externally supplied
* in econtext->caseValue_datum. We'll take care of that
* scenario at runtime.
*/
scratch.opcode = EEOP_CASE_TESTVAL;
scratch.d.casetest.value = state->innermost_caseval;
scratch.d.casetest.isnull = state->innermost_casenull;
ExprEvalPushStep(state, &scratch);
break;
}
case T_ArrayExpr:
{
ArrayExpr *arrayexpr = (ArrayExpr *) node;
int nelems = list_length(arrayexpr->elements);
ListCell *lc;
int elemoff;
/*
* Evaluate by computing each element, and then forming the
* array. Elements are computed into scratch arrays
* associated with the ARRAYEXPR step.
*/
scratch.opcode = EEOP_ARRAYEXPR;
scratch.d.arrayexpr.elemvalues =
(Datum *) palloc(sizeof(Datum) * nelems);
scratch.d.arrayexpr.elemnulls =
(bool *) palloc(sizeof(bool) * nelems);
scratch.d.arrayexpr.nelems = nelems;
/* fill remaining fields of step */
scratch.d.arrayexpr.multidims = arrayexpr->multidims;
scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid;
/* do one-time catalog lookup for type info */
get_typlenbyvalalign(arrayexpr->element_typeid,
&scratch.d.arrayexpr.elemlength,
&scratch.d.arrayexpr.elembyval,
&scratch.d.arrayexpr.elemalign);
/* prepare to evaluate all arguments */
elemoff = 0;
foreach(lc, arrayexpr->elements)
{
Expr *e = (Expr *) lfirst(lc);
ExecInitExprRec(e, state,
&scratch.d.arrayexpr.elemvalues[elemoff],
&scratch.d.arrayexpr.elemnulls[elemoff]);
elemoff++;
}
/* and then collect all into an array */
ExprEvalPushStep(state, &scratch);
break;
}
case T_RowExpr:
{
RowExpr *rowexpr = (RowExpr *) node;
int nelems = list_length(rowexpr->args);
TupleDesc tupdesc;
int i;
ListCell *l;
/* Build tupdesc to describe result tuples */
if (rowexpr->row_typeid == RECORDOID)
{
/* generic record, use types of given expressions */
tupdesc = ExecTypeFromExprList(rowexpr->args);
}
else
{
/* it's been cast to a named type, use that */
tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
}
/* In either case, adopt RowExpr's column aliases */
ExecTypeSetColNames(tupdesc, rowexpr->colnames);
/* Bless the tupdesc in case it's now of type RECORD */
BlessTupleDesc(tupdesc);
/*
* In the named-type case, the tupdesc could have more columns
* than are in the args list, since the type might have had
* columns added since the ROW() was parsed. We want those
* extra columns to go to nulls, so we make sure that the
* workspace arrays are large enough and then initialize any
* extra columns to read as NULLs.
*/
Assert(nelems <= tupdesc->natts);
nelems = Max(nelems, tupdesc->natts);
/*
* Evaluate by first building datums for each field, and then
* a final step forming the composite datum.
*/
scratch.opcode = EEOP_ROW;
scratch.d.row.tupdesc = tupdesc;
/* space for the individual field datums */
scratch.d.row.elemvalues =
(Datum *) palloc(sizeof(Datum) * nelems);
scratch.d.row.elemnulls =
(bool *) palloc(sizeof(bool) * nelems);
/* as explained above, make sure any extra columns are null */
memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems);
/* Set up evaluation, skipping any deleted columns */
i = 0;
foreach(l, rowexpr->args)
{
Form_pg_attribute att = TupleDescAttr(tupdesc, i);
Expr *e = (Expr *) lfirst(l);
if (!att->attisdropped)
{
/*
* Guard against ALTER COLUMN TYPE on rowtype since
* the RowExpr was created. XXX should we check
* typmod too? Not sure we can be sure it'll be the
* same.
*/
if (exprType((Node *) e) != att->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("ROW() column has type %s instead of type %s",
format_type_be(exprType((Node *) e)),
format_type_be(att->atttypid))));
}
else
{
/*
* Ignore original expression and insert a NULL. We
* don't really care what type of NULL it is, so
* always make an int4 NULL.
*/
e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
}
/* Evaluate column expr into appropriate workspace slot */
ExecInitExprRec(e, state,
&scratch.d.row.elemvalues[i],
&scratch.d.row.elemnulls[i]);
i++;
}
/* And finally build the row value */
ExprEvalPushStep(state, &scratch);
break;
}
case T_RowCompareExpr:
{
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
int nopers = list_length(rcexpr->opnos);
List *adjust_jumps = NIL;
ListCell *l_left_expr,
*l_right_expr,
*l_opno,
*l_opfamily,
*l_inputcollid;
ListCell *lc;
int off;
/*
* Iterate over each field, prepare comparisons. To handle
* NULL results, prepare jumps to after the expression. If a
* comparison yields a != 0 result, jump to the final step.
*/
Assert(list_length(rcexpr->largs) == nopers);
Assert(list_length(rcexpr->rargs) == nopers);
Assert(list_length(rcexpr->opfamilies) == nopers);
Assert(list_length(rcexpr->inputcollids) == nopers);
off = 0;
for (off = 0,
l_left_expr = list_head(rcexpr->largs),
l_right_expr = list_head(rcexpr->rargs),
l_opno = list_head(rcexpr->opnos),
l_opfamily = list_head(rcexpr->opfamilies),
l_inputcollid = list_head(rcexpr->inputcollids);
off < nopers;
off++,
l_left_expr = lnext(l_left_expr),
l_right_expr = lnext(l_right_expr),
l_opno = lnext(l_opno),
l_opfamily = lnext(l_opfamily),
l_inputcollid = lnext(l_inputcollid))
{
Expr *left_expr = (Expr *) lfirst(l_left_expr);
Expr *right_expr = (Expr *) lfirst(l_right_expr);
Oid opno = lfirst_oid(l_opno);
Oid opfamily = lfirst_oid(l_opfamily);
Oid inputcollid = lfirst_oid(l_inputcollid);
int strategy;
Oid lefttype;
Oid righttype;
Oid proc;
FmgrInfo *finfo;
FunctionCallInfo fcinfo;
get_op_opfamily_properties(opno, opfamily, false,
&strategy,
&lefttype,
&righttype);
proc = get_opfamily_proc(opfamily,
lefttype,
righttype,
BTORDER_PROC);
if (!OidIsValid(proc))
elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
BTORDER_PROC, lefttype, righttype, opfamily);
/* Set up the primary fmgr lookup information */
finfo = palloc0(sizeof(FmgrInfo));
fcinfo = palloc0(sizeof(FunctionCallInfoData));
fmgr_info(proc, finfo);
fmgr_info_set_expr((Node *) node, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
inputcollid, NULL, NULL);
/*
* If we enforced permissions checks on index support
* functions, we'd need to make a check here. But the
* index support machinery doesn't do that, and thus
* neither does this code.
*/
/* evaluate left and right args directly into fcinfo */
ExecInitExprRec(left_expr, state,
&fcinfo->arg[0], &fcinfo->argnull[0]);
ExecInitExprRec(right_expr, state,
&fcinfo->arg[1], &fcinfo->argnull[1]);
scratch.opcode = EEOP_ROWCOMPARE_STEP;
scratch.d.rowcompare_step.finfo = finfo;
scratch.d.rowcompare_step.fcinfo_data = fcinfo;
scratch.d.rowcompare_step.fn_addr = finfo->fn_addr;
/* jump targets filled below */
scratch.d.rowcompare_step.jumpnull = -1;
scratch.d.rowcompare_step.jumpdone = -1;
ExprEvalPushStep(state, &scratch);
adjust_jumps = lappend_int(adjust_jumps,
state->steps_len - 1);
}
/*
* We could have a zero-column rowtype, in which case the rows
* necessarily compare equal.
*/
if (nopers == 0)
{
scratch.opcode = EEOP_CONST;
scratch.d.constval.value = Int32GetDatum(0);
scratch.d.constval.isnull = false;
ExprEvalPushStep(state, &scratch);
}
/* Finally, examine the last comparison result */
scratch.opcode = EEOP_ROWCOMPARE_FINAL;
scratch.d.rowcompare_final.rctype = rcexpr->rctype;
ExprEvalPushStep(state, &scratch);
/* adjust jump targetss */
foreach(lc, adjust_jumps)
{
ExprEvalStep *as = &state->steps[lfirst_int(lc)];
Assert(as->opcode == EEOP_ROWCOMPARE_STEP);
Assert(as->d.rowcompare_step.jumpdone == -1);
Assert(as->d.rowcompare_step.jumpnull == -1);
/* jump to comparison evaluation */
as->d.rowcompare_step.jumpdone = state->steps_len - 1;
/* jump to the following expression */
as->d.rowcompare_step.jumpnull = state->steps_len;
}
break;
}
case T_CoalesceExpr:
{
CoalesceExpr *coalesce = (CoalesceExpr *) node;
List *adjust_jumps = NIL;
ListCell *lc;
/* We assume there's at least one arg */
Assert(coalesce->args != NIL);
/*
* Prepare evaluation of all coalesced arguments, after each
* one push a step that short-circuits if not null.
*/
foreach(lc, coalesce->args)
{
Expr *e = (Expr *) lfirst(lc);
/* evaluate argument, directly into result datum */
ExecInitExprRec(e, state, resv, resnull);
/* if it's not null, skip to end of COALESCE expr */
scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
scratch.d.jump.jumpdone = -1; /* adjust later */
ExprEvalPushStep(state, &scratch);
adjust_jumps = lappend_int(adjust_jumps,
state->steps_len - 1);
}
/*
* No need to add a constant NULL return - we only can get to
* the end of the expression if a NULL already is being
* returned.
*/
/* adjust jump targets */
foreach(lc, adjust_jumps)
{
ExprEvalStep *as = &state->steps[lfirst_int(lc)];
Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
Assert(as->d.jump.jumpdone == -1);
as->d.jump.jumpdone = state->steps_len;
}
break;
}
case T_MinMaxExpr:
{
MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
int nelems = list_length(minmaxexpr->args);
TypeCacheEntry *typentry;
FmgrInfo *finfo;
FunctionCallInfo fcinfo;
ListCell *lc;
int off;
/* Look up the btree comparison function for the datatype */
typentry = lookup_type_cache(minmaxexpr->minmaxtype,
TYPECACHE_CMP_PROC);
if (!OidIsValid(typentry->cmp_proc))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify a comparison function for type %s",
format_type_be(minmaxexpr->minmaxtype))));
/*
* If we enforced permissions checks on index support
* functions, we'd need to make a check here. But the index
* support machinery doesn't do that, and thus neither does
* this code.
*/
/* Perform function lookup */
finfo = palloc0(sizeof(FmgrInfo));
fcinfo = palloc0(sizeof(FunctionCallInfoData));
fmgr_info(typentry->cmp_proc, finfo);
fmgr_info_set_expr((Node *) node, finfo);
InitFunctionCallInfoData(*fcinfo, finfo, 2,
minmaxexpr->inputcollid, NULL, NULL);
scratch.opcode = EEOP_MINMAX;
/* allocate space to store arguments */
scratch.d.minmax.values =
(Datum *) palloc(sizeof(Datum) * nelems);
scratch.d.minmax.nulls =
(bool *) palloc(sizeof(bool) * nelems);
scratch.d.minmax.nelems = nelems;
scratch.d.minmax.op = minmaxexpr->op;
scratch.d.minmax.finfo = finfo;
scratch.d.minmax.fcinfo_data = fcinfo;
/* evaluate expressions into minmax->values/nulls */
off = 0;
foreach(lc, minmaxexpr->args)
{
Expr *e = (Expr *) lfirst(lc);
ExecInitExprRec(e, state,
&scratch.d.minmax.values[off],
&scratch.d.minmax.nulls[off]);
off++;
}
/* and push the final comparison */
ExprEvalPushStep(state, &scratch);
break;
}
case T_SQLValueFunction:
{
SQLValueFunction *svf = (SQLValueFunction *) node;
scratch.opcode = EEOP_SQLVALUEFUNCTION;
scratch.d.sqlvaluefunction.svf = svf;
ExprEvalPushStep(state, &scratch);
break;
}
case T_XmlExpr:
{
XmlExpr *xexpr = (XmlExpr *) node;
int nnamed = list_length(xexpr->named_args);
int nargs = list_length(xexpr->args);
int off;
ListCell *arg;
scratch.opcode = EEOP_XMLEXPR;
scratch.d.xmlexpr.xexpr = xexpr;
/* allocate space for storing all the arguments */
if (nnamed)
{
scratch.d.xmlexpr.named_argvalue =
(Datum *) palloc(sizeof(Datum) * nnamed);
scratch.d.xmlexpr.named_argnull =
(bool *) palloc(sizeof(bool) * nnamed);
}
else
{
scratch.d.xmlexpr.named_argvalue = NULL;
scratch.d.xmlexpr.named_argnull = NULL;
}
if (nargs)
{
scratch.d.xmlexpr.argvalue =
(Datum *) palloc(sizeof(Datum) * nargs);
scratch.d.xmlexpr.argnull =
(bool *) palloc(sizeof(bool) * nargs);
}
else
{
scratch.d.xmlexpr.argvalue = NULL;
scratch.d.xmlexpr.argnull = NULL;
}
/* prepare argument execution */
off = 0;
foreach(arg, xexpr->named_args)
{
Expr *e = (Expr *) lfirst(arg);
ExecInitExprRec(e, state,
&scratch.d.xmlexpr.named_argvalue[off],
&scratch.d.xmlexpr.named_argnull[off]);
off++;
}
off = 0;
foreach(arg, xexpr->args)
{
Expr *e = (Expr *) lfirst(arg);
ExecInitExprRec(e, state,
&scratch.d.xmlexpr.argvalue[off],
&scratch.d.xmlexpr.argnull[off]);
off++;
}
/* and evaluate the actual XML expression */
ExprEvalPushStep(state, &scratch);
break;
}
case T_NullTest:
{
NullTest *ntest = (NullTest *) node;
if (ntest->nulltesttype == IS_NULL)
{
if (ntest->argisrow)
scratch.opcode = EEOP_NULLTEST_ROWISNULL;
else
scratch.opcode = EEOP_NULLTEST_ISNULL;
}
else if (ntest->nulltesttype == IS_NOT_NULL)
{
if (ntest->argisrow)
scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL;
else
scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
}
else
{
elog(ERROR, "unrecognized nulltesttype: %d",
(int) ntest->nulltesttype);
}
/* initialize cache in case it's a row test */
scratch.d.nulltest_row.argdesc = NULL;
/* first evaluate argument into result variable */
ExecInitExprRec(ntest->arg, state,
resv, resnull);
/* then push the test of that argument */
ExprEvalPushStep(state, &scratch);
break;
}
case T_BooleanTest:
{
BooleanTest *btest = (BooleanTest *) node;
/*
* Evaluate argument, directly into result datum. That's ok,
* because resv/resnull is definitely not used anywhere else,
* and will get overwritten by the below EEOP_BOOLTEST_IS_*
* step.
*/
ExecInitExprRec(btest->arg, state, resv, resnull);
switch (btest->booltesttype)
{
case IS_TRUE:
scratch.opcode = EEOP_BOOLTEST_IS_TRUE;
break;
case IS_NOT_TRUE:
scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE;
break;
case IS_FALSE:
scratch.opcode = EEOP_BOOLTEST_IS_FALSE;
break;
case IS_NOT_FALSE:
scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE;
break;
case IS_UNKNOWN:
/* Same as scalar IS NULL test */
scratch.opcode = EEOP_NULLTEST_ISNULL;
break;
case IS_NOT_UNKNOWN:
/* Same as scalar IS NOT NULL test */
scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
break;
default:
elog(ERROR, "unrecognized booltesttype: %d",
(int) btest->booltesttype);
}
ExprEvalPushStep(state, &scratch);
break;
}
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
ExecInitCoerceToDomain(&scratch, ctest, state,
resv, resnull);
break;
}
case T_CoerceToDomainValue:
{
/*
* Read from location identified by innermost_domainval. Note
* that innermost_domainval could be NULL, if we're compiling
* a standalone domain check rather than one embedded in a
* larger expression. In that case we must read from
* econtext->domainValue_datum. We'll take care of that
* scenario at runtime.
*/
scratch.opcode = EEOP_DOMAIN_TESTVAL;
/* we share instruction union variant with case testval */
scratch.d.casetest.value = state->innermost_domainval;
scratch.d.casetest.isnull = state->innermost_domainnull;
ExprEvalPushStep(state, &scratch);
break;
}
case T_CurrentOfExpr:
{
scratch.opcode = EEOP_CURRENTOFEXPR;
ExprEvalPushStep(state, &scratch);
break;
}
case T_NextValueExpr:
{
NextValueExpr *nve = (NextValueExpr *) node;
scratch.opcode = EEOP_NEXTVALUEEXPR;
scratch.d.nextvalueexpr.seqid = nve->seqid;
scratch.d.nextvalueexpr.seqtypid = nve->typeId;
ExprEvalPushStep(state, &scratch);
break;
}
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(node));
break;
}
}
/*
* Add another expression evaluation step to ExprState->steps.
* 添加表达式解析步骤到ExprState->steps数组中.
*
* Note that this potentially re-allocates es->steps, therefore no pointer
* into that array may be used while the expression is still being built.
*/
void
ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
{
if (es->steps_alloc == 0)
{
es->steps_alloc = 16;
es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
}
else if (es->steps_alloc == es->steps_len)
{
es->steps_alloc *= 2;
es->steps = repalloc(es->steps,
sizeof(ExprEvalStep) * es->steps_alloc);
}
memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
}
测试脚本
testdb=# select 1+id,c2 from t_expr where id < 3;
进入ExecInitExprRec,Node节点为OpExpr,执行ExprEvalPushStep压入步骤中
(gdb) step
ExecInitExprRec (node=0x1c9a930, state=0x1c8f7d8, resv=0x1c8f7e0, resnull=0x1c8f7dd) at execExpr.c:645
645 ExprEvalStep scratch = {0};
(gdb) n
648 check_stack_depth();
(gdb)
651 Assert(resv != NULL && resnull != NULL);
(gdb)
652 scratch.resvalue = resv;
(gdb)
653 scratch.resnull = resnull;
(gdb)
656 switch (nodeTag(node))
(gdb)
891 OpExpr *op = (OpExpr *) node;
(gdb) p *node
$16 = {type = T_OpExpr}
(gdb) n
893 ExecInitFunc(&scratch, node,
(gdb)
896 ExprEvalPushStep(state, &scratch);
(gdb)
897 break;
(gdb)
2122 }
(gdb)
到此,相信大家对“怎么使用PostgreSQL中ExecInitExprRec函数”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:http://blog.itpub.net/6906/viewspace-2640221/