本篇内容主要讲解“怎么实现PostgreSQL中的类型转换”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么实现PostgreSQL中的类型转换”吧!
解析表达式,涉及不同数据类型时:
1.如有相应类型的Operator定义(pg_operator),则尝试进行类型转换,否则报错;
2.如有相应类型的转换规则,转换为目标类型后解析,否则报错.
Form_pg_operator
pg_operator中的定义,代码会其中的定义转换为FormData_pg_operator结构体
/* ---------------- * pg_operator definition. cpp turns this into * typedef struct FormData_pg_operator * ---------------- */ CATALOG(pg_operator,2617,OperatorRelationId) { Oid oid; /* oid */ /* name of operator */ NameData oprname; /* OID of namespace containing this oper */ Oid oprnamespace BKI_DEFAULT(PGNSP); /* operator owner */ Oid oprowner BKI_DEFAULT(PGUID); /* 'l', 'r', or 'b' */ char oprkind BKI_DEFAULT(b); /* can be used in merge join? */ bool oprcanmerge BKI_DEFAULT(f); /* can be used in hash join? */ bool oprcanhash BKI_DEFAULT(f); /* left arg type, or 0 if 'l' oprkind */ Oid oprleft BKI_LOOKUP(pg_type); /* right arg type, or 0 if 'r' oprkind */ Oid oprright BKI_LOOKUP(pg_type); /* result datatype */ Oid oprresult BKI_LOOKUP(pg_type); /* OID of commutator oper, or 0 if none */ Oid oprcom BKI_DEFAULT(0) BKI_LOOKUP(pg_operator); /* OID of negator oper, or 0 if none */ Oid oprnegate BKI_DEFAULT(0) BKI_LOOKUP(pg_operator); /* OID of underlying function */ regproc oprcode BKI_LOOKUP(pg_proc); /* OID of restriction estimator, or 0 */ regproc oprrest BKI_DEFAULT(-) BKI_LOOKUP(pg_proc); /* OID of join estimator, or 0 */ regproc oprjoin BKI_DEFAULT(-) BKI_LOOKUP(pg_proc); } FormData_pg_operator; /* ---------------- * Form_pg_operator corresponds to a pointer to a tuple with * the format of pg_operator relation. * ---------------- */ typedef FormData_pg_operator *Form_pg_operator;
coerce_type函数实现具体的类型转换.
/* * coerce_type() * Convert an expression to a different type. * 类型转换 * * The caller should already have determined that the coercion is possible; * see can_coerce_type. * 调用者应确定转换是OK的. * * Normally, no coercion to a typmod (length) is performed here. The caller * must call coerce_type_typmod as well, if a typmod constraint is wanted. * (But if the target type is a domain, it may internally contain a * typmod constraint, which will be applied inside coerce_to_domain.) * In some cases pg_cast specifies a type coercion function that also * applies length conversion, and in those cases only, the result will * already be properly coerced to the specified typmod. * 通常来说,在这里不会执行转换为typmod(length)的操作. * * pstate is only used in the case that we are able to resolve the type of * a previously UNKNOWN Param. It is okay to pass pstate = NULL if the * caller does not want type information updated for Params. * * Note: this function must not modify the given expression tree, only add * decoration on top of it. See transformSetOperationTree, for example. */ Node * coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location) { Node *result;//结果Node CoercionPathType pathtype; Oid funcId; if (targetTypeId == inputTypeId || node == NULL) { /* no conversion needed */ //不需要转换 return node; } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || targetTypeId == ANYNONARRAYOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. * * Note: by returning the unmodified node here, we are saying that * it's OK to treat an UNKNOWN constant as a valid input for a * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be * all right, since an UNKNOWN value is still a perfectly valid Datum. * * NB: we do NOT want a RelabelType here: the exposed type of the * function argument must be its actual type, not the polymorphic * pseudotype. */ //目标类型可以为任意类型(ANYXXX) return node; } if (targetTypeId == ANYARRAYOID || targetTypeId == ANYENUMOID || targetTypeId == ANYRANGEOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. * * These cases are unlike the ones above because the exposed type of * the argument must be an actual array, enum, or range type. In * particular the argument must *not* be an UNKNOWN constant. If it * is, we just fall through; below, we'll call anyarray_in, * anyenum_in, or anyrange_in, which will produce an error. Also, if * what we have is a domain over array, enum, or range, we have to * relabel it to its base type. * * Note: currently, we can't actually see a domain-over-enum here, * since the other functions in this file will not match such a * parameter to ANYENUM. But that should get changed eventually. */ if (inputTypeId != UNKNOWNOID) { //获取基本类型 Oid baseTypeId = getBaseType(inputTypeId); if (baseTypeId != inputTypeId) { RelabelType *r = makeRelabelType((Expr *) node, baseTypeId, -1, InvalidOid, cformat); r->location = location; return (Node *) r; } /* Not a domain type, so return it as-is */ return node; } } if (inputTypeId == UNKNOWNOID && IsA(node, Const)) { //---------------- 输入类型为unknown并且是常量 /* * Input is a string constant with previously undetermined type. Apply * the target type's typinput function to it to produce a constant of * the target type. * 应用目标类型的typinput函数,生成目标类型常量 * * NOTE: this case cannot be folded together with the other * constant-input case, since the typinput function does not * necessarily behave the same as a type conversion function. For * example, int4's typinput function will reject "1.2", whereas * float-to-int type conversion will round to integer. * * XXX if the typinput function is not immutable, we really ought to * postpone evaluation of the function call until runtime. But there * is no way to represent a typinput function call as an expression * tree, because C-string values are not Datums. (XXX This *is* * possible as of 7.3, do we want to do it?) */ Const *con = (Const *) node;//常量 Const *newcon = makeNode(Const);//转换后逇常量 Oid baseTypeId;//基本类型Oid int32 baseTypeMod;//基本typmode int32 inputTypeMod;//输入typmode Type baseType;//基本类型 ParseCallbackState pcbstate;//解析回调函数 /* * If the target type is a domain, we want to call its base type's * input routine, not domain_in(). This is to avoid premature failure * when the domain applies a typmod: existing input routines follow * implicit-coercion semantics for length checks, which is not always * what we want here. The needed check will be applied properly * inside coerce_to_domain(). */ baseTypeMod = targetTypeMod; baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); /* * For most types we pass typmod -1 to the input routine, because * existing input routines follow implicit-coercion semantics for * length checks, which is not always what we want here. Any length * constraint will be applied later by our caller. An exception * however is the INTERVAL type, for which we *must* pass the typmod * or it won't be able to obey the bizarre SQL-spec input rules. (Ugly * as sin, but so is this part of the spec...) */ if (baseTypeId == INTERVALOID) inputTypeMod = baseTypeMod; else inputTypeMod = -1; baseType = typeidType(baseTypeId); //构造输出常量Const newcon->consttype = baseTypeId; newcon->consttypmod = inputTypeMod; newcon->constcollid = typeTypeCollation(baseType); newcon->constlen = typeLen(baseType); newcon->constbyval = typeByVal(baseType); newcon->constisnull = con->constisnull; /* * We use the original literal's location regardless of the position * of the coercion. This is a change from pre-9.2 behavior, meant to * simplify life for pg_stat_statements. */ //使用原始位置 newcon->location = con->location; /* * Set up to point at the constant's text if the input routine throws * an error. */ //如果报错,则指向该常量文本 setup_parser_errposition_callback(&pcbstate, pstate, con->location); /* * We assume here that UNKNOWN's internal representation is the same * as CSTRING. * 内部表示跟CSTRING一样 */ if (!con->constisnull) newcon->constvalue = stringTypeDatum(baseType, DatumGetCString(con->constvalue), inputTypeMod); else newcon->constvalue = stringTypeDatum(baseType, NULL, inputTypeMod); /* * If it's a varlena value, force it to be in non-expanded * (non-toasted) format; this avoids any possible dependency on * external values and improves consistency of representation. */ //如为可变长度值,则强制其为非扩展格式. if (!con->constisnull && newcon->constlen == -1) newcon->constvalue = PointerGetDatum(PG_DETOAST_DATUM(newcon->constvalue)); #ifdef RANDOMIZE_ALLOCATED_MEMORY /* * For pass-by-reference data types, repeat the conversion to see if * the input function leaves any uninitialized bytes in the result. We * can only detect that reliably if RANDOMIZE_ALLOCATED_MEMORY is * enabled, so we don't bother testing otherwise. The reason we don't * want any instability in the input function is that comparison of * Const nodes relies on bytewise comparison of the datums, so if the * input function leaves garbage then subexpressions that should be * identical may not get recognized as such. See pgsql-hackers * discussion of 2008-04-04. */ if (!con->constisnull && !newcon->constbyval) { Datum val2; val2 = stringTypeDatum(baseType, DatumGetCString(con->constvalue), inputTypeMod); if (newcon->constlen == -1) val2 = PointerGetDatum(PG_DETOAST_DATUM(val2)); if (!datumIsEqual(newcon->constvalue, val2, false, newcon->constlen)) elog(WARNING, "type %s has unstable input conversion for \"%s\"", typeTypeName(baseType), DatumGetCString(con->constvalue)); } #endif cancel_parser_errposition_callback(&pcbstate); //结果Node result = (Node *) newcon; /* If target is a domain, apply constraints. */ if (baseTypeId != targetTypeId) result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, ccontext, cformat, location, false); ReleaseSysCache(baseType); //返回 return result; } if (IsA(node, Param) && pstate != NULL && pstate->p_coerce_param_hook != NULL) { /* * Allow the CoerceParamHook to decide what happens. It can return a * transformed node (very possibly the same Param node), or return * NULL to indicate we should proceed with normal coercion. */ result = pstate->p_coerce_param_hook(pstate, (Param *) node, targetTypeId, targetTypeMod, location); if (result) return result; } if (IsA(node, CollateExpr)) { /* * If we have a COLLATE clause, we have to push the coercion * underneath the COLLATE. This is really ugly, but there is little * choice because the above hacks on Consts and Params wouldn't happen * otherwise. This kluge has consequences in coerce_to_target_type. */ CollateExpr *coll = (CollateExpr *) node; CollateExpr *newcoll = makeNode(CollateExpr); newcoll->arg = (Expr *) coerce_type(pstate, (Node *) coll->arg, inputTypeId, targetTypeId, targetTypeMod, ccontext, cformat, location); newcoll->collOid = coll->collOid; newcoll->location = coll->location; return (Node *) newcoll; } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) { if (pathtype != COERCION_PATH_RELABELTYPE) { /* * Generate an expression tree representing run-time application * of the conversion function. If we are dealing with a domain * target type, the conversion function will yield the base type, * and we need to extract the correct typmod to use from the * domain's typtypmod. */ Oid baseTypeId; int32 baseTypeMod; baseTypeMod = targetTypeMod; baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); result = build_coercion_expression(node, pathtype, funcId, baseTypeId, baseTypeMod, ccontext, cformat, location); /* * If domain, coerce to the domain type and relabel with domain * type ID, hiding the previous coercion node. */ if (targetTypeId != baseTypeId) result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, ccontext, cformat, location, true); } else { /* * We don't need to do a physical conversion, but we do need to * attach a RelabelType node so that the expression will be seen * to have the intended type when inspected by higher-level code. * * Also, domains may have value restrictions beyond the base type * that must be accounted for. If the destination is a domain * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, -1, targetTypeId, ccontext, cformat, location, false); if (result == node) { /* * XXX could we label result with exprTypmod(node) instead of * default -1 typmod, to save a possible length-coercion * later? Would work if both types have same interpretation of * typmod, which is likely but not certain. */ RelabelType *r = makeRelabelType((Expr *) result, targetTypeId, -1, InvalidOid, cformat); r->location = location; result = (Node *) r; } } return result; } if (inputTypeId == RECORDOID && ISCOMPLEX(targetTypeId)) { /* Coerce a RECORD to a specific complex type */ return coerce_record_to_complex(pstate, node, targetTypeId, ccontext, cformat, location); } if (targetTypeId == RECORDOID && ISCOMPLEX(inputTypeId)) { /* Coerce a specific complex type to RECORD */ /* NB: we do NOT want a RelabelType here */ return node; } #ifdef NOT_USED if (inputTypeId == RECORDARRAYOID && is_complex_array(targetTypeId)) { /* Coerce record[] to a specific complex array type */ /* not implemented yet ... */ } #endif if (targetTypeId == RECORDARRAYOID && is_complex_array(inputTypeId)) { /* Coerce a specific complex array type to record[] */ /* NB: we do NOT want a RelabelType here */ return node; } if (typeInheritsFrom(inputTypeId, targetTypeId) || typeIsOfTypedTable(inputTypeId, targetTypeId)) { /* * Input class type is a subclass of target, so generate an * appropriate runtime conversion (removing unneeded columns and * possibly rearranging the ones that are wanted). * * We will also get here when the input is a domain over a subclass of * the target type. To keep life simple for the executor, we define * ConvertRowtypeExpr as only working between regular composite types; * therefore, in such cases insert a RelabelType to smash the input * expression down to its base type. */ Oid baseTypeId = getBaseType(inputTypeId); ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr); if (baseTypeId != inputTypeId) { RelabelType *rt = makeRelabelType((Expr *) node, baseTypeId, -1, InvalidOid, COERCE_IMPLICIT_CAST); rt->location = location; node = (Node *) rt; } r->arg = (Expr *) node; r->resulttype = targetTypeId; r->convertformat = cformat; r->location = location; return (Node *) r; } /* If we get here, caller blew it */ elog(ERROR, "failed to find conversion function from %s to %s", format_type_be(inputTypeId), format_type_be(targetTypeId)); return NULL; /* keep compiler quiet */ } /* * Given a type structure and a string, returns the internal representation * of that string. The "string" can be NULL to perform conversion of a NULL * (which might result in failure, if the input function rejects NULLs). */ Datum stringTypeDatum(Type tp, char *string, int32 atttypmod) { Form_pg_type typform = (Form_pg_type) GETSTRUCT(tp); Oid typinput = typform->typinput; Oid typioparam = getTypeIOParam(tp); //调用函数进行转换 return OidInputFunctionCall(typinput, string, typioparam, atttypmod); } /* * As above, for I/O functions identified by OID. These are only to be used * in seldom-executed code paths. They are not only slow but leak memory. */ Datum OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod) { FmgrInfo flinfo; //构造函数调用参数 fmgr_info(functionId, &flinfo); return InputFunctionCall(&flinfo, str, typioparam, typmod); } /* * Call a previously-looked-up datatype input function. * * "str" may be NULL to indicate we are reading a NULL. In this case * the caller should assume the result is NULL, but we'll call the input * function anyway if it's not strict. So this is almost but not quite * the same as FunctionCall3. */ Datum InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod) { LOCAL_FCINFO(fcinfo, 3); Datum result; if (str == NULL && flinfo->fn_strict) return (Datum) 0; /* just return null result */ InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL); fcinfo->args[0].value = CStringGetDatum(str); fcinfo->args[0].isnull = false; fcinfo->args[1].value = ObjectIdGetDatum(typioparam); fcinfo->args[1].isnull = false; fcinfo->args[2].value = Int32GetDatum(typmod); fcinfo->args[2].isnull = false; //调用函数 result = FunctionCallInvoke(fcinfo); /* Should get null result if and only if str is NULL */ if (str == NULL) { if (!fcinfo->isnull) elog(ERROR, "input function %u returned non-NULL", flinfo->fn_oid); } else { if (fcinfo->isnull) elog(ERROR, "input function %u returned NULL", flinfo->fn_oid); } return result; } /* * int4in - converts "num" to int4 */ Datum int4in(PG_FUNCTION_ARGS) { char *num = PG_GETARG_CSTRING(0); PG_RETURN_INT32(pg_strtoint32(num)); } /* * Convert input string to a signed 32 bit integer. * * Allows any number of leading or trailing whitespace characters. Will throw * ereport() upon bad input format or overflow. * * NB: Accumulate input as a negative number, to deal with two's complement * representation of the most negative number, which can't be represented as a * positive number. */ int32 pg_strtoint32(const char *s) { const char *ptr = s; int32 tmp = 0; bool neg = false; /* skip leading spaces */ while (likely(*ptr) && isspace((unsigned char) *ptr)) ptr++; /* handle sign */ if (*ptr == '-') { ptr++; neg = true; } else if (*ptr == '+') ptr++; /* require at least one digit */ if (unlikely(!isdigit((unsigned char) *ptr))) goto invalid_syntax; /* process digits */ while (*ptr && isdigit((unsigned char) *ptr))//如'123',-1->-12->-123 { int8 digit = (*ptr++ - '0');//获取数字 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) ||//tmp*10 unlikely(pg_sub_s32_overflow(tmp, digit, &tmp)))//tmp - digit goto out_of_range; } /* allow trailing whitespace, but not other trailing chars */ while (*ptr != '\0' && isspace((unsigned char) *ptr)) ptr++; if (unlikely(*ptr != '\0')) goto invalid_syntax; if (!neg) { /* could fail if input is most negative number */ if (unlikely(tmp == PG_INT32_MIN)) goto out_of_range; tmp = -tmp;//符号取反,-123->123 } return tmp; out_of_range: ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("value \"%s\" is out of range for type %s", s, "integer"))); invalid_syntax: ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type %s: \"%s\"", "integer", s))); return 0; /* keep compiler quiet */ }
SQL脚本
testdb=# select * from t_conv where id = '1';
跟踪分析
(gdb) b coerce_type Breakpoint 1 at 0x6055f0: file parse_coerce.c, line 164. (gdb) c Continuing. Breakpoint 1, coerce_type (pstate=0x2c89e30, node=0x2c8a678, inputTypeId=705, targetTypeId=23, targetTypeMod=-1, ccontext=COERCION_IMPLICIT, cformat=COERCE_IMPLICIT_CAST, location=-1) at parse_coerce.c:164 164 if (targetTypeId == inputTypeId || (gdb)
输入参数,输入类型ID为705(unknown),目标类型为23(int4)
(gdb) p *pstate $1 = {parentParseState = 0x0, p_sourcetext = 0x2c88e08 "select * from t_conv where id = '1';", p_rtable = 0x2c8a2a0, p_joinexprs = 0x0, p_joinlist = 0x2c8a3a8, p_namespace = 0x2c8a328, p_lateral_active = false, p_ctenamespace = 0x0, p_future_ctes = 0x0, p_parent_cte = 0x0, p_target_relation = 0x0, p_target_rangetblentry = 0x0, p_is_insert = false, p_windowdefs = 0x0, p_expr_kind = EXPR_KIND_WHERE, p_next_resno = 2, p_multiassign_exprs = 0x0, p_locking_clause = 0x0, p_locked_from_parent = false, p_resolve_unknowns = true, p_queryEnv = 0x0, p_hasAggs = false, p_hasWindowFuncs = false, p_hasTargetSRFs = false, p_hasSubLinks = false, p_hasModifyingCTE = false, p_last_srf = 0x0, p_pre_columnref_hook = 0x0, p_post_columnref_hook = 0x0, p_paramref_hook = 0x0, p_coerce_param_hook = 0x0, p_ref_hook_state = 0x0} (gdb) p *node $2 = {type = T_Const} (gdb) p *(Const *)node $3 = {xpr = {type = T_Const}, consttype = 705, consttypmod = -1, constcollid = 0, constlen = -2, constvalue = 46701384, constisnull = false, constbyval = false, location = 32} (gdb) x/1cb 46701384 0x2c89b48: 49 '1' (gdb)
进入常量转换分支
(gdb) n 170 if (targetTypeId == ANYOID || (gdb) 171 targetTypeId == ANYELEMENTOID || (gdb) 188 if (targetTypeId == ANYARRAYOID || (gdb) 189 targetTypeId == ANYENUMOID || (gdb) 225 if (inputTypeId == UNKNOWNOID && IsA(node, Const)) (gdb) 244 Const *con = (Const *) node; (gdb) 245 Const *newcon = makeNode(Const); (gdb) 260 baseTypeMod = targetTypeMod; (gdb) p *con $4 = {xpr = {type = T_Const}, consttype = 705, consttypmod = -1, constcollid = 0, constlen = -2, constvalue = 46701384, constisnull = false, constbyval = false, location = 32} (gdb) n 261 baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); (gdb) 272 if (baseTypeId == INTERVALOID) (gdb) 275 inputTypeMod = -1; (gdb) 277 baseType = typeidType(baseTypeId); (gdb) 279 newcon->consttype = baseTypeId; (gdb) 280 newcon->consttypmod = inputTypeMod; (gdb) 281 newcon->constcollid = typeTypeCollation(baseType); (gdb) 282 newcon->constlen = typeLen(baseType); (gdb) 283 newcon->constbyval = typeByVal(baseType); (gdb) 284 newcon->constisnull = con->constisnull; (gdb) 291 newcon->location = con->location; (gdb) 297 setup_parser_errposition_callback(&pcbstate, pstate, con->location); (gdb) p *newcon $5 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 0, constisnull = false, constbyval = true, location = 32} (gdb)
完成转换
(gdb) n 303 if (!con->constisnull) (gdb) 305 DatumGetCString(con->constvalue), (gdb) 304 newcon->constvalue = stringTypeDatum(baseType, (gdb) 317 if (!con->constisnull && newcon->constlen == -1) (gdb) 349 cancel_parser_errposition_callback(&pcbstate); (gdb) 351 result = (Node *) newcon; (gdb) p *newcon $6 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 1, constisnull = false, constbyval = true, location = 32} (gdb) n 354 if (baseTypeId != targetTypeId) (gdb) 361 ReleaseSysCache(baseType); (gdb) 363 return result; (gdb)
转换的实现
跟踪InputFunctionCall
(gdb) b InputFunctionCall Breakpoint 2 at 0xa6fabe: file fmgr.c, line 1533. (gdb) c Continuing. Breakpoint 2, InputFunctionCall (flinfo=0x7ffd8b1da1f0, str=0x2c89b48 "1", typioparam=23, typmod=-1) at fmgr.c:1533 1533 LOCAL_FCINFO(fcinfo, 3); (gdb) n 1536 if (str == NULL && flinfo->fn_strict) (gdb) 1539 InitFunctionCallInfoData(*fcinfo, flinfo, 3, InvalidOid, NULL, NULL); (gdb) 1541 fcinfo->args[0].value = CStringGetDatum(str); (gdb) 1542 fcinfo->args[0].isnull = false; (gdb) 1543 fcinfo->args[1].value = ObjectIdGetDatum(typioparam); (gdb) 1544 fcinfo->args[1].isnull = false; (gdb) 1545 fcinfo->args[2].value = Int32GetDatum(typmod); (gdb) 1546 fcinfo->args[2].isnull = false; (gdb) 1548 result = FunctionCallInvoke(fcinfo); (gdb) p *fcinfo $7 = {flinfo = 0x7ffd8b1da1f0, context = 0x0, resultinfo = 0x0, fncollation = 0, isnull = false, nargs = 3, args = 0x7ffd8b1da180} (gdb) p *fcinfo->flinfo $8 = {fn_addr = 0x9694fc <int4in>, fn_oid = 42, fn_nargs = 1, fn_strict = true, fn_retset = false, fn_stats = 2 '\002', fn_extra = 0x0, fn_mcxt = 0x2c88cf0, fn_expr = 0x0} (gdb)
实现函数是int4in->pg_strtoint32
(gdb) b pg_strtoint32 Breakpoint 3 at 0x9b8b45: file numutils.c, line 201. (gdb) c Continuing. Breakpoint 3, pg_strtoint32 (s=0x2c89b48 "123") at numutils.c:201 201 const char *ptr = s; (gdb) n 202 int32 tmp = 0; (gdb) 203 bool neg = false; (gdb) 206 while (likely(*ptr) && isspace((unsigned char) *ptr)) (gdb) 210 if (*ptr == '-') (gdb) 215 else if (*ptr == '+') (gdb) 219 if (unlikely(!isdigit((unsigned char) *ptr))) (gdb) 223 while (*ptr && isdigit((unsigned char) *ptr)) (gdb) 225 int8 digit = (*ptr++ - '0'); (gdb) 227 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || (gdb) p digit $9 = 1 '\001' (gdb) n 228 unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) (gdb) p tmp $10 = 0 (gdb) n 227 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || (gdb) p tmp $11 = -1 (gdb) n 223 while (*ptr && isdigit((unsigned char) *ptr)) (gdb) p tmp $12 = -1 (gdb) n 225 int8 digit = (*ptr++ - '0'); (gdb) 227 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || (gdb) p digit $13 = 2 '\002' (gdb) n 228 unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) (gdb) 227 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || (gdb) p tmp $14 = -12 (gdb) n 223 while (*ptr && isdigit((unsigned char) *ptr)) (gdb) 225 int8 digit = (*ptr++ - '0'); (gdb) 227 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || (gdb) p digit $15 = 3 '\003' (gdb) n 228 unlikely(pg_sub_s32_overflow(tmp, digit, &tmp))) (gdb) 227 if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) || (gdb) p tmp $16 = -123 (gdb) n 223 while (*ptr && isdigit((unsigned char) *ptr)) (gdb) 233 while (*ptr != '\0' && isspace((unsigned char) *ptr)) (gdb) 236 if (unlikely(*ptr != '\0')) (gdb) 239 if (!neg) (gdb) 242 if (unlikely(tmp == PG_INT32_MIN)) (gdb) p tmp $17 = -123 (gdb) n 244 tmp = -tmp; (gdb) 247 return tmp;
到此,相信大家对“怎么实现PostgreSQL中的类型转换”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。