本篇内容主要讲解“怎么实现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中的类型转换”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:http://blog.itpub.net/6906/viewspace-2647659/