Npgsql 默认参数行为
Npgsql default parameter behaviour
如果我有一个更新语句,例如 update foo set bar = @bar, baz = @baz
,并创建一个缺少参数的命令,似乎更新将使用这些列的当前值。
我无法在 Npgsql 或 Postgresql 中找到这方面的文档 - 它是我无法依赖的受支持功能,还是只是发生了什么?
简单的例子:
using System;
using Npgsql;
namespace MissingParametersUpdate
{
static class Program
{
// you will need to have CREATE TABLE foo ( bar integer, baz integer )
static void Main(string[] args)
{
using (var connection = new NpgsqlConnection(args[0]))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = @"delete from foo";
command.ExecuteNonQuery();
}
using (var command = connection.CreateCommand())
{
command.CommandText = @"insert into foo ( bar, baz ) values ( 1, 2 ), (3, 4)";
command.ExecuteNonQuery();
}
DumpValues("Initial", connection);
// empty update
using (var command = connection.CreateCommand())
{
command.CommandText = @"update foo set bar = @bar, baz = @baz";
command.ExecuteNonQuery();
}
DumpValues("Empty Update", connection);
// update bar
using (var command = connection.CreateCommand())
{
command.CommandText = @"update foo set bar = @bar, baz = @baz";
command.Parameters.AddWithValue(@"bar", 42);
command.ExecuteNonQuery();
}
DumpValues("Update Bar", connection);
// update baz
using (var command = connection.CreateCommand())
{
command.CommandText = @"update foo set bar = @bar, baz = @baz";
command.Parameters.AddWithValue(@"baz", 12);
command.ExecuteNonQuery();
}
DumpValues("Update Baz", connection);
}
}
private static void DumpValues(string caption, NpgsqlConnection connection)
{
Console.WriteLine(caption);
using (var command = connection.CreateCommand())
{
command.CommandText = @"select bar, baz from foo";
using (var reader = command.ExecuteReader())
while (reader.Read())
Console.WriteLine(" (bar: {0}, baz: {1})", reader.GetInt32(0), reader.GetInt32(1));
}
Console.WriteLine();
}
}
}
这确实有点奇怪,这是怎么回事。
PostgreSQL 接受格式为 $1、$2 等的位置参数占位符。但是,命名占位符在 .NET 中有点标准,例如@bar,@baz。为了支持这一点,Npgsql 解析您的 SQL 客户端以查找任何参数占位符(例如 @bar
)。当找到一个时,它会在 NpgsqlCommand 上查找具有相应名称的 NpgsqlParameter,并将其替换为 PostgreSQL 兼容的位置占位符(例如 $1)。
现在,如果 Npgsql 遇到一个占位符 而没有 相应的 NpgsqlParameter,它只会不理会它。在某些时候它会抛出异常,但在某些情况下,Npgsql 的内部 SQL 解析器不够好,并且错误地将查询的某些部分识别为参数占位符。保留没有相应 NpgsqlParameter 的已识别占位符可解决此问题。
所有这些都是为了说明您的 PostgreSQL 接收文字 SQL update foo set bar = @bar, baz = @baz
,而 Npgsql 方面没有任何形式的操作。现在,PostgreSQL 将 @ 视为特殊字符 - 您可以将其定义为运算符(参见 https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html)。但是,默认情况下 @ 似乎根本不执行任何操作,因此您实际执行的操作是 运行 update foo set bar = bar, baz = baz
,这显然根本不执行任何操作。您可以通过执行 SELECT @foo
来查看 @ 行为 - PostgreSQL 将响应一个错误 column "foo" does not exist
.
所以这是 Npgsql 之间的相互作用,因为未设置参数而按原样保留查询,而 PostgreSQL 忽略 @.
如果我有一个更新语句,例如 update foo set bar = @bar, baz = @baz
,并创建一个缺少参数的命令,似乎更新将使用这些列的当前值。
我无法在 Npgsql 或 Postgresql 中找到这方面的文档 - 它是我无法依赖的受支持功能,还是只是发生了什么?
简单的例子:
using System;
using Npgsql;
namespace MissingParametersUpdate
{
static class Program
{
// you will need to have CREATE TABLE foo ( bar integer, baz integer )
static void Main(string[] args)
{
using (var connection = new NpgsqlConnection(args[0]))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = @"delete from foo";
command.ExecuteNonQuery();
}
using (var command = connection.CreateCommand())
{
command.CommandText = @"insert into foo ( bar, baz ) values ( 1, 2 ), (3, 4)";
command.ExecuteNonQuery();
}
DumpValues("Initial", connection);
// empty update
using (var command = connection.CreateCommand())
{
command.CommandText = @"update foo set bar = @bar, baz = @baz";
command.ExecuteNonQuery();
}
DumpValues("Empty Update", connection);
// update bar
using (var command = connection.CreateCommand())
{
command.CommandText = @"update foo set bar = @bar, baz = @baz";
command.Parameters.AddWithValue(@"bar", 42);
command.ExecuteNonQuery();
}
DumpValues("Update Bar", connection);
// update baz
using (var command = connection.CreateCommand())
{
command.CommandText = @"update foo set bar = @bar, baz = @baz";
command.Parameters.AddWithValue(@"baz", 12);
command.ExecuteNonQuery();
}
DumpValues("Update Baz", connection);
}
}
private static void DumpValues(string caption, NpgsqlConnection connection)
{
Console.WriteLine(caption);
using (var command = connection.CreateCommand())
{
command.CommandText = @"select bar, baz from foo";
using (var reader = command.ExecuteReader())
while (reader.Read())
Console.WriteLine(" (bar: {0}, baz: {1})", reader.GetInt32(0), reader.GetInt32(1));
}
Console.WriteLine();
}
}
}
这确实有点奇怪,这是怎么回事。
PostgreSQL 接受格式为 $1、$2 等的位置参数占位符。但是,命名占位符在 .NET 中有点标准,例如@bar,@baz。为了支持这一点,Npgsql 解析您的 SQL 客户端以查找任何参数占位符(例如 @bar
)。当找到一个时,它会在 NpgsqlCommand 上查找具有相应名称的 NpgsqlParameter,并将其替换为 PostgreSQL 兼容的位置占位符(例如 $1)。
现在,如果 Npgsql 遇到一个占位符 而没有 相应的 NpgsqlParameter,它只会不理会它。在某些时候它会抛出异常,但在某些情况下,Npgsql 的内部 SQL 解析器不够好,并且错误地将查询的某些部分识别为参数占位符。保留没有相应 NpgsqlParameter 的已识别占位符可解决此问题。
所有这些都是为了说明您的 PostgreSQL 接收文字 SQL update foo set bar = @bar, baz = @baz
,而 Npgsql 方面没有任何形式的操作。现在,PostgreSQL 将 @ 视为特殊字符 - 您可以将其定义为运算符(参见 https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html)。但是,默认情况下 @ 似乎根本不执行任何操作,因此您实际执行的操作是 运行 update foo set bar = bar, baz = baz
,这显然根本不执行任何操作。您可以通过执行 SELECT @foo
来查看 @ 行为 - PostgreSQL 将响应一个错误 column "foo" does not exist
.
所以这是 Npgsql 之间的相互作用,因为未设置参数而按原样保留查询,而 PostgreSQL 忽略 @.