summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Kreen2011-04-19 21:03:32 +0000
committerMarko Kreen2011-04-19 21:09:54 +0000
commit05c9944f8812606547e9fb51e8faddbdf2e21dab (patch)
tree5910f83348f577ec75876330242e4f376c7764fc
parent026f67b5861fa5844ef0e05ccea6d4d5356e57f5 (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.c47
-rw-r--r--sql/pgq/triggers/common.h2
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;
};