# 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)
Run Code Online (Sandbox Code Playgroud)
这里,在插入和选择时,postgres 都会隐式地将带引号的字符串转换为整型,而不是引发类型错误,除非带引号的值非常明确地键入为 varchar:
# 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.
Run Code Online (Sandbox Code Playgroud)
这里的问题是针对没有隐式转换的动态类型语言(例如 Ruby 或 Python)
有没有办法禁用它并强制引用值始终为 varchar(除非显式转换)?
编辑:因为人们显然关注不相关的内容,这些查询来自参数化语句,psycopg2 会将字符串转换为带引号的值,然后将带引号的值转换回字符串,因此无论访问方法如何,不匹配都存在,这是一个红鲱鱼。这与参数化语句完全相同:
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()
Run Code Online (Sandbox Code Playgroud)
其输出:
[(1, 1)]
Run Code Online (Sandbox Code Playgroud)
不可以,您不能禁用带引号的文字到任何目标类型的隐式转换。PostgreSQL 认为此类文字是类型unknown,除非被强制转换或文字类型说明符覆盖,并且会转换为unknown任何类型。没有从unknown到 中的类型的转换pg_cast;这是隐含的。所以你不能丢掉它。
据我所知,PostgreSQL 遵循 SQL 规范,接受带引号的文字作为整数。
\n\n对于 PostgreSQL 的类型引擎,1是integer, ,\'1\'是unknown类型推断为integer传递给integer函数、运算符或字段的 if 的类型。您不能禁用类型推断unknown或强制unknown将其视为text不直接攻击解析器/查询规划器。
您应该做的是使用参数化语句,而不是将文字替换为 SQL。如果这样做,就不会遇到此问题,因为客户端类型是已知的或可以指定。那当然可以与 Python (psycopg2) 和 Ruby (Pg gem) 配合使用不符合我对 psycopg2 的想法,见下文。
问题澄清后更新:在此处描述的狭义情况下,psycopg2 的客户端参数化语句虽然正确,但不会产生原始发布者期望的结果。运行更新中的演示表明 psycopg2 没有使用 PostgreSQL 的 v3 绑定/执行协议,它使用简单查询协议并在本地进行参数替换。因此,当您在 Python 中使用参数化语句时,您不会在 PostgreSQL 中使用参数化语句。我在上面说 psycopg2 中的参数化语句可以解决这个问题,这是错误的。
\n\n该演示从 PostgreSQL 日志运行此 SQL:
\n\n< 2014-07-07 18:17:24.450 WST >LOG: statement: INSERT INTO foo (val) VALUES (1), (\'2\')\n< 2014-07-07 18:17:24.451 WST >LOG: statement: SELECT * FROM foo WHERE id=\'1\'\nRun Code Online (Sandbox Code Playgroud)\n\n请注意缺少放置参数。它们被客户端取代。
\n\n所以如果你想要psycopg2更严格,则必须调整客户端框架。
psycopg2是可扩展的,所以这应该非常实用 - 您需要根据适应新类型str来重写,unicode和integer(或者,在 Python3 中,bytes,str和integer)的类型处理程序。甚至还有一个关于覆盖 psycopg2 处理的常见问题解答条目作为示例:http ://initd.org/psycopg/docs/faq.html#faq-floatpsycopg2.extrasfloat
由于无限递归, na\xc3\xafve 方法不起作用:
\n\ndef adapt_str_strict(thestr):\n return psycopg2.extensions.AsIs(\'TEXT \' + psycopg2.extensions.adapt(thestr))\n\npsycopg2.extensions.register_adapter(str, adapt_str_strict)\nRun Code Online (Sandbox Code Playgroud)\n\n因此您需要绕过类型适配器注册来调用原始底层适配器str. 这会的,尽管它很难看:
def adapt_str_strict(thestr):\n return psycopg2.extensions.AsIs(\'TEXT \' + str(psycopg2.extensions.QuotedString(thestr)))\n\npsycopg2.extensions.register_adapter(str, adapt_str_strict)\nRun Code Online (Sandbox Code Playgroud)\n\n运行你的演示,你会得到:
\n\npsycopg2.ProgrammingError: parameter $1 of type text cannot be coerced to the expected type integer\nHINT: You will need to rewrite or cast the expression.\nRun Code Online (Sandbox Code Playgroud)\n\n(顺便说一句,使用服务器端PREPARE是EXECUTE行不通的,因为在将值传递给EXECUTEvia时,您只会遇到相同的键入问题psycopg2时您只会遇到相同的键入问题)。
| 归档时间: |
|
| 查看次数: |
3199 次 |
| 最近记录: |