千家信息网

PostgreSQL 源码解读(202)- 查询#115(类型转换)

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,本节简单介绍了PostgreSQL中的类型转换实现.首先来看看几个示例SQL:testdb=# testdb=# drop table if exists t_conv;om t_conv where
千家信息网最后更新 2026年11月04日PostgreSQL 源码解读(202)- 查询#115(类型转换)

本节简单介绍了PostgreSQL中的类型转换实现.
首先来看看几个示例SQL:

testdb=# testdb=# drop table if exists t_conv;om t_conv where id = '1'::text;explain verbose select * from t_conv where id > '13'::text;DROP TABLEtestdb=# create table t_conv(id int);CREATE TABLEtestdb=# testdb=# insert into t_conv values(1);INSERT 0 1testdb=# insert into t_conv values(2);INSERT 0 1testdb=#

查询条件为id = '1','1'转换为int进行比较.

testdb=# explain verbose select * from t_conv where id = '1';                          QUERY PLAN                           --------------------------------------------------------------- Seq Scan on public.t_conv  (cost=0.00..41.88 rows=13 width=4)   Output: id   Filter: (t_conv.id = 1)(3 rows)

查询条件为id = '1.1',出错(1.1无法转换为整型)

testdb=# explain verbose select * from t_conv where id = '1.1';psql: ERROR:  invalid input syntax for type integer: "1.1"LINE 1: explain verbose select * from t_conv where id = '1.1';                                                        ^

查询条件为id = '1'::text,出错(text和int4没有转换规则)

testdb=# explain verbose select * from t_conv where id = '1'::text;psql: ERROR:  operator does not exist: integer = textLINE 1: explain verbose select * from t_conv where id = '1'::text;                                                      ^HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.testdb=#

查询条件为id > '13'::text,转换为numeric进行比较

testdb=# explain verbose select * from t_conv where id > '13'::text;                           QUERY PLAN                           ---------------------------------------------------------------- Seq Scan on public.t_conv  (cost=0.00..48.25 rows=850 width=4)   Output: id   Filter: ((t_conv.id)::numeric > '13'::numeric)(3 rows)testdb=#

系统转换规则定义,">"操作符有定义text&int的比较,而"="操作符没有定义,因此"select * from t_conv where id = '1'::text"语句会出错.

testdb=# select oid,typname from pg_type where typname in ('int4','text'); oid | typname -----+---------  23 | int4  25 | text(2 rows)testdb=# select oid,oprname,oprleft,oprright,oprresult,oprcode from pg_operator where oprname = '>' and oprleft = 23 and oprright = 25;  oid  | oprname | oprleft | oprright | oprresult |     oprcode      -------+---------+---------+----------+-----------+------------------ 16407 | >       |      23 |       25 |        16 | int_greater_text(1 row)testdb=# select oid,oprname,oprleft,oprright,oprresult,oprcode from pg_operator where oprname = '=' and oprleft = 23 and oprright = 25; oid | oprname | oprleft | oprright | oprresult | oprcode -----+---------+---------+----------+-----------+---------(0 rows)

总结一下:
解析表达式,涉及不同数据类型时:
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;

二、源码解读

make_op
变换操作符表达式以确保类型兼容,会存在某些类型转换.

/* * make_op() *    Operator expression construction. * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. * 变换操作符表达式以确保类型兼容,会存在某些类型转换. * * last_srf should be a copy of pstate->p_last_srf from just before we * started transforming the operator's arguments; this is used for nested-SRF * detection.  If the caller will throw an error anyway for a set-returning * expression, it's okay to cheat and just pass pstate->p_last_srf. * last_srf应为pstate->p_last_srf的一个拷贝. */Expr *make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,    Node *last_srf, int location){  Oid     ltypeId,//左算子类型OID        rtypeId;//右算子类型OID  Operator  tup;//操作符tuple  Form_pg_operator opform;//操作符form(参照pg_operator)  Oid     actual_arg_types[2];//实际参数类型  Oid     declared_arg_types[2];//操作符声明的参数类型  int     nargs;//参数格式  List     *args;//参数链表  Oid     rettype;//返回结果类型  OpExpr     *result;//结果  /* Select the operator */  //选择操作符  if (rtree == NULL)  {    /* right operator */    //右树为NULL,则为右操作符,如阶乘运算符"!" : 10!    ltypeId = exprType(ltree);    rtypeId = InvalidOid;    tup = right_oper(pstate, opname, ltypeId, false, location);  }  else if (ltree == NULL)  {    /* left operator */    //左树为空,则为左操作符,如按位NOT运算符 : "~21"    rtypeId = exprType(rtree);    ltypeId = InvalidOid;    tup = left_oper(pstate, opname, rtypeId, false, location);  }  else  {    /* otherwise, binary operator */    //二元操作符    ltypeId = exprType(ltree);    rtypeId = exprType(rtree);    tup = oper(pstate, opname, ltypeId, rtypeId, false, location);  }  //获取操作符  opform = (Form_pg_operator) GETSTRUCT(tup);  /* Check it's not a shell */  if (!RegProcedureIsValid(opform->oprcode))    ereport(ERROR,        (errcode(ERRCODE_UNDEFINED_FUNCTION),         errmsg("operator is only a shell: %s",            op_signature_string(opname,                      opform->oprkind,                      opform->oprleft,                      opform->oprright)),         parser_errposition(pstate, location)));  /* Do typecasting and build the expression tree */  //执行类型转换并构建表达式树  if (rtree == NULL)  {    /* right operator */    args = list_make1(ltree);//参数    actual_arg_types[0] = ltypeId;//左实际参数类型    declared_arg_types[0] = opform->oprleft;//声明类型    nargs = 1;//参数个数  }  else if (ltree == NULL)  {    /* left operator */    args = list_make1(rtree);    actual_arg_types[0] = rtypeId;    declared_arg_types[0] = opform->oprright;    nargs = 1;  }  else  {    /* otherwise, binary operator */    args = list_make2(ltree, rtree);    actual_arg_types[0] = ltypeId;    actual_arg_types[1] = rtypeId;    declared_arg_types[0] = opform->oprleft;    declared_arg_types[1] = opform->oprright;    nargs = 2;  }  /*   * enforce consistency with polymorphic argument and return types,   * possibly adjusting return type or declared_arg_types (which will be   * used as the cast destination by make_fn_arguments)   * 强制多态参数与返回类型的一致性,可能会调整返回类型或者declared_arg_types   * (可能会在函数make_fn_arguments中作为转换目标类型)   */  rettype = enforce_generic_type_consistency(actual_arg_types,                         declared_arg_types,                         nargs,                         opform->oprresult,                         false);  /* perform the necessary typecasting of arguments */  //参数类型转换  make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);  /* and build the expression node */  //构建表达式节点  result = makeNode(OpExpr);  result->opno = oprid(tup);  result->opfuncid = opform->oprcode;  result->opresulttype = rettype;  result->opretset = get_func_retset(opform->oprcode);  /* opcollid and inputcollid will be set by parse_collate.c */  result->args = args;  result->location = location;  /* if it returns a set, check that's OK */  if (result->opretset)  {    check_srf_call_placement(pstate, last_srf, location);    /* ... and remember it for error checks at higher levels */    pstate->p_last_srf = (Node *) result;  }  ReleaseSysCache(tup);  return (Expr *) result;}

make_fn_arguments
给定实际的参数表达式和期望的输入类型,为表达式树执行有需要的类型转换.

/* * make_fn_arguments() * * Given the actual argument expressions for a function, and the desired * input types for the function, add any necessary typecasting to the * expression tree.  Caller should already have verified that casting is * allowed. * 给定实际的参数表达式和期望的输入类型,为表达式树执行有需要的类型转换. * 调用者已验证转换是可行的. * * Caution: given argument list is modified in-place. * 就地转换参数类型. * * As with coerce_type, pstate may be NULL if no special unknown-Param * processing is wanted. */voidmake_fn_arguments(ParseState *pstate,          List *fargs,          Oid *actual_arg_types,          Oid *declared_arg_types){  ListCell   *current_fargs;  int     i = 0;  foreach(current_fargs, fargs)  {    /* types don't match? then force coercion using a function call... */    //类型不匹配?    if (actual_arg_types[i] != declared_arg_types[i])    {      //获取节点      Node     *node = (Node *) lfirst(current_fargs);      /*       * If arg is a NamedArgExpr, coerce its input expr instead --- we       * want the NamedArgExpr to stay at the top level of the list.       * 如果参数是NamedArgExpr,则强制转换为输入表达式       */      if (IsA(node, NamedArgExpr))      {        NamedArgExpr *na = (NamedArgExpr *) node;        node = coerce_type(pstate,                   (Node *) na->arg,                   actual_arg_types[i],                   declared_arg_types[i], -1,                   COERCION_IMPLICIT,                   COERCE_IMPLICIT_CAST,                   -1);        na->arg = (Expr *) node;      }      else      {        //执行转换        node = coerce_type(pstate,                   node,                   actual_arg_types[i],                   declared_arg_types[i], -1,                   COERCION_IMPLICIT,                   COERCE_IMPLICIT_CAST,                   -1);        lfirst(current_fargs) = node;      }    }    i++;  }}

三、跟踪分析

SQL脚本

testdb=# select * from t_conv where id = '1';

跟踪分析

(gdb) b make_opBreakpoint 2 at 0x619cdb: file parse_oper.c, line 762.(gdb) cContinuing.Breakpoint 2, make_op (pstate=0x22d6e30, opname=0x22d6c48, ltree=0x22d7620, rtree=0x22d7678, last_srf=0x0, location=30)    at parse_oper.c:762762   if (rtree == NULL)(gdb)

调用栈信息

(gdb) bt#0  make_op (pstate=0x22d6e30, opname=0x22d6c48, ltree=0x22d7620, rtree=0x22d7678, last_srf=0x0, location=30)    at parse_oper.c:859#1  0x000000000060df9c in transformAExprOp (pstate=0x22d6e30, a=0x22d6ba0) at parse_expr.c:1010#2  0x000000000060c55b in transformExprRecurse (pstate=0x22d6e30, expr=0x22d6ba0) at parse_expr.c:216#3  0x000000000060c298 in transformExpr (pstate=0x22d6e30, expr=0x22d6ba0, exprKind=EXPR_KIND_WHERE) at parse_expr.c:155#4  0x0000000000602164 in transformWhereClause (pstate=0x22d6e30, clause=0x22d6ba0, exprKind=EXPR_KIND_WHERE,     constructName=0xb458a7 "WHERE") at parse_clause.c:1691#5  0x00000000005c7eb5 in transformSelectStmt (pstate=0x22d6e30, stmt=0x22d6c80) at analyze.c:1239#6  0x00000000005c6392 in transformStmt (pstate=0x22d6e30, parseTree=0x22d6c80) at analyze.c:301#7  0x00000000005c626d in transformOptionalSelectInto (pstate=0x22d6e30, parseTree=0x22d6c80) at analyze.c:246#8  0x00000000005c612b in transformTopLevelStmt (pstate=0x22d6e30, parseTree=0x22d6d98) at analyze.c:196#9  0x00000000005c5f83 in parse_analyze (parseTree=0x22d6d98, sourceText=0x22d5e08 "select * from t_conv where id = '1';",     paramTypes=0x0, numParams=0, queryEnv=0x0) at analyze.c:116#10 0x00000000008e78d9 in pg_analyze_and_rewrite (parsetree=0x22d6d98,     query_string=0x22d5e08 "select * from t_conv where id = '1';", paramTypes=0x0, numParams=0, queryEnv=0x0)    at postgres.c:695#11 0x00000000008e7f23 in exec_simple_query (query_string=0x22d5e08 "select * from t_conv where id = '1';")    at postgres.c:1140#12 0x00000000008ec3a0 in PostgresMain (argc=1, argv=0x22ffdb8, dbname=0x22ffc00 "testdb", username=0x22d2a68 "pg12")    at postgres.c:4249#13 0x0000000000843650 in BackendRun (port=0x22f7be0) at postmaster.c:4431#14 0x0000000000842e2e in BackendStartup (port=0x22f7be0) at postmaster.c:4122#15 0x000000000083f06a in ServerLoop () at postmaster.c:1704#16 0x000000000083e920 in PostmasterMain (argc=1, argv=0x22d0a20) at postmaster.c:1377#17 0x000000000075f834 in main (argc=1, argv=0x22d0a20) at main.c:228

输入参数
操作符为"=";ltree为字段列,类型为int4(vartype = 23);rtree为常量,类型为unknown(consttype = 705);last_srf为NULL;该操作符在字符串中的偏移为30.

(gdb) p *pstate$7 = {parentParseState = 0x0, p_sourcetext = 0x22d5e08 "select * from t_conv where id = '1';", p_rtable = 0x22d72a0,   p_joinexprs = 0x0, p_joinlist = 0x22d73a8, p_namespace = 0x22d7328, 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 *opname$8 = {type = T_List, length = 1, head = 0x22d6c20, tail = 0x22d6c20}(gdb) p *(Node *)opname->head->data.ptr_value$9 = {type = T_String}(gdb) p *(Value *)opname->head->data.ptr_value$14 = {type = T_String, val = {ival = 12298942, str = 0xbbaabe "="}}(gdb) p *ltree$15 = {type = T_Var}(gdb) p *(Var *)ltree$16 = {xpr = {type = T_Var}, varno = 1, varattno = 1, vartype = 23, vartypmod = -1, varcollid = 0, varlevelsup = 0,   varnoold = 1, varoattno = 1, location = 27}(gdb) p *(Const *)rtree$18 = {xpr = {type = T_Const}, consttype = 705, consttypmod = -1, constcollid = 0, constlen = -2, constvalue = 36531016,   constisnull = false, constbyval = false, location = 32}  ###testdb=# select typname from pg_type where oid in (23,705); typname --------- int4 unknown(2 rows)###

确定操作符(二元操作符)

(gdb) n769   else if (ltree == NULL)(gdb) 779     ltypeId = exprType(ltree);(gdb) 780     rtypeId = exprType(rtree);(gdb) 781     tup = oper(pstate, opname, ltypeId, rtypeId, false, location);(gdb) p ltypeId$19 = 23(gdb) p rtypeId$20 = 705(gdb)

获取操作符(对应pg_operator)

(gdb) n784   opform = (Form_pg_operator) GETSTRUCT(tup);(gdb) 787   if (!RegProcedureIsValid(opform->oprcode))(gdb) p *opform$21 = {oid = 96, oprname = {data = "=", '\000' }, oprnamespace = 11, oprowner = 10, oprkind = 98 'b',   oprcanmerge = true, oprcanhash = true, oprleft = 23, oprright = 23, oprresult = 16, oprcom = 96, oprnegate = 518,   oprcode = 65, oprrest = 101, oprjoin = 105}(gdb)

执行类型转换并构建表达式树.
实际类型为23&705,操作符声明操作类型为23&23

(gdb) n798   if (rtree == NULL)(gdb) 806   else if (ltree == NULL)(gdb) 817     args = list_make2(ltree, rtree);(gdb) 818     actual_arg_types[0] = ltypeId;(gdb) 819     actual_arg_types[1] = rtypeId;(gdb) 820     declared_arg_types[0] = opform->oprleft;(gdb) 821     declared_arg_types[1] = opform->oprright;(gdb) 822     nargs = 2;(gdb) p opform->oprleft$22 = 23(gdb) p opform->oprright$23 = 23(gdb) n830   rettype = enforce_generic_type_consistency(actual_arg_types,(gdb)

进入函数make_fn_arguments

(gdb) 837   make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);(gdb) nBreakpoint 1, make_fn_arguments (pstate=0x22d6e30, fargs=0x22d76f8, actual_arg_types=0x7ffda1b2af80,     declared_arg_types=0x7ffda1b2af70) at parse_func.c:18351835    int     i = 0;(gdb)

调用coerce_type函数执行转换,unknown -> int4

(gdb) n1837    foreach(current_fargs, fargs)(gdb) 1840      if (actual_arg_types[i] != declared_arg_types[i])(gdb) 1873      i++;(gdb) 1837    foreach(current_fargs, fargs)(gdb) 1840      if (actual_arg_types[i] != declared_arg_types[i])(gdb) p actual_arg_types[i]$24 = 705(gdb) p declared_arg_types[i]$25 = 23(gdb) n1842        Node     *node = (Node *) lfirst(current_fargs);(gdb) n1848        if (IsA(node, NamedArgExpr))(gdb) 1866                     declared_arg_types[i], -1,(gdb) 1863          node = coerce_type(pstate,(gdb) 1865                     actual_arg_types[i],(gdb) 1863          node = coerce_type(pstate,(gdb) 1870          lfirst(current_fargs) = node;(gdb) p *node$26 = {type = T_Const}(gdb) p *(Const *)node$27 = {xpr = {type = T_Const}, consttype = 23, consttypmod = -1, constcollid = 0, constlen = 4, constvalue = 1,   constisnull = false, constbyval = true, location = 32}(gdb)

完成调用

(gdb) n1873      i++;(gdb) 1837    foreach(current_fargs, fargs)(gdb) 1875  }(gdb) make_op (pstate=0x22d6e30, opname=0x22d6c48, ltree=0x22d7620, rtree=0x22d7678, last_srf=0x0, location=30)    at parse_oper.c:840840   result = makeNode(OpExpr);

回到make_op,构造结果result结构体

(gdb) 841   result->opno = oprid(tup);(gdb) 842   result->opfuncid = opform->oprcode;(gdb) 843   result->opresulttype = rettype;(gdb) 844   result->opretset = get_func_retset(opform->oprcode);(gdb) 846   result->args = args;(gdb) 847   result->location = location;(gdb) 850   if (result->opretset)(gdb) 857   ReleaseSysCache(tup);(gdb) 859   return (Expr *) result;(gdb) p *(Expr *) result$28 = {type = T_OpExpr}(gdb) p *(OpExpr *) result$29 = {xpr = {type = T_OpExpr}, opno = 96, opfuncid = 65, opresulttype = 16, opretset = false, opcollid = 0,   inputcollid = 0, args = 0x22d76f8, location = 30}(gdb)

DONE!

实现函数coerce_type下节再行介绍

四、参考资料

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

类型 操作符 参数 表达式 规则 实际 数据 查询 函数 条件 输入 结构 结果 目标 算子 节点 运算符 分析 变换 强制 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 广东万易互联网科技有限公司惠州分公司 互联网科技公司的优势 互联网扶贫科技 腾讯科技 互联网女皇 互联网 视角下的科技创新 兼蜜互联网科技 滨湖区互联网智能科技产品供应 农村开互联网科技讲堂 南山科技园互联网公司有哪些 互联网科技和信息 互联网金融科技展 本届互联网博览会上的黑科技 华夏恒生互联网科技013171 内蒙古九一互联网科技 翼展科技 首家互联网医院 高科技互联网发展前景如何 城市宝互联网科技有限公司 从那以后互联网科技有限公司 容联科技互联网电商 科技互联网经济论坛 互联网科技咨询 成都玖崟互联网科技有限公司乐商圈怎么样 互联网汽车科技小镇 苏州五指互联网络科技有限 青海科技产业互联网发展 互联网公司高科技产品 领航科技大厦互联网公司 风投北京互联网科技 中国互联网公司的科技投入 六子台科技互联网展会
0