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


--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to