Postgresql 禁用将带引号的值隐式转换为整数
在这里,插入时和选择时postgres都会隐式地将带引号的字符串转换为整数类型,而不是引发类型错误,除非带引号的值被非常明确地类型化为varchar:Postgresql 禁用将带引号的值隐式转换为整数,postgresql,Postgresql,在这里,插入时和选择时postgres都会隐式地将带引号的字符串转换为整数类型,而不是引发类型错误,除非带引号的值被非常明确地类型化为varchar: # CREATE TABLE foo ( id serial, val integer ); CREATE TABLE # INSERT INTO foo (val) VALUES ('1'), (2); INSERT 0 2 # SELECT * FROM foo WHERE id='1'; id | val ----+----- 1
# CREATE TABLE foo ( id serial, val integer );
CREATE TABLE
# INSERT INTO foo (val) VALUES ('1'), (2);
INSERT 0 2
# SELECT * FROM foo WHERE id='1';
id | val
----+-----
1 | 1
(1 row)
这里的问题是没有隐式转换的动态类型语言(例如Ruby或Python)
- 带引号的值映射到字符串
- 整数映射为整数
- 这些不兼容,因此根据连接应用程序的体系结构,此行为可能导致不一致的缓存等
# INSERT INTO foo (val) VALUES (varchar '1');
ERROR: column "val" is of type integer
but expression is of type character varying
LINE 1: INSERT INTO foo (val) VALUES (varchar '1');
^
HINT: You will need to rewrite or cast the expression.
哪些产出:
import psycopg2.extensions
with psycopg2.connect(dbname='postgres') as cn:
cn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
with cn.cursor() as cx:
cx.execute("DROP DATABASE IF EXISTS test")
cx.execute("CREATE DATABASE test")
with psycopg2.connect(dbname='test') as cn:
with cn.cursor() as cx:
cx.execute("CREATE TABLE foo ( id serial, val integer )")
cx.execute("INSERT INTO foo (val) VALUES (%s), (%s)",
(1, '2'))
cx.execute("SELECT * FROM foo WHERE id=%s",
('1',))
print cx.fetchall()
否,不能禁用将带引号的文字隐式转换为任何目标类型。PostgreSQL将此类文本视为
unknown
类型,除非由强制转换或文本类型说明符重写,并将从unknown
转换为任何类型。没有从unknown
转换到pg_cast
中的类型的转换;这是含蓄的。所以你不能放弃它
据我所知,PostgreSQL遵循SQL规范,接受带引号的文本作为整数
对于PostgreSQL的类型引擎,1
是一个整数
,'1'
是一个未知
,如果传递给整数
函数、运算符或字段,则该类型将被推断为整数
。您不能禁用来自unknown
的类型推断,或强制unknown
被视为text
,而不直接对解析器/查询计划器进行黑客攻击
您应该做的是使用参数化语句,而不是将文本替换为SQL。如果这样做,则不会出现此问题,因为客户端类型已知或可以指定。这当然适用于Python(psycopg2),而Ruby(Pg gem)不适用于我对psycopg2的想法,见下文
澄清问题后更新:在这里描述的狭义案例中,psycopg2的客户端参数化声明虽然正确,但不会产生原始海报所期望的结果。在更新中运行演示表明,psycopg2没有使用PostgreSQL的v3绑定/执行协议,而是使用简单查询协议并在本地进行参数替换。因此,虽然在Python中使用参数化语句,但在PostgreSQL中没有使用参数化语句。我在上面错误地说psycopg2中的参数化状态可以解决这个问题 演示程序从PostgreSQL日志中运行以下SQL:
[(1, 1)]
因此,您需要绕过类型适配器注册来调用str
的原始底层适配器。这将,尽管它很丑陋:
def adapt_str_strict(thestr):
return psycopg2.extensions.AsIs('TEXT ' + psycopg2.extensions.adapt(thestr))
psycopg2.extensions.register_adapter(str, adapt_str_strict)
用它运行您的演示,您将获得:
def adapt_str_strict(thestr):
return psycopg2.extensions.AsIs('TEXT ' + str(psycopg2.extensions.QuotedString(thestr)))
psycopg2.extensions.register_adapter(str, adapt_str_strict)
(顺便说一句,使用服务器端的
PREPARE
和EXECUTE
是行不通的,因为当通过psycopg2
将值传递给EXECUTE
时,您只会遇到同样的键入问题。)否,您不能禁用引用的文本到任何目标类型的隐式转换。PostgreSQL将此类文本视为unknown
类型,除非由强制转换或文本类型说明符重写,并将从unknown
转换为任何类型。没有从unknown
转换到pg_cast
中的类型的转换;这是含蓄的。所以你不能放弃它
据我所知,PostgreSQL遵循SQL规范,接受带引号的文本作为整数
对于PostgreSQL的类型引擎,1
是一个整数
,'1'
是一个未知
,如果传递给整数
函数、运算符或字段,则该类型将被推断为整数
。您不能禁用来自unknown
的类型推断,或强制unknown
被视为text
,而不直接对解析器/查询计划器进行黑客攻击
您应该做的是使用参数化语句,而不是将文本替换为SQL。如果这样做,则不会出现此问题,因为客户端类型已知或可以指定。这当然适用于Python(psycopg2),而Ruby(Pg gem)不适用于我对psycopg2的想法,见下文
澄清问题后更新:在这里描述的狭义案例中,psycopg2的客户端参数化声明虽然正确,但不会产生原始海报所期望的结果。在更新中运行演示表明,psycopg2没有使用PostgreSQL的v3绑定/执行协议,而是使用简单查询协议并在本地进行参数替换。因此,虽然在Python中使用参数化语句,但在PostgreSQL中没有使用参数化语句。我在上面错误地说psycopg2中的参数化状态可以解决这个问题 演示程序从PostgreSQL日志中运行以下SQL:
[(1, 1)]
因此,您需要绕过类型适配器注册来调用str
的原始底层适配器。这将,尽管它很丑陋:
def adapt_str_strict(thestr):
return psycopg2.extensions.AsIs('TEXT ' + psycopg2.extensions.adapt(thestr))
psycopg2.extensions.register_adapter(str, adapt_str_strict)
用它运行您的演示,您将获得:
def adapt_str_strict(thestr):
return psycopg2.extensions.AsIs('TEXT ' + str(psycopg2.extensions.QuotedString(thestr)))
psycopg2.extensions.register_adapter(str, adapt_str_strict)
(顺便说一句,使用服务器端的
PREPARE
和EXECUTE
是行不通的,因为在通过psycopg2
将值传递给EXECUTE
时,你会遇到同样的键入问题。)那么,为什么你要在SQL中使用字符文字作为数字呢?@a_horse_,一个猜测,SQL构建风格,使用INSERT-INTO-foo(val)值(%s)
和客户端替换…你们都错了,这里的查询只是一些示例,但使用参数化语句(正在使用