【问题标题】:psycopg2 throws unterminated dollar-quoted string error at or near "$body$"psycopg2 在 "$body$" 处或附近抛出未终止的美元引用字符串错误
【发布时间】:2019-04-13 05:26:05
【问题描述】:

我正在尝试构建一个 alembic 迁移修订版以在 PostgreSQL 服务器版本 10.2 中创建一个用户定义的函数,但是脚本不断抛出错误:

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) unterminated dollar-quoted string at or near "$body$
                select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'), '"
    LINE 6:         as $body$
                       ^
     [SQL: "\n        create or replace function naturalsort(text)\n          returns bytea\n          language sql\n          immutable strict\n        as $body$ \n            select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'), '\x00') from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r; \n        $body$;\n    "] (Background on this error at: http://sqlalche.me/e/f405)

我正在运行的脚本:

def upgrade():
    conn = op.get_bind()

    conn.execute('DROP FUNCTION IF EXISTS "naturalsort"')

    conn.execute("""
        create or replace function naturalsort(text)
          returns bytea
          language sql
          immutable strict
        as $body$ 
            select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'), '\x00') from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r; 
        $body$;
    """)

代码应该允许自然排序列并且是来自rhodium toad的复制/粘贴。

由于美元引号字符串是重写字符串常量的一种方法,因此替代方法是(注意 postgresql 文档中的双单引号:“要在字符串常量中包含单引号字符,请编写两个相邻的单引号,例如,'Dianne''s horse'。请注意,这与双引号字符(“)不同。”):

conn.execute("""
    create or replace function naturalsort(text)
      returns bytea
      language sql
      immutable strict
    as 'select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), ''SQL_ASCII''), ''\x00'') from regexp_matches($1, ''0*([0-9]+)|([^0-9]+)'', ''g'') r;';
""")

但是这会引发类似的错误:

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) unterminated quoted string at or near "'select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), ''SQL_ASCII''), ''"
LINE 6:         as 'select string_agg(convert_to(coalesce(r[2], leng...
                   ^
 [SQL: "\n        create or replace function naturalsort(text)\n          returns bytea\n          language sql\n          immutable strict\n        as 'select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), ''SQL_ASCII''), ''\x00'') from regexp_matches($1, ''0*([0-9]+)|([^0-9]+)'', ''g'') r;';\n    "] (Background on this error at: http://sqlalche.me/e/f405)

奇怪的是,这些查询在 pgAdmin 中执行得很好,而且 alembic 似乎生成了有效的 SQL(alembic 升级:--sql):

DROP FUNCTION IF EXISTS "naturalsort";

create or replace function naturalsort(text)
          returns bytea
          language sql
          immutable strict
        as $body$
            select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'), '') from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r;
        $body$;;

UPDATE alembic_version SET version_num='ad99fdcb28bc' WHERE alembic_version.version_num = 'ff00ac684617';

COMMIT;

任何关于为什么 psycopg2 不断抛出 unterminated dollar-quoted string at or near "$body$ 错误的线索?

【问题讨论】:

  • 您可能想尝试将$body$ 作为字符串参数传递给execute 方法,而不是在查询本身中。
  • @moshevi,感谢您的回复。你的意思是这样的?:conn.execute(""" create or replace function naturalsort(text) returns bytea language sql immutable strict as %s; """ % "$body$ select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'), '\x00') from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r; $body$") 不幸的是,运行这个仍然给我同样的错误(unterminated dollar-quoted string at or near "$body$)。

标签: python-3.x postgresql sqlalchemy psycopg2 alembic


【解决方案1】:
def upgrade():
    op.execute('DROP FUNCTION IF EXISTS "naturalsort"')

    op.execute("""
      create or replace function naturalsort(text)
      returns bytea
      language sql
      immutable strict
    as $my_body$
        select string_agg(convert_to(coalesce(r[2], length(length(r[1])::text) || length(r[1])::text || r[1]), 'SQL_ASCII'), '') from regexp_matches($1, '0*([0-9]+)|([^0-9]+)', 'g') r;
    $my_body$;;
    """)


def downgrade():
    op.execute('DROP FUNCTION IF EXISTS "naturalsort"')

执行升级时,它会成功且不会出现语法错误。 实际上在 postgres 中执行该函数也会使用此查询返回所需的自然排序结果:

SELECT * FROM public.requirement ORDER BY naturalsort(eid) asc

使用PvdLs 对my_body 的评论部分起作用,如果我在空字符串所在的位置添加'\x00'(在SQL_ASCII 之后),它将引发语法错误

【讨论】:

    【解决方案2】:

    $body 是一个不能包含 $ 的别名,将 $body$ 替换为类似 my_body 的名称,它应该可以工作

    【讨论】:

    • 不幸的是,这不起作用:sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) syntax error at or near "some_tag" LINE 6: as some_tag select string_agg(convert_to(coalesce(r... 我们正在使用美元引用的字符串常量来规避在内部 SQL 查询中使用引号。 §4.1.2.4 中描述了here
    猜你喜欢
    • 1970-01-01
    • 2016-06-14
    • 2013-09-26
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    相关资源
    最近更新 更多