ITPub博客

首页 > 数据库 > PostgreSQL > PostgreSQL 源码解读(203)- 查询#116(类型转换实现)

PostgreSQL 源码解读(203)- 查询#116(类型转换实现)

原创 PostgreSQL 作者:husthxd 时间:2019-06-14 15:28:04 0 删除 编辑

本节简单介绍了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;

DONE!

四、参考资料

PostgreSQL Type Conversion
PostgreSQL数据类型转换规则#1
PostgreSQL数据类型转换规则#2
PostgreSQL数据类型转换规则#3

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/6906/viewspace-2647659/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论
长期从事政务、金融等行业产品研发和架构设计工作,ITPUB数据库版块资深版主,对Oracle、PostgreSQL有深入研究。现就职于广州云图数据技术有限公司,系统架构师。

注册时间:2007-12-28

  • 博文量
    1308
  • 访问量
    3785513