Re: Fwd: Core dump with nested CREATE TEMP TABLE - Mailing list pgsql-hackers

From Jim Nasby
Subject Re: Fwd: Core dump with nested CREATE TEMP TABLE
Date
Msg-id 55E6801E.8090000@BlueTreble.com
Whole thread Raw
In response to Re: Fwd: Core dump with nested CREATE TEMP TABLE  (Michael Paquier <michael.paquier@gmail.com>)
List 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



pgsql-hackers by date:

Previous
From: Noah Misch
Date:
Subject: Re: security labels on databases are bad for dump & restore
Next
From: Michael Paquier
Date:
Subject: Re: Fwd: Core dump with nested CREATE TEMP TABLE