是否可以将refcursor作为参数直接传递给Npgsql上的FETCH?

时间:2019-05-13 12:45:06

标签: postgresql ado.net npgsql

在针对PostgreSQL使用Npgsql库时,我找不到任何方法将游标ref作为参数传递到FETCH命令中。这可能吗?

using (DbConnection connection = new NpgsqlConnection(connectionString))
{
    connection.Open();
    using (DbTransaction transaction = connection.BeginTransaction())
    {
        var outCursor = new NpgsqlParameter("cursor", NpgsqlTypes.NpgsqlDbType.Refcursor);
        outCursor.Direction = ParameterDirection.Output;

        // get a refcursor from somewhere
        using (DbCommand commandGet = connection.CreateCommand())
        {
            commandGet.CommandText = "get_cursor";
            commandGet.CommandType = CommandType.StoredProcedure;
            commandGet.Parameters.Add(outCursor);
            commandGet.Connection = connection;
            commandGet.ExecuteNonQuery();
        }

        // try to use it
        using (DbCommand commandFetch = connection.CreateCommand())
        {
            var inCursor = new NpgsqlParameter("cursor", NpgsqlTypes.NpgsqlDbType.Refcursor);
            inCursor.Direction = ParameterDirection.Input;
            inCursor.Value = outCursor.Value;

            // This commented out line using string interpolation works fine.
            // Can it be done with a parameter, as I'm trying to do below?
            //commandFetch.CommandText = $"FETCH 100 FROM \"{outCursor.Value}\"";

            // The same inCursor pattern used here works fine when the cursor is being passed
            // on to a function, but does not work for FETCH
            commandFetch.CommandText = $"FETCH 100 FROM :cursor";
            commandFetch.Parameters.Add(inCursor);

            commandFetch.Connection = connection;

            // This line fails for param-based version; 
            // works fine with string-interpolation version
            using (var reader = commandFetch.ExecuteReader())
            {
                while (reader.Read())
                {
                    int a = (int)reader[0];
                }
            }
        }

        // close it
        using (DbCommand commandClose = connection.CreateCommand())
        {
            // I would like to be able to pass the cursor as a true parameter here, too
            commandClose.CommandText = $"CLOSE \"{outCursor.Value}\"";
            commandClose.Connection = connection;
            var reader = commandClose.ExecuteNonQuery();
        }
    }
}

请注意注释掉的一行:我可以使代码正常工作,我只想找到一种使光标ref作为参数传递的方法。

我得到的例外是Npgsql.PostgresException : 42601: syntax error at or near "$1"

传递纯字符串值的参数(看起来可能可行...)也会失败,但有相同的例外。

如果将输入游标传递给函数,则上述从输出游标创建输入游标的模式也可以正常工作。

1 个答案:

答案 0 :(得分:0)

This Stack Overflow answer及其后续注释基本上可以回答,由于底层数据库的局限性,无法执行我试图做的事情(至少在没有创建和执行动态SQL的情况下)。

  

您不能在FETCH语句中使用变量。使用动态SQL:EXECUTE格式('FETCH ALL FROM%I',foo);

所以这不是Npgsql的限制,另一个答案中建议的解决方法也可以在Npgsql中应用。或者,您也可以只将字符串插入SQL中,尽管这在某种程度上“难看”(至少在我看来),但实际上是绝对安全的。

(在这种情况下-但是将值直接内插到SQL中通常是一个不好的想法,至少没有第二和第三种思考原因,以及关于是否即使在任何给定的有限用例中,在所有情况下都可以安全地抵御注入攻击可能的情况。)