Merged in enordstr/backend-database/enordstr/cleanup-insert-state-on-error (pull request #133)

Cleanup insert state on error

Approved-by: RobAtticus NA <rob.kiefer@gmail.com>
Approved-by: Matvey Arye <cevian@gmail.com>
Approved-by: ci-vast
This commit is contained in:
enordstr NA 2017-03-22 14:32:37 +00:00
commit e4928c0835
3 changed files with 204 additions and 130 deletions

View File

@ -554,8 +554,10 @@ insert_main_table_trigger(PG_FUNCTION_ARGS)
InsertTriggerCtx *tctx = insert_trigger_ctx;
HeapTuple tuple;
TupleDesc tupdesc = trigdata->tg_relation->rd_att;
MemoryContext oldctx;
MemoryContext oldctx = NULL;
PG_TRY();
{
/* Check that this is called the way it should be */
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "Trigger not called by trigger manager");
@ -583,19 +585,37 @@ insert_main_table_trigger(PG_FUNCTION_ARGS)
*/
oldctx = MemoryContextSwitchTo(tctx->mctx);
if (tctx->sort == NULL)
{
/*
* Multi-tuple case, i.e., we must sort the tuples. Initialize the
* sort state and put the first tuple that we saved from the last row
* trigger in the same batch.
* sort state and put the first tuple that we saved from the last
* row trigger in the same batch.
*/
insert_trigger_ctx_sort_init(insert_trigger_ctx, tupdesc);
insert_trigger_ctx_tuplesort_put(insert_trigger_ctx, tctx->first_tuple, tupdesc);
}
/* The rest of the tuples in the batch are put into the sort state here */
/*
* The rest of the tuples in the batch are put into the sort state
* here
*/
insert_trigger_ctx_tuplesort_put(insert_trigger_ctx, tuple, tupdesc);
}
PG_CATCH();
{
if (oldctx != NULL)
MemoryContextSwitchTo(oldctx);
if (insert_trigger_ctx != NULL)
{
insert_trigger_ctx_free(tctx);
insert_trigger_ctx = NULL;
}
PG_RE_THROW();
}
PG_END_TRY();
MemoryContextSwitchTo(oldctx);
@ -610,6 +630,8 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
InsertTriggerCtx *tctx = insert_trigger_ctx;
char *insert_guard;
PG_TRY();
{
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "not called by trigger manager");
@ -618,13 +640,14 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
elog(ERROR, "Unsupported event for trigger");
/*
* This guard protects against calling insert_data() twice in the same
* transaction, which might otherwise cause a deadlock in case the second
* insert_data() involves a chunk that was inserted into in the first call
* to insert_data(). This is a temporary safe guard that should ideally be
* removed once chunk management has been refactored and improved to avoid
* such deadlocks. NOTE: In its current form, this safe guard unfortunately
* prohibits transactions involving INSERTs on two different hypertables.
* This guard protects against inserting twice in the same
* transaction, which might otherwise cause a deadlock in case the
* second insert involves a chunk that was targeted in the first
* insert. This is a temporary safe guard that should ideally be
* removed once chunk management has been refactored and improved to
* avoid such deadlocks. NOTE: In its current form, this safe guard
* unfortunately prohibits transactions involving INSERTs on two
* different hypertables.
*/
insert_guard = GetConfigOptionByName("io.insert_data_guard", NULL, true);
@ -638,7 +661,6 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
set_config_option("io.insert_data_guard", "on", PGC_USERSET, PGC_S_SESSION,
GUC_ACTION_LOCAL, true, 0, false);
if (tctx->sort == NULL)
{
/* Single-tuple batch fast path */
@ -649,7 +671,10 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
.tuple = tctx->first_tuple,
};
/* We must set the time attno because it was not set on the fast path */
/*
* We must set the time attno because it was not set on the fast
* path
*/
tctx->time_attno = get_attnum(trigdata->tg_relation->rd_id,
tctx->hypertable->time_column_name);
part = insert_trigger_ctx_lookup_partition(insert_trigger_ctx,
@ -685,8 +710,8 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
.desc = trigdata->tg_relation->rd_att,
/*
* Strip off the partition attribute from the tuple so that we
* do not add it to the chunk when we insert.
* Strip off the partition attribute from the tuple so
* that we do not add it to the chunk when we insert.
*/
.tuple = heap_modify_tuple(tuple, trigdata->tg_relation->rd_att, NULL, NULL, doreplace),
};
@ -704,6 +729,17 @@ insert_main_table_trigger_after(PG_FUNCTION_ARGS)
tuplesort_end(tctx->sort);
}
}
PG_CATCH();
{
if (insert_trigger_ctx != NULL)
{
insert_trigger_ctx_free(tctx);
insert_trigger_ctx = NULL;
}
PG_RE_THROW();
}
PG_END_TRY();
insert_trigger_ctx_free(tctx);
insert_trigger_ctx = NULL;

View File

@ -490,3 +490,29 @@ SELECT * FROM ONLY "testNs";
------------+-----------+----------+----------+----------+-------------
(0 rows)
CREATE TABLE error_test(time timestamp, temp float8, device text NOT NULL);
SELECT create_hypertable('error_test', 'time', 'device', 2);
create_hypertable
-------------------
(1 row)
INSERT INTO error_test VALUES ('Mon Mar 20 09:18:20.1 2017', 21.3, 'dev1');
\set ON_ERROR_STOP 0
-- generate insert error
INSERT INTO error_test VALUES ('Mon Mar 20 09:18:22.3 2017', 21.1, NULL);
ERROR: 23502: null value in column "device" violates not-null constraint
DETAIL: Failing row contains (Mon Mar 20 09:18:22.3 2017, 21.1, null).
SCHEMA NAME: _timescaledb_internal
TABLE NAME: _hyper_3_5_0_7_data
COLUMN NAME: device
LOCATION: ExecConstraints, execMain.c:1732
\set ON_ERROR_STOP 1
INSERT INTO error_test VALUES ('Mon Mar 20 09:18:25.7 2017', 22.4, 'dev2');
SELECT * FROM error_test;
time | temp | device
----------------------------+------+--------
Mon Mar 20 09:18:20.1 2017 | 21.3 | dev1
Mon Mar 20 09:18:25.7 2017 | 22.4 | dev2
(2 rows)

View File

@ -13,3 +13,15 @@ SELECT * FROM chunk_closing_test;
SELECT * FROM ONLY chunk_closing_test;
SELECT * FROM "testNs";
SELECT * FROM ONLY "testNs";
CREATE TABLE error_test(time timestamp, temp float8, device text NOT NULL);
SELECT create_hypertable('error_test', 'time', 'device', 2);
INSERT INTO error_test VALUES ('Mon Mar 20 09:18:20.1 2017', 21.3, 'dev1');
\set ON_ERROR_STOP 0
-- generate insert error
INSERT INTO error_test VALUES ('Mon Mar 20 09:18:22.3 2017', 21.1, NULL);
\set ON_ERROR_STOP 1
INSERT INTO error_test VALUES ('Mon Mar 20 09:18:25.7 2017', 22.4, 'dev2');
SELECT * FROM error_test;