我需要使用按位逻辑AND / OR编写一些查询,但只有当我使用oracle数据库时,才会收到以下错误:
sqlalchemy.exc.DatabaseError:(cx_Oracle.DatabaseError)ORA-01036: 非法变量名称/编号[SQL:' SELECT" Cars"。" Id", "汽车"。"名称","汽车"。"价格" \ nFROM"汽车" \ n WHERHER(" Cars"。" Price"& :Price_1)> :param_1'] [参数:{' Price_1':32768,' param_1':0}]
如果我使用PostgreSql或Sqlite,我会收到预期的答案。
create_engine(' sqlite:///cars.sqlite3')好的! create_engine(' postgresql + psycopg2:// xxx:yyy @ localhost:5432 / db_sql_alchemy')好的! create_engine('预言+ cx_oracle:// XXX:YYY @本地:49161 / XE') ERROR!
其他操作,例如select,where子句,表创建,在所有3个数据库中按预期工作。
观察错误日志,似乎查询未正确转换为oracle语法。我期待这样的事情:
SELECT" Cars"。" Id"," Cars"。" Name"," Cars"。& #34;价格"来自"汽车"哪里 (BitAnd(" Cars"。" Price",32768)> 0);
生成错误的操作是:
stm = stm.where(cars.c.Price.op('&')(0x8000)> 0)
我正在使用Python 2.7.12和SQLAlchemy == 1.1.2。
答案 0 :(得分:0)
以下是创建在不同后端编译不同的自定义运算符的示例:
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.elements import ColumnElement, Visitable, Grouping
from sqlalchemy.sql.operators import custom_op, is_precedent
class BitwiseAnd(ColumnElement):
type = Integer()
operator = custom_op("&", precedence=6)
def __init__(self, left, right):
if not isinstance(left, Visitable):
left = bindparam("bitand", left, unique=True)
if not isinstance(right, Visitable):
right = bindparam("bitand", right, unique=True)
self.left = left
self.right = right
def self_group(self, against=None):
if is_precedent(self.operator, against):
return Grouping(self)
else:
return self
@compiles(BitwiseAnd)
def _compile_bitwise_and(element, compiler, **kwargs):
left = element.left.self_group(against=element.operator)
right = element.right.self_group(against=element.operator)
return compiler.process(element.operator(left, right))
@compiles(BitwiseAnd, "oracle")
def _compile_bitwise_and_oracle(element, compiler, **kwargs):
return compiler.process(func.BITAND(element.left, element.right))
q = select([BitwiseAnd(BitwiseAnd(1, 2), BitwiseAnd(3, 4))])
print(q.compile(dialect=mysql.dialect()))
# SELECT (%s & %s) & (%s & %s) AS anon_1
print(q.compile(dialect=oracle.dialect()))
# SELECT BITAND(BITAND(:bitand_1, :bitand_2), BITAND(:bitand_3, :bitand_4)) AS anon_1 FROM DUAL