问题
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