Re: Fwd: Core dump with nested CREATE TEMP TABLE

From: Jim Nasby <Jim(dot)Nasby(at)BlueTreble(dot)com>
To: Michael Paquier <michael(dot)paquier(at)gmail(dot)com>
Cc: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Pg Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: Fwd: Core dump with nested CREATE TEMP TABLE
Date: 2015-09-02 04:50:38
Message-ID: 55E6801E.8090000@BlueTreble.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 9/1/15 8:42 PM, Michael Paquier wrote:
>>test_factory is a jungle to me. Perhaps you could just extract a
>>self-contained test case? It does not matter if the file is long as
>>long as the problem can be easily reproduced.

Sorry, more info on what's happening here.

The idea behind test_factory is to allow you to register a command that
creates and returns test data (IE: INSERT INTO table VALUES( DEFAULT,
'my test data' ) RETURNING *). That insert statement is stored in a
table (_tf._test_factory). When this dynamic statement is executed, the
results will be stored in a specially named table (plpgsql variable
c_data_table_name).

When you call tf.get(), it first attempts to grab all the rows from
c_data_table_name. The first time you do this in a database, that table
won't exist. tg.get's exception block will create a temp table holding
the results of the stored statement (IE: INSERT INTO table ...).

Something else important here is that in crash.sql there is a nested
tf.get() call:

-- Invoice
, $$INSERT INTO invoice VALUES(
DEFAULT
, (tf.get( NULL::customer, 'insert' )).customer_id
, current_date
, current_date + 30
) RETURNING *$$

Note that calls tf.get for customer (which is a simple INSERT).

This is where stuff gets weird. If you run tf.get( NULL::customer,
'insert' ) you get a regular plpgsql error. If you simply run tf.get()
for invoices, *outside* of tap.results_eq(), you also only get a plpgsql
error. To trigger the assert, you must use tf.get( NULL::invoice, 'base'
) from within tap.results_eq(). That's the only way I've found to
trigger this.

AFAICT, that call stack looks like this:

results_eq opens a cursor to run $$SELECT * FROM tf.get( NULL::invoice,
'base' )$$

plpgsql does it's thing and eventually that statement begins execution
tf.get() does a few things then attempts to read from a non-existent
table. tf.get's outer block catches that exception and runs dynamic SQL
to create a temp table. That dynamic SQL contains (in part) this: ,
(tf.get( NULL::customer, 'insert' )).customer_id

*That* tf.get also attempts to read from a non-existent table and
*successfully* creates it's temp table. It then does
PERFORM _tf.table_create( c_data_table_name );

which fails due to a bug in _tf.table_create().

Now we have a second exception bubbling back up to the exception handler
of the second tf.get call, which goes up to the exception handler for
the first tf.get call. That call was in the process of creating a temp
table (invoice_003). The error continues up to the FETCH command in
results_eq(). The assert happens somewhere after here, and it's because
the refcount on that temp table (invoice_003) is unexpected. I'm tracing
through this scenario by hand right now to try and figure out exactly
when that assert blows up, but I know it's happening in
results_eq(refcursor, refcursor, text).

BTW, I just updated the crash branch to ensure that test_factory 0.1.1
is what gets installed.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Michael Paquier 2015-09-02 04:59:28 Re: Fwd: Core dump with nested CREATE TEMP TABLE
Previous Message Noah Misch 2015-09-02 04:43:33 Re: security labels on databases are bad for dump & restore