@@ -102,6 +102,7 @@ PG_FUNCTION_INFO_V1(aqo_queries_update);
102
102
PG_FUNCTION_INFO_V1 (aqo_reset );
103
103
PG_FUNCTION_INFO_V1 (aqo_cleanup );
104
104
PG_FUNCTION_INFO_V1 (aqo_drop_class );
105
+ PG_FUNCTION_INFO_V1 (aqo_cardinality_error );
105
106
106
107
107
108
bool
@@ -2203,3 +2204,95 @@ aqo_drop_class(PG_FUNCTION_ARGS)
2203
2204
2204
2205
PG_RETURN_INT32 (cnt );
2205
2206
}
2207
+
2208
+ typedef enum {
2209
+ AQE_NN = 0 , AQE_QUERYID , AQE_FS , AQE_CERROR , AQE_NEXECS , AQE_TOTAL_NCOLS
2210
+ } ce_output_order ;
2211
+
2212
+ /*
2213
+ * Show cardinality error gathered on last execution.
2214
+ * Skip entries with empty stat slots. XXX: is it possible?
2215
+ */
2216
+ Datum
2217
+ aqo_cardinality_error (PG_FUNCTION_ARGS )
2218
+ {
2219
+ bool controlled = PG_GETARG_BOOL (0 );
2220
+ ReturnSetInfo * rsinfo = (ReturnSetInfo * ) fcinfo -> resultinfo ;
2221
+ TupleDesc tupDesc ;
2222
+ MemoryContext per_query_ctx ;
2223
+ MemoryContext oldcontext ;
2224
+ Tuplestorestate * tupstore ;
2225
+ Datum values [AQE_TOTAL_NCOLS ];
2226
+ bool nulls [AQE_TOTAL_NCOLS ];
2227
+ HASH_SEQ_STATUS hash_seq ;
2228
+ QueriesEntry * qentry ;
2229
+ StatEntry * sentry ;
2230
+ int counter = 0 ;
2231
+
2232
+ /* check to see if caller supports us returning a tuplestore */
2233
+ if (rsinfo == NULL || !IsA (rsinfo , ReturnSetInfo ))
2234
+ ereport (ERROR ,
2235
+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
2236
+ errmsg ("set-valued function called in context that cannot accept a set" )));
2237
+ if (!(rsinfo -> allowedModes & SFRM_Materialize ))
2238
+ ereport (ERROR ,
2239
+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
2240
+ errmsg ("materialize mode required, but it is not allowed in this context" )));
2241
+
2242
+ /* Switch into long-lived context to construct returned data structures */
2243
+ per_query_ctx = rsinfo -> econtext -> ecxt_per_query_memory ;
2244
+ oldcontext = MemoryContextSwitchTo (per_query_ctx );
2245
+
2246
+ /* Build a tuple descriptor for our result type */
2247
+ if (get_call_result_type (fcinfo , NULL , & tupDesc ) != TYPEFUNC_COMPOSITE )
2248
+ elog (ERROR , "return type must be a row type" );
2249
+ Assert (tupDesc -> natts == AQE_TOTAL_NCOLS );
2250
+
2251
+ tupstore = tuplestore_begin_heap (true, false, work_mem );
2252
+ rsinfo -> returnMode = SFRM_Materialize ;
2253
+ rsinfo -> setResult = tupstore ;
2254
+ rsinfo -> setDesc = tupDesc ;
2255
+
2256
+ MemoryContextSwitchTo (oldcontext );
2257
+
2258
+ LWLockAcquire (& aqo_state -> queries_lock , LW_SHARED );
2259
+ LWLockAcquire (& aqo_state -> stat_lock , LW_SHARED );
2260
+
2261
+ hash_seq_init (& hash_seq , queries_htab );
2262
+ while ((qentry = hash_seq_search (& hash_seq )) != NULL )
2263
+ {
2264
+ bool found ;
2265
+ double * ce ;
2266
+ int64 nexecs ;
2267
+ int nvals ;
2268
+
2269
+ memset (nulls , 0 , AQE_TOTAL_NCOLS * sizeof (nulls [0 ]));
2270
+
2271
+ sentry = (StatEntry * ) hash_search (stat_htab , & qentry -> queryid ,
2272
+ HASH_FIND , & found );
2273
+ if (!found )
2274
+ /* Statistics not found by some reason. Just go further */
2275
+ continue ;
2276
+
2277
+ nvals = controlled ? sentry -> cur_stat_slot_aqo : sentry -> cur_stat_slot ;
2278
+ if (nvals == 0 )
2279
+ /* No one stat slot filled */
2280
+ continue ;
2281
+
2282
+ nexecs = controlled ? sentry -> execs_with_aqo : sentry -> execs_without_aqo ;
2283
+ ce = controlled ? sentry -> est_error_aqo : sentry -> est_error ;
2284
+
2285
+ values [AQE_NN ] = Int32GetDatum (counter ++ );
2286
+ values [AQE_QUERYID ] = Int64GetDatum (qentry -> queryid );
2287
+ values [AQE_FS ] = Int64GetDatum (qentry -> fs );
2288
+ values [AQE_NEXECS ] = Int64GetDatum (nexecs );
2289
+ values [AQE_CERROR ] = Float8GetDatum (ce [nvals - 1 ]);
2290
+ tuplestore_putvalues (tupstore , tupDesc , values , nulls );
2291
+ }
2292
+
2293
+ LWLockRelease (& aqo_state -> stat_lock );
2294
+ LWLockRelease (& aqo_state -> queries_lock );
2295
+
2296
+ tuplestore_donestoring (tupstore );
2297
+ return (Datum ) 0 ;
2298
+ }
0 commit comments