INT文字VS PL / pgSQL的函数int参数之间的理解差异(Understanding di

2019-09-30 11:04发布

我必须在PostgreSQL的9.5左板位刺的函数:

CREATE OR REPLACE FUNCTION lpad_bits(val bit varying) 
RETURNS bit varying as
$BODY$
  BEGIN return val::bit(32) >> (32-length(val));
  END;
$BODY$
LANGUAGE plpgsql IMMUTABLE;

其正常工作:

 # select lpad_bits(b'1001100111000');
        lpad_bits
 ----------------------------------
 00000000000000000001001100111000
 (1 row)

我的问题是,当我尝试添加一个参数来改变填充量:

CREATE OR REPLACE FUNCTION lpad_bits(val bit varying, sz integer default 1024) 
  RETURNS bit varying as
$BODY$
  BEGIN return val::bit(sz) >> (sz-length(val));
  END;
$BODY$
LANGUAGE plpgsql IMMUTABLE;

该功能现在打破:

# select lpad_bits(b'1001100111000', 32);                                      
 ERROR: invalid input syntax for integer: "sz" LINE 1: SELECT val::bit(sz) >> (sz-length(val)) ^ QUERY: SELECT val::bit(sz) >> (sz-length(val)) CONTEXT: PL/pgSQL function lpad_bits(bit varying,integer) line 2 at RETURN 

我在盯着有位串的文件和PL / pgSQL函数的文档 ,我根本就没有看到的是这两种实现之间的根本不同。

Answer 1:

为什么?

PL / pgSQL的执行像准备好的发言 SQL查询。 有关参数substituion手册:

准备的语句可以带参数:在执行时,它被代入语句的值。

此处所谓的价值观 。 只有实际值可以带参数,但不是关键词,标识符或类型名。 32bit(32) 看起来像一个值,而是一个数据类型的改性剂是只有一个“值”内部和不能被参数化。 SQL要求知道在规划阶段的数据类型,它不能等待执行阶段。

可以使用动态SQL实现自己的目标和EXECUTE 。 由于概念证明

CREATE OR REPLACE FUNCTION lpad_bits(val varbit, sz int = 32, OUT outval varbit) AS
$func$
BEGIN
   EXECUTE format('SELECT $1::bit(%s) >> $2', sz)  -- literal
   USING val, sz - length(val)                     -- values
   INTO outval;
END
$func$  LANGUAGE plpgsql IMMUTABLE;

呼叫:

SELECT lpad_bits(b'1001100111000', 32);  

注意区分sz被用作字面来构建语句,并使用它的作为它的第二次出现,可以作为参数传递。

更快的替代方案

对于这个特殊的任务比较优秀的方案是只使用lpad()类似@Abelisto建议 :

CREATE OR REPLACE FUNCTION lpad_bits2(val varbit, sz int = 32)
  RETURNS varbit AS
$func$
SELECT lpad(val::text, sz, '0')::varbit;
$func$  LANGUAGE sql IMMUTABLE;

(简单以纯SQL函数,它也允许内联函数外的查询的情况下。)

数倍于上述功能更快。 一个小小的瑕疵:我们要转换为text ,并回到varbit 。 不幸的是, lpad()目前未实现varbit 。 手动:

:下面的SQL标准的功能对位串以及字符串的工作lengthbit_lengthoctet_lengthpositionsubstringoverlay

overlay()是可用的,我们可以有一个更便宜的功能:

CREATE OR REPLACE FUNCTION lpad_bits3(val varbit, base varbit = '00000000000000000000000000000000')
  RETURNS varbit AS
$func$
SELECT overlay(base PLACING val FROM bit_length(base) - bit_length(val))
$func$  LANGUAGE sql IMMUTABLE;

更快,如果你可以工作varbit值开始。 (其优点是(部分)作废,如果你要投textvarbit反正。)

呼叫:

SELECT lpad_bits3(b'1001100111000', '00000000000000000000000000000000');
SELECT lpad_bits3(b'1001100111000',  repeat('0', 32)::varbit);

我们可能overlaod功能具有一个变体将整数生成base本身:

CREATE OR REPLACE FUNCTION lpad_bits3(val varbit, sz int = 32)
  RETURNS varbit AS
$func$
SELECT overlay(repeat('0', sz)::varbit PLACING val FROM sz - bit_length(val))
$func$  LANGUAGE sql IMMUTABLE;

呼叫:

SELECT lpad_bits3(b'1001100111000', 32;

有关:

  • PostgreSQL的转换位不同的整数
  • 十六进制转换的文本表示十进制数


Answer 2:

解析器不允许在该位置的变量。 另一种方法是使用一个常数,修剪:

select right((val::bit(128) >> (128 -length(val)))::text, sz)::bit(sz)
from (values (b'1001100111000', 32)) s(val,sz)
;
              right               
----------------------------------
 00000000000000000001001100111000

lpad函数在评论中所建议的。



文章来源: Understanding difference between int literal vs int parameter in PL/pgSQL function