diff options
author | Marko Kreen | 2011-04-19 21:03:32 +0000 |
---|---|---|
committer | Marko Kreen | 2011-04-19 21:09:54 +0000 |
commit | 05c9944f8812606547e9fb51e8faddbdf2e21dab (patch) | |
tree | 5910f83348f577ec75876330242e4f376c7764fc | |
parent | 026f67b5861fa5844ef0e05ccea6d4d5356e57f5 (diff) |
pgq.triggers: Fix potential rare crash.
The bad hash_search(HASH_ENTER) + SPI_execute() pattern
is used here also. Fix it by always properly initializing
the info structure.
The similar pattern exist also in PgqTriggerInfo, but that
is already handled via ->finalized flag.
-rw-r--r-- | sql/pgq/triggers/common.c | 47 | ||||
-rw-r--r-- | sql/pgq/triggers/common.h | 2 |
2 files changed, 39 insertions, 10 deletions
diff --git a/sql/pgq/triggers/common.c b/sql/pgq/triggers/common.c index 4bbdd9b2..676c0fc2 100644 --- a/sql/pgq/triggers/common.c +++ b/sql/pgq/triggers/common.c @@ -275,9 +275,6 @@ static void fill_tbl_info(Relation rel, struct PgqTableInfo *info) bool isnull; int res, i, attno; - /* allow reset ASAP, but ignore it in this call */ - info->invalid = false; - /* load pkeys */ values[0] = ObjectIdGetDatum(rel->rd_id); res = SPI_execute_plan(pkey_plan, values, NULL, false, 0); @@ -308,10 +305,14 @@ static void fill_tbl_info(Relation rel, struct PgqTableInfo *info) info->tg_cache = NULL; } -static void free_info(struct PgqTableInfo *info) +static void clean_info(struct PgqTableInfo *info, bool found) { struct PgqTriggerInfo *tg, *tmp = info->tg_cache; int i; + + if (!found) + goto uninitialized; + for (tg = info->tg_cache; tg; ) { tmp = tg->next; if (tg->ignore_list) @@ -325,9 +326,20 @@ static void free_info(struct PgqTableInfo *info) pfree(tg); tg = tmp; } - pfree(info->table_name); - pfree(info->pkey_attno); - pfree((void *)info->pkey_list); + if (info->table_name) + pfree(info->table_name); + if (info->pkey_attno) + pfree(info->pkey_attno); + if (info->pkey_list) + pfree((void *)info->pkey_list); + +uninitialized: + info->tg_cache = NULL; + info->table_name = NULL; + info->pkey_attno = NULL; + info->pkey_list = NULL; + info->n_pkeys = 0; + info->invalid = true; } /* @@ -358,9 +370,26 @@ static struct PgqTableInfo *find_table_info(Relation rel) entry = hash_search(tbl_cache_map, &rel->rd_id, HASH_ENTER, &found); if (!found || entry->invalid) { - if (found) - free_info(entry); + clean_info(entry, found); + + /* + * During fill_tbl_info() 2 events can happen: + * - table info reset + * - exception + * To survive both, always clean struct and tag + * as invalid but differently from reset. + */ + entry->invalid = 2; + + /* find info */ fill_tbl_info(rel, entry); + + /* + * If no reset happened, it's valid. Actual reset + * is postponed to next call. + */ + if (entry->invalid == 2) + entry->invalid = false; } return entry; diff --git a/sql/pgq/triggers/common.h b/sql/pgq/triggers/common.h index 310002ca..0192c493 100644 --- a/sql/pgq/triggers/common.h +++ b/sql/pgq/triggers/common.h @@ -73,7 +73,7 @@ struct PgqTableInfo { const char *pkey_list; /* pk column name list */ int *pkey_attno; /* pk column positions */ char *table_name; /* schema-quelified table name */ - bool invalid; /* set if the info was invalidated */ + int invalid; /* set if the info was invalidated */ struct PgqTriggerInfo *tg_cache; }; |