暂无图片
暂无图片
4
暂无图片
暂无图片
暂无图片

梧桐数据库:类型转换规则

原创 2024-08-04
203

Overview

SQL内常量的数据类型在梧桐数据库中需要经过两个阶段才可以确定,第一阶段是词法解析,此时只能确定常量的大致范围(整形/字符串/浮点数等),接着在transform阶段才可以准确的确定常量的数据类型(int/float/text/char/varchar这些数据库已经创建的数据类型)。
梧桐数据库提供了CREATE CAST语法,支持创建不同数据类型间的转换。但隐式类型转换涉及范围较广,存在很多潜在的风险。比如函数的选择逻辑就会受到影响,部分基础操作符不再唯一。因此建议使用时按需创建操作符和函数,而不是添加隐式类型转换。

1. 解析规则

语法解析通过lex和yacc完成,简单来说字符串常量解析为SCONST,纯数字解析为ICONST,数字+dot解析为FCONST。但是此时无法确定部分常量的具体类型,因为不同数据类型之间的比较规则不一样,比如char和varchar/text的比较规则就不同,如果提前确定了常量数据类型就有可能会导致选错操作符从而导致结果集错误。

2. 转换规则

转换规则是根据pg_cast这张系统表确定的,初始化之后系统已经内置了很多数据类型间的转换规则,可以通过CREATE CAST来创建新的转换规则。

  • IMPLICT/ASSIGNMENT:说明转换规则是隐式/赋值时调用
  • INOUT/FUNCTION:inout是通过类型的out函数+in函数方式读取,function则是通过类型转换函数实现。without function说明两个类型存储的二进制是兼容的,直接relabel就可以读取。

举例说明一个转换规则在transform的时候是怎么应用的,又为什么会引起报错

CREATE CAST (INTEGER AS TEXT) WITH INOUT AS IMPLICIT; SELECT 1::int = '1'::text; -- 添加前不支持,添加后语句正确执行 SELECT 1||'1'; -- 添加前语句正确执行,添加后语句执行报错

解析阶段内核首先根据操作符/函数的名字和参数类型进行精准匹配(函数名和入参的个数与类型都必须完全相同),如果匹配到了则选取该函数并转换为query树中的节点。否则需要用到隐式转换转换规则,将所有满足隐式转换的函数作为备选,最后根据匹配到的参数个数确定最优的备选并作为最终输出。
添加了隐式转换后,等值条件匹配操作符的时候通过pg_cast匹配到了对应的函数:

select oid,format_type(oprleft, null),format_type(oprright, null) from pg_operator where oprname='=' and (oprleft = 23 or oprright=25); oid | format_type | format_type --------+-------------+------------- 15 | integer | bigint 96 | integer | integer 98 | text | text 254 | name | text 533 | integer | smallint (5 rows)

拼接sql的报错也是和支持的操作符有关,添加隐式转换后textanycat/textcat/anytextcat都可以作为备选,导致transform无法选出一个最匹配的结果而报错。

select oid,format_type(oprleft, null),format_type(oprright, null),oprcode from pg_operator where oprname='||'; oid | format_type | format_type | oprcode ------+-------------+-------------+----------------- 349 | anyarray | anyelement | array_append 374 | anyelement | anyarray | array_prepend 375 | anyarray | anyarray | array_cat 654 | text | text | textcat 1797 | bit varying | bit varying | bitcat 2018 | bytea | bytea | byteacat 2779 | text | anynonarray | textanycat 2780 | anynonarray | text | anytextcat 3633 | tsvector | tsvector | tsvector_concat 3681 | tsquery | tsquery | tsquery_or 3284 | jsonb | jsonb | jsonb_concat (11 rows)

3. 添加操作符和重载函数

-- 添加操作符示例,添加后可直接进行int与text的比较 CREATE FUNCTION public.int4_text_eq(integer, text) RETURNS boolean AS 'SELECT $1::text=$2' LANGUAGE SQL IMMUTABLE STRICT; -- immutable说明同样的入参结果是不是总是一致的,可以按需改成volatile或者stable -- strict说明函数是不是严格的,输入null结果是null的函数就是严格的 CREATE OPERATOR public.= ( leftarg = integer, rightarg = text, procedure = int4_text_eq, commutator = = ); -- 两侧都要加 CREATE FUNCTION public.text_int4_eq(text, int4) RETURNS boolean AS 'SELECT $1=$2::text' LANGUAGE SQL IMMUTABLE STRICT; CREATE OPERATOR public.= ( leftarg = text, rightarg = integer, procedure = text_int4_eq, commutator = = ); -- 重载函数 -- 假设已有函数func1(text) returns text -- 可以通过重载函数使预期入参可以是text也可以是int -- 尽量不要使用anyelement类型入参,anyelement需要显示指定数据类型,而且重载可能导致死循环。 CREATE OR REPLACE FUNCTION func1(int) RETURNS text AS ' SELECT func1($1::text); ' LANGUAGE 'sql' IMMUTABLE STRICT;
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论