Search code examples
rubypostgresqlsequel

How to print postgres "raise notice" output from Sequel?


Raise notice is typically used to debug PSQL scripts in postgres (link).

The docs say that there's some kind of support for printing notices when using the pg gem, but there's no info on how to use this proc, what it yields, possible (probable?) caveats etc.

Does anyone have a working code example for production and/or development? Ideally, I'm looking for a solution that allows PG notices to be printed out in development when Sequel logging is enabled.

When I do:

DB = Sequel.connect(
  ENV['DATABASE_URL'],
  notice_receiver: lambda{ |x| binding.pry }
)

the notice_receiver lambda never gets called once I execute a function that raises a notice. I.e

[1] pry(#<Psql::CalculateMasterBalancesTest>)> DB.select{ |o| Sequel.function(:emit_notice) }.first
I, [2017-05-17T16:51:56.746003 #23139]  INFO -- : (0.000335s) SELECT emit_notice() LIMIT 1
=> {:emit_notice=>""}

where emit notice is:

CREATE OR REPLACE FUNCTION emit_notice()
  RETURNS VOID AS $$

BEGIN
  RAISE NOTICE 'NOTICE ME!!!';
END;
$$ LANGUAGE plpgsql;

and it works from PgAdmin:

NOTICE:  NOTICE ME!!!
Total query runtime: 21 ms.
1 row retrieved.

UPDATE

Alejandro C gave a good working example, and it seems that notices don't get distributed with the notice_receiver hook. For example:

Sequel.connect(DB.opts.merge(:notice_receiver=>proc{|r| puts r.result_error_message})){ |db|
  db.do("BEGIN\nRAISE NOTICE 'foo';\nEND;")
}

prints nothing, and:

Sequel.connect(DB.opts.merge(:notice_receiver=>proc{|r| puts r.result_error_message})){ |db|
  db.do("BEGIN\nRAISE WARNING 'foo';\nEND;")
}

Prints

WARNING: foo

Since Sequel just calls set_notice_receiver from PG, I guess I should file a bug report with PG.

EDIT 2

Yet when I try things just with the PG gem I get

conn = PG.connect( :dbname => 'db_test', user: 'test', password: 'test', host: '127.0.0.1' )
conn.set_notice_receiver{|r| puts r.result_error_message }
conn.exec("SELECT emit_notice()")
NOTICE:  NOTICE ME!!!
=> #<PG::Result:0x0000000405ac18 status=PGRES_TUPLES_OK ntuples=1 nfields=1 cmd_tuples=1>

So at this point I'm a bit confused...

EDIT 3

Posted an issue GitHub...

EDIT 4

Ah, apparently there's another options you need to use, client_min_messages needs to be set to :notice as so:

DB = Sequel.connect(
  ENV['DATABASE_URL'],
  notice_receiver: proc{|r| puts r.result_error_message},
  client_min_messages: :notice
)

and this works


Solution

  • You pass in your own proc which gets the notice as a string. To have it trigger on notices and not just warnings and above, use client_min_messages. For example:

    a = nil
    Sequel.connect(
      DB.opts.merge(
          notice_receiver: proc{|r| a = r.result_error_message}, 
          client_min_messages: :notice)) { |db|
      db.do("BEGIN\nRAISE WARNING 'foo';\nEND;")
    }
    a == "WARNING:  foo\n" # true