pgq.triggers: Fix potential rare crash.
authorMarko Kreen <markokr@gmail.com>
Tue, 15 Nov 2011 22:49:38 +0000 (00:49 +0200)
committerMarko Kreen <markokr@gmail.com>
Tue, 15 Nov 2011 22:53:59 +0000 (00:53 +0200)
The bad hash_search(HASH_ENTER) + SPI_execute() pattern
is used here also.  Fix it by always properly initializing
the info structure.

[backport from 3.0]

sql/pgq/triggers/common.c
sql/pgq/triggers/common.h

index 07e1abd331e0c6e4e696521c55985b9121890e4c..9beda5b425003c183ae66824860502d0e72f51a0 100644 (file)
@@ -212,9 +212,6 @@ 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);
@@ -245,11 +242,23 @@ fill_tbl_info(Relation rel, struct PgqTableInfo *info)
 }
 
 static void
-free_info(struct PgqTableInfo *info)
+clean_info(struct PgqTableInfo *info, bool found)
 {
-       pfree(info->table_name);
-       pfree(info->pkey_attno);
-       pfree((void *)info->pkey_list);
+       if (!found)
+               goto uninitialized;
+
+       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->table_name = NULL;
+       info->pkey_attno = NULL;
+       info->pkey_list = NULL;
+       info->n_pkeys = 0;
 }
 
 /*
@@ -281,9 +290,27 @@ pgq_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;
index 50fe834bdf95edf39892d403ae413360dc0f32a7..7c25c64b1212dea65ce060f293904152e4d107ba 100644 (file)
@@ -36,7 +36,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 */
 };
 
 /* common.c */