question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SET LOCAL working as expected with multi-statement batch query but raising warning in Postgres

See original GitHub issue

Steps to reproduce

From the C# side, this code is working exactly as expected:

using var connection = new NpgsqlConnection(connectionString);
await connection.OpenAsync();

Console.WriteLine("*** SET LOCAL ***");

// with SET LOCAL, the SET persists to the end of the batch but does not leak into subsequent commands
Console.WriteLine(await ExecuteAsync("SET LOCAL lock_timeout = 12345; CREATE TEMPORARY TABLE temp_table (id INT); SHOW lock_timeout;")); // 12345ms
Console.WriteLine(await ExecuteAsync("SHOW lock_timeout;")); // 0
Console.WriteLine(await ExecuteAsync("SET LOCAL lock_timeout = 456; SELECT 'x'")); // 'x'
Console.WriteLine(await ExecuteAsync("SHOW lock_timeout")); // 0

Console.WriteLine("*** SET ***");

// with SET, the SET persists for the lifetime of the connection
Console.WriteLine(await ExecuteAsync("SET lock_timeout = 987; CREATE TEMPORARY TABLE temp_table2 (id INT); SHOW lock_timeout;")); // 987ms
Console.WriteLine(await ExecuteAsync("SHOW lock_timeout;")); // 987ms (leak!)
Console.WriteLine(await ExecuteAsync("SET lock_timeout = 654; SELECT 'x'")); // 'x'
Console.WriteLine(await ExecuteAsync("SHOW lock_timeout")); // 654ms (leak!)

async Task<string> ExecuteAsync(string sql)
{
    using var command = connection.CreateCommand();
    command.CommandText = sql;
    return (string)await command.ExecuteScalarAsync();
}

The issue

However, this same code logs a warning on the Postgres side that is visible in the postgres logs:

WARNING:  SET LOCAL can only be used in transaction blocks

Apologies in advance if this is just revealing my lack of understanding when it comes to Postgres functionality. It feels weird to me that working code would produce a warning and that the seeming alternative does not work as desired. This could also be an issue to report to Postgres, but I wanted to check here first because I know that Npgsql does some explicit handling of multi-statement queries (e. g. https://github.com/npgsql/npgsql/blob/main/src/Npgsql/SqlQueryParser.cs#L17) that I thought could be impacting this behavior.

Further technical details

Npgsql version: 5.0.4 PostgreSQL version: 12 Operating system: Windows 10

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:11 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
Brarcommented, May 2, 2021

can you post that as a response to Tom Lane’s message?

Done

Here’s the thread if you want to follow along: https://www.postgresql.org/message-id/flat/16988-58edba102adb5128%40postgresql.org

2reactions
Brarcommented, May 1, 2021

The issue is probably with extended query protocol.

What Tom Lane was simulating via psql is essentially sending the whole query string as simple query via PQexec. As he expected, this does not match what Npgsql is doing at the wire protocol level.

Npqsql uses the extended query protocol and sends the two queries separately without syncing in-between them in a way that resembles the new pipeline mode in libpq.

I’m not entirely sure if it is a Npgsql issue or a PostgreSQL issue but at least the problem can be reproduced via libpq (currently unreleased PG 14 version) with the following small program (for brevity I’ve omitted any error checking):

#include <libpq-fe.h>

int
main(int argc, char **argv)
{
    PGconn* conn;
    PGresult* res;

    conn = PQconnectdb("");
    PQenterPipelineMode(conn);
    PQsendQueryParams(conn, "SET LOCAL work_mem = 100;", 0, NULL, NULL, NULL, NULL, 1);
    PQsendQueryParams(conn, "SHOW work_mem;", 0, NULL, NULL, NULL, NULL, 1);
    PQpipelineSync(conn);
    res = PQgetResult(conn); // SET LOCAL => PGRES_COMMAND_OK
    PQclear(res);
    PQgetResult(conn); // NULL
    res = PQgetResult(conn); // SHOW => PGRES_TUPLES_OK
    fprintf(stdout, "%s\n", PQgetvalue(res, 0, 0));
    PQclear(res);
    PQgetResult(conn); // NULL
    res = PQgetResult(conn); // PGRES_PIPELINE_SYNC
    PQclear(res);
    PQfinish(conn);
    return 0;
}

Read more comments on GitHub >

github_iconTop Results From Across the Web

Work with multi-statement queries | BigQuery
A multi-statement query is a collection of SQL statements that you can execute in one request. With multi-statement queries you can run multiple...
Read more >
How to use batch in upsert data in postgres sql?
The query is working as expected. But I want to use batch in the query. How to use batch in the same query?...
Read more >
Troubleshoot SQL Statements
If a SQL statement returns an unexpected result or takes longer than expected to process, this page will help you troubleshoot the issue....
Read more >
Documentation: 15: 55.2. Message Flow
A warning message has been issued. The frontend should display the message but continue listening for ReadyForQuery or ErrorResponse.
Read more >
Azure Cosmos DB for PostgreSQL server parameters
When set to 'false', shards are visible to all client applications. citus.use_citus_managed_tables (boolean). Allow new local tables to be ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found