问题
I'm trying to add a value to an enumerated type in postgreSQL-9.6, and am having trouble figuring out what I'm doing wrong.
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();
I tried both the commented out code and the current version, and both resulted in an exception. The exception details are:
$exception {"42601: syntax error at or near \"$1\""}
I understand the $1 is in reference to the text being passed through the "a" parameter, but I don't understand why there is a problem or how to fix it. PostgreSQL's documentation says that ALTER TYPE cannot be performed in a transaction block, but as far as I can tell Npgsql doesn't automatically start transactions, so that shouldn't be a problem. If I perform a different SQL command with similar syntax, such as:
var tc = new NpgsqlCommand(@"INSERT INTO test VALUES (:a)", conn);
the program works flawlessly. Also, if I type the command directly into the psql shell, like this:
ALTER TYPE attributeName ADD VALUE IF NOT EXISTS 'test';
it works as expected. Can anybody help me understand what I'm doing wrong? Thanks.
回答1:
I might be wrong, but I think it can't be achieved for the same reason a table name cannot be passed as a query parameter.
However, you can use a string replacement for it:
string name = "test";
var tc = new NpgsqlCommand($"ALTER TYPE attributeName ADD VALUE IF NOT EXISTS '{name}'", conn);
Please keep in mind it's not a secure approach!
UPDATE:
Another solution would be usage of a stored procedure that executes the command. However, you cannot simply call the ALTER TYPE ... ADD VALUE ...
command inside a procedure/function because it doesn't work inside transaction blocks. You would get the following error:
"ALTER TYPE ... ADD cannot be executed from a function or multi-command string"
This thread should shed some light on the issue: Problems with ENUM type manipulation in 9.1
However, there might be a solution for your problem. It looks like you're trying to modify the existing enum type (add a new value). You can create a function that operates on the pg_enum
table directly. The set of database functions, designed for enum types manipulation, can found here: PostgreSQL 8.3+, 9.1+ ALTER ENUM emulation: element addition/removal, transactions.
The function that adds a new value to existing enum looks as follows:
-- 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.';
Now, you can just call the stored procedure/function from your C# code:
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();
}
Please note, that the above function will fail if you try to add an existing value.
回答2:
I have no experience with npgsql, though might be totally wrong, but NpgsqlCommand seems to execute prepared statement. If so, you cant prepare ALTER statement:
statement
Any SELECT, INSERT, UPDATE, DELETE, or VALUES statement.
回答3:
PostgreSQL doesn't support parameter placeholders everywhere, and unless I'm mistaken it's specifically unsupported in DDL statements such as ALTER TYPE
. You will likely have to insert the value you want as a literal in your statement (be sure to take into account SQL injection).
来源:https://stackoverflow.com/questions/44011994/syntax-error-at-or-near-1-while-trying-to-alter-type-in-postgresql-9-6