具有位变化的PostgreSQL Bitwise运算符“不能对不同大小的字符串进行AND位”

时间:2012-12-04 00:19:12

标签: postgresql bit-manipulation

我有一个不同的位掩码字段,我想对它执行按位AND。

PG::Error: ERROR:  cannot AND bit strings of different sizes
SELECT "groups".* FROM "groups"  WHERE (read_roles_bitmask = B'0' OR read_roles_bitmask & B'10' > B'0')

(你需要在表中使用不同长度的位掩码来获得此错误。)

我期待按位数学看起来如下: 00010& 100000010 = 00010

我也尝试将位掩码转换为整数而没有运气。

为什么PostgreSQL会扼杀这个?

我该怎样重写这个查询才能很好地玩?

我能够使用以下方法让按位运算符工作: LPAD(read_roles_bitmask :: VARCHAR,64, '0'):: BIGINT

然而,这仅限于64位,有更好的方法吗?

3 个答案:

答案 0 :(得分:6)

PostgreSQL bitbit varying类型的行为非常无用,它拒绝扩展操作的位域的方式,并且它正确地扩展它们用于强制转换而不是向左扩展它们。

Pg在AND或OR运算之前用零加左延伸较小的操作数是有意义的,而不是失败。

你不能使用强制转换来bit(n)来获得相同的长度,因为出于某种疯狂的原因,强制转换为bit(n) 右键填充参数,几乎在所有情况下都没用。

您可以使用lpad($1::text, greatest(length($1), length($2)),'0')::bit varying之类的内容向左扩展一个带有零的位字段到两个长度中的较大者。这很麻烦,但它会起作用。我建议编写包装器函数以包含混乱。

或者,考虑修改bit中的src/backend/utils/adt/varbit.c支持代码以向左扩展和左截断位字段添加函数,并使用函数进行左扩展比较。根据现有代码,这应该很容易。

答案 1 :(得分:1)

今天我遇到了类似的问题。我想做几乎完全相同的事情:屏蔽位串的最低两位并将结果与​​文字值进行比较,如下所示:

status & b'11' > b'01'

(状态是我的位变化列。)

最初我尝试使用Craig的解决方案,但它很快就变得非常混乱,因为不仅需要扩展掩码,我将比较结果的值也是如此,因为根据postgresql:

t2=> select b'0010' < b'01';
 ?column?
----------
 t
(1 row)

在应用<操作之前,RHS正确填充以使其与LHS相同。

最后我解决了这个问题:

(status << length(status)-2)::bit(2) > b'01'

关于这一点的好处是它允许您提取任何位集以进行比较。例如,从左侧获取第3位:

(status << length(status)-6)::bit(2)

您还可以使用substring提取任意位集以进行比较。

答案 2 :(得分:0)

1),如其他答案中所述-

qscintillaWidget.setCaretLineVisible(True)
qscintillaWidget.setCaretLineBackgroundColor(QtGui.QColor('lightblue'))

2)一种解决方法是-

postgres has has some inconvenient/counterintuitive behaviors - 
right padding when casting to bit(n), 
bitwise ops only on similar size,
etc.

优点:

double-casting every value - to integer and then to bit(XX)

示例:

基本左填充:

- left vs right padding works correctly 
- all the bit-strings have same length for correct bitwise operations
- comparisons work correctly
- bit-masking/casting to get least significant bits

按位操作:

select B'0010'::int::bit(22)
0000000000000000000010

比较:

select B'0010'::int::bit(22) | B'01'::int::bit(22)
0000000000000000000011

位屏蔽/广播以获得三个最低有效位:

select B'0010'::int::bit(22) > B'01'::int::bit(22)
true

位屏蔽/广播以获得三个MOST有效位:

select B'11010'::int::bit(3)
010

更新: 使用int8容纳更长的位字符串