尝试更改 postgreSQL-9.6 中的类型时语法错误等于或接近 $1
Syntax error at or near $1 while trying to alter type in postgreSQL-9.6
我正在尝试向 postgreSQL-9.6 中的枚举类型添加一个值,但我无法弄清楚我做错了什么。
var tc = new NpgsqlCommand(@"ALTER TYPE attributeName ADD VALUE IF NOT EXISTS
:a", conn);
//tc.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Text));
//tc.Parameters[0].Value = "test";
tc.Parameters.AddWithValue("a", NpgsqlDbType.Text, "test");
tc.ExecuteNonQuery();
我尝试了注释掉的代码和当前版本,都导致了异常。异常详细信息为:
$exception {"42601: syntax error at or near \"\""}
我知道 $1 是指通过 "a" 参数传递的文本,但我不明白为什么会出现问题或如何解决问题。 PostgreSQL 的文档说 ALTER TYPE 不能在事务块中执行,但据我所知 Npgsql 不会自动启动事务,所以这应该不是问题。如果我执行具有相似语法的不同 SQL 命令,例如:
var tc = new NpgsqlCommand(@"INSERT INTO test VALUES (:a)", conn);
程序运行完美。另外,如果我直接在 psql shell 中键入命令,就像这样:
ALTER TYPE attributeName ADD VALUE IF NOT EXISTS 'test';
它按预期工作。谁能帮我理解我做错了什么?谢谢
我没有使用 npgsql 的经验,虽然可能完全错误,但是 NpgsqlCommand seems to execute prepared statement. If so, you cant prepare ALTER 语句:
statement
Any SELECT, INSERT, UPDATE, DELETE, or VALUES statement.
我可能是错的,但我认为它无法实现,原因与无法将 table 名称作为查询参数传递一样。
但是,您可以使用字符串替换它:
string name = "test";
var tc = new NpgsqlCommand($"ALTER TYPE attributeName ADD VALUE IF NOT EXISTS '{name}'", conn);
请记住,这不是一种安全的方法!
更新:
另一种解决方案是使用执行命令的存储过程。但是,您不能简单地在 procedure/function 内调用 ALTER TYPE ... ADD VALUE ...
命令,因为它在事务块内不起作用。您将收到以下错误:
"ALTER TYPE ... ADD cannot be executed from a function or
multi-command string"
这个帖子应该可以阐明这个问题:Problems with ENUM type manipulation in 9.1
但是,您的问题可能有解决方案。看起来您正在尝试修改现有的枚举类型(添加新值)。您可以创建一个直接在 pg_enum
table 上运行的函数。专为枚举类型操作而设计的数据库函数集可在此处找到:PostgreSQL 8.3+, 9.1+ ALTER ENUM emulation: element addition/removal, transactions.
向现有枚举添加新值的函数如下所示:
-- Also works within transactions in PostgreSQL 9.1+ (but you need
-- to reconnect to the database after transaction commit, because
-- new enum items are not be visible within the session you used
-- to add them).
--
-- See http://en.dklab.ru/lib/dklab_postgresql_enum/
--
-- (C) Dmitry Koterov, 2013
-- This code is BSD licensed.
--
CREATE SCHEMA enum AUTHORIZATION postgres;
SET search_path = enum, pg_catalog;
SET check_function_bodies = false;
CREATE OR REPLACE FUNCTION enum.enum_add (
enum_name varchar,
enum_elem varchar
)
RETURNS void AS
$body$
DECLARE
eoid OID;
has_sortorder BOOLEAN;
BEGIN
eoid := (
SELECT pg_type.oid
FROM pg_type JOIN pg_namespace ON pg_namespace.oid=pg_type.typnamespace
WHERE typtype='e' AND enum_name IN(typname, nspname||'.'||typname)
);
has_sortorder := EXISTS(
select 1
from pg_attribute
where attrelid=(select oid from pg_class where relname='pg_enum') and attname='enumsortorder'
);
IF has_sortorder THEN
EXECUTE '
INSERT INTO pg_enum(enumtypid, enumlabel, enumsortorder) VALUES(
'||eoid||',
'||quote_literal(enum_elem)||',
(SELECT MAX(enumsortorder) + 1 FROM pg_enum WHERE enumtypid='||eoid||')
)
';
ELSE
EXECUTE E'INSERT INTO pg_enum(enumtypid, enumlabel) VALUES('||eoid||', '||quote_literal(enum_elem)||')';
END IF;
END;
$body$
LANGUAGE 'plpgsql';
COMMENT ON FUNCTION enum.enum_add (enum_name character varying, enum_elem character varying) IS 'Inserts a new ENUM element wthout re-creating the whole type.';
现在,您只需从 C# 代码中调用存储的 procedure/function:
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = @"enum.enum_add";
cmd.Parameters.AddWithValue("enum_name", "attributeName");
cmd.Parameters.AddWithValue("enum_elem", "O'Reilly");
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
}
请注意,如果您尝试添加现有值,上述功能将失败。
PostgreSQL 并非在任何地方都支持参数占位符,除非我记错了,否则它在 ALTER TYPE
等 DDL 语句中特别不受支持。您可能必须在语句中插入您想要的值作为文字(一定要考虑 SQL 注入)。
我正在尝试向 postgreSQL-9.6 中的枚举类型添加一个值,但我无法弄清楚我做错了什么。
var tc = new NpgsqlCommand(@"ALTER TYPE attributeName ADD VALUE IF NOT EXISTS
:a", conn);
//tc.Parameters.Add(new NpgsqlParameter("a", NpgsqlDbType.Text));
//tc.Parameters[0].Value = "test";
tc.Parameters.AddWithValue("a", NpgsqlDbType.Text, "test");
tc.ExecuteNonQuery();
我尝试了注释掉的代码和当前版本,都导致了异常。异常详细信息为:
$exception {"42601: syntax error at or near \"\""}
我知道 $1 是指通过 "a" 参数传递的文本,但我不明白为什么会出现问题或如何解决问题。 PostgreSQL 的文档说 ALTER TYPE 不能在事务块中执行,但据我所知 Npgsql 不会自动启动事务,所以这应该不是问题。如果我执行具有相似语法的不同 SQL 命令,例如:
var tc = new NpgsqlCommand(@"INSERT INTO test VALUES (:a)", conn);
程序运行完美。另外,如果我直接在 psql shell 中键入命令,就像这样:
ALTER TYPE attributeName ADD VALUE IF NOT EXISTS 'test';
它按预期工作。谁能帮我理解我做错了什么?谢谢
我没有使用 npgsql 的经验,虽然可能完全错误,但是 NpgsqlCommand seems to execute prepared statement. If so, you cant prepare ALTER 语句:
statement
Any SELECT, INSERT, UPDATE, DELETE, or VALUES statement.
我可能是错的,但我认为它无法实现,原因与无法将 table 名称作为查询参数传递一样。
但是,您可以使用字符串替换它:
string name = "test";
var tc = new NpgsqlCommand($"ALTER TYPE attributeName ADD VALUE IF NOT EXISTS '{name}'", conn);
请记住,这不是一种安全的方法!
更新:
另一种解决方案是使用执行命令的存储过程。但是,您不能简单地在 procedure/function 内调用 ALTER TYPE ... ADD VALUE ...
命令,因为它在事务块内不起作用。您将收到以下错误:
"ALTER TYPE ... ADD cannot be executed from a function or multi-command string"
这个帖子应该可以阐明这个问题:Problems with ENUM type manipulation in 9.1
但是,您的问题可能有解决方案。看起来您正在尝试修改现有的枚举类型(添加新值)。您可以创建一个直接在 pg_enum
table 上运行的函数。专为枚举类型操作而设计的数据库函数集可在此处找到:PostgreSQL 8.3+, 9.1+ ALTER ENUM emulation: element addition/removal, transactions.
向现有枚举添加新值的函数如下所示:
-- Also works within transactions in PostgreSQL 9.1+ (but you need
-- to reconnect to the database after transaction commit, because
-- new enum items are not be visible within the session you used
-- to add them).
--
-- See http://en.dklab.ru/lib/dklab_postgresql_enum/
--
-- (C) Dmitry Koterov, 2013
-- This code is BSD licensed.
--
CREATE SCHEMA enum AUTHORIZATION postgres;
SET search_path = enum, pg_catalog;
SET check_function_bodies = false;
CREATE OR REPLACE FUNCTION enum.enum_add (
enum_name varchar,
enum_elem varchar
)
RETURNS void AS
$body$
DECLARE
eoid OID;
has_sortorder BOOLEAN;
BEGIN
eoid := (
SELECT pg_type.oid
FROM pg_type JOIN pg_namespace ON pg_namespace.oid=pg_type.typnamespace
WHERE typtype='e' AND enum_name IN(typname, nspname||'.'||typname)
);
has_sortorder := EXISTS(
select 1
from pg_attribute
where attrelid=(select oid from pg_class where relname='pg_enum') and attname='enumsortorder'
);
IF has_sortorder THEN
EXECUTE '
INSERT INTO pg_enum(enumtypid, enumlabel, enumsortorder) VALUES(
'||eoid||',
'||quote_literal(enum_elem)||',
(SELECT MAX(enumsortorder) + 1 FROM pg_enum WHERE enumtypid='||eoid||')
)
';
ELSE
EXECUTE E'INSERT INTO pg_enum(enumtypid, enumlabel) VALUES('||eoid||', '||quote_literal(enum_elem)||')';
END IF;
END;
$body$
LANGUAGE 'plpgsql';
COMMENT ON FUNCTION enum.enum_add (enum_name character varying, enum_elem character varying) IS 'Inserts a new ENUM element wthout re-creating the whole type.';
现在,您只需从 C# 代码中调用存储的 procedure/function:
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = @"enum.enum_add";
cmd.Parameters.AddWithValue("enum_name", "attributeName");
cmd.Parameters.AddWithValue("enum_elem", "O'Reilly");
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
}
请注意,如果您尝试添加现有值,上述功能将失败。
PostgreSQL 并非在任何地方都支持参数占位符,除非我记错了,否则它在 ALTER TYPE
等 DDL 语句中特别不受支持。您可能必须在语句中插入您想要的值作为文字(一定要考虑 SQL 注入)。