@@ -183,6 +183,17 @@ restore_selectivities(List *clauselist,
183183return lst ;
184184}
185185
186+ static bool
187+ HasNeverVisitedNodes (PlanState * ps ,void * context )
188+ {
189+ Assert (context == NULL );
190+
191+ InstrEndLoop (ps -> instrument );
192+ if (ps -> instrument == NULL || ps -> instrument -> nloops == 0 )
193+ return true;
194+
195+ return planstate_tree_walker (ps ,HasNeverVisitedNodes ,NULL );
196+ }
186197/*
187198 * Walks over obtained PlanState tree, collects relation objects with their
188199 * clauses, selectivities and relids and passes each object to learn_sample.
@@ -233,7 +244,6 @@ learnOnPlanState(PlanState *p, void *context)
233244double learn_rows = 0. ;
234245double predicted = 0. ;
235246
236- InstrEndLoop (p -> instrument );
237247if (p -> instrument -> nloops > 0. )
238248{
239249/* If we can strongly calculate produced rows, do it. */
@@ -269,41 +279,42 @@ learnOnPlanState(PlanState *p, void *context)
269279
270280if (p -> plan -> predicted_cardinality > 0. )
271281predicted = p -> plan -> predicted_cardinality ;
272- else if (p -> plan -> parallel_aware ||
273- (p -> plan -> path_parallel_workers > 0 &&
274- (nodeTag (p -> plan )== T_HashJoin ||
275- nodeTag (p -> plan )== T_MergeJoin ||
276- nodeTag (p -> plan )== T_NestLoop )))
282+ else if (IsParallelTuplesProcessing (p -> plan ))
277283predicted = p -> plan -> plan_rows *
278284get_parallel_divisor (p -> plan -> path_parallel_workers );
279285else
280286predicted = p -> plan -> plan_rows ;
281287
282288/* It is needed for correct exp(result) calculation. */
289+ predicted = clamp_row_est (predicted );
283290learn_rows = clamp_row_est (learn_rows );
284291}
285292else
286293{
287294/*
288- * LAV: I found only one case for this code: if query returns
289- * with error. May be we will process this case and not learn
290- * AQO on the query?
295+ * LAV: I found two cases for this code:
296+ * 1. if query returns with error.
297+ * 2. plan node has never visited.
298+ * Both cases can't be used to learning AQO because give an
299+ * incorrect number of rows.
291300 */
292- learn_rows = 1. ;
301+ elog ( PANIC , "AQO: impossible situation" ) ;
293302}
294303
295- cardinality_sum_errors += fabs ( log ( predicted ) -
296- log (learn_rows ));
304+ Assert ( predicted >= 1 && learn_rows >= 1 );
305+ cardinality_sum_errors += fabs ( log ( predicted ) - log (learn_rows ));
297306cardinality_num_objects += 1 ;
298307
299308/*
300309 * A subtree was not visited. In this case we can not teach AQO
301310 * because ntuples value is equal to 0 and we will got
302311 * learn rows == 1.
303- * It is falseteaching, because at another place of a plan
304- *scanning of the node may produce many tuples.
312+ * It is falseknowledge: at another place of a plan, scanning of
313+ * the node may produce many tuples.
305314 */
306- if (ctx -> learn && p -> instrument -> nloops >=1 )
315+ Assert (p -> instrument -> nloops >=1 );
316+
317+ if (ctx -> learn )
307318learn_sample (SubplanCtx .clauselist ,SubplanCtx .selectivities ,
308319p -> plan -> path_relids ,learn_rows ,predicted );
309320}
@@ -327,16 +338,18 @@ update_query_stat_row(double *et, int *et_size,
327338double cardinality_error ,
328339int64 * n_exec )
329340{
330- int i ;
341+ int i ;
331342
332- if (cardinality_error >=0 )
333- {
334- if (* ce_size >=aqo_stat_size )
343+ /*
344+ * If plan contains one or more "never visited" nodes, cardinality_error
345+ * have -1 value and will be written to the knowledge base. User can use it
346+ * as a sign that AQO ignores this query.
347+ */
348+ if (* ce_size >=aqo_stat_size )
335349for (i = 1 ;i < aqo_stat_size ;++ i )
336350ce [i - 1 ]= ce [i ];
337351* ce_size = (* ce_size >=aqo_stat_size ) ?aqo_stat_size : (* ce_size + 1 );
338352ce [* ce_size - 1 ]= cardinality_error ;
339- }
340353
341354if (* et_size >=aqo_stat_size )
342355for (i = 1 ;i < aqo_stat_size ;++ i )
@@ -367,9 +380,12 @@ void
367380aqo_ExecutorStart (QueryDesc * queryDesc ,int eflags )
368381{
369382instr_time current_time ;
383+ bool use_aqo ;
384+
385+ use_aqo = !IsParallelWorker ()&& (query_context .use_aqo ||
386+ query_context .learn_aqo || force_collect_stat );
370387
371- if (!IsParallelWorker ()&&
372- (query_context .use_aqo || query_context .learn_aqo || force_collect_stat ))
388+ if (use_aqo )
373389{
374390INSTR_TIME_SET_CURRENT (current_time );
375391INSTR_TIME_SUBTRACT (current_time ,query_context .query_starttime );
@@ -391,8 +407,8 @@ aqo_ExecutorStart(QueryDesc *queryDesc, int eflags)
391407standard_ExecutorStart (queryDesc ,eflags );
392408
393409/* Plan state has initialized */
394-
395- StorePlanInternals (queryDesc );
410+ if ( use_aqo )
411+ StorePlanInternals (queryDesc );
396412}
397413
398414/*
@@ -403,12 +419,15 @@ aqo_ExecutorStart(QueryDesc *queryDesc, int eflags)
403419void
404420aqo_ExecutorEnd (QueryDesc * queryDesc )
405421{
406- double totaltime ;
407- double cardinality_error ;
408- QueryStat * stat = NULL ;
409- instr_time endtime ;
422+ double totaltime ;
423+ double cardinality_error ;
424+ QueryStat * stat = NULL ;
425+ instr_time endtime ;
410426EphemeralNamedRelation enr = get_ENR (queryDesc -> queryEnv ,PlanStateInfo );
411427
428+ cardinality_sum_errors = 0. ;
429+ cardinality_num_objects = 0 ;
430+
412431if (!ExtractFromQueryContext (queryDesc ))
413432/* AQO keep all query-related preferences at the query context.
414433 * It is needed to prevent from possible recursive changes, at
@@ -418,8 +437,7 @@ aqo_ExecutorEnd(QueryDesc *queryDesc)
418437 */
419438gotoend ;
420439
421- if (enr )
422- njoins = * (int * )enr -> reldata ;
440+ njoins = (enr != NULL ) ?* (int * )enr -> reldata :-1 ;
423441
424442Assert (!IsParallelWorker ());
425443
@@ -429,13 +447,11 @@ aqo_ExecutorEnd(QueryDesc *queryDesc)
429447query_context .collect_stat = false;
430448}
431449
432- if (query_context .learn_aqo || query_context .collect_stat )
450+ if ((query_context .learn_aqo || query_context .collect_stat )&&
451+ !HasNeverVisitedNodes (queryDesc -> planstate ,NULL ))
433452{
434453aqo_obj_stat ctx = {NIL ,NIL ,NIL ,query_context .learn_aqo };
435454
436- cardinality_sum_errors = 0. ;
437- cardinality_num_objects = 0 ;
438-
439455learnOnPlanState (queryDesc -> planstate , (void * )& ctx );
440456list_free (ctx .clauselist );
441457list_free (ctx .relidslist );
@@ -448,8 +464,7 @@ aqo_ExecutorEnd(QueryDesc *queryDesc)
448464INSTR_TIME_SUBTRACT (endtime ,query_context .query_starttime );
449465totaltime = INSTR_TIME_GET_DOUBLE (endtime );
450466if (cardinality_num_objects > 0 )
451- cardinality_error = cardinality_sum_errors /
452- cardinality_num_objects ;
467+ cardinality_error = cardinality_sum_errors /cardinality_num_objects ;
453468else
454469cardinality_error = -1 ;
455470
@@ -668,6 +683,12 @@ RemoveFromQueryContext(QueryDesc *queryDesc)
668683unregister_ENR (queryDesc -> queryEnv ,AQOPrivateData );
669684pfree (enr -> reldata );
670685pfree (enr );
686+
687+ /* Remove the plan state internals */
688+ enr = get_ENR (queryDesc -> queryEnv ,PlanStateInfo );
689+ unregister_ENR (queryDesc -> queryEnv ,PlanStateInfo );
690+ pfree (enr -> reldata );
691+ pfree (enr );
671692}
672693
673694/*
@@ -705,13 +726,18 @@ void print_into_explain(PlannedStmt *plannedstmt, IntoClause *into,
705726ExplainPropertyText ("AQO mode" ,"LEARN" ,es );
706727break ;
707728case AQO_MODE_FROZEN :
708- ExplainPropertyText ("AQO mode" ,"FIXED " ,es );
729+ ExplainPropertyText ("AQO mode" ,"FROZEN " ,es );
709730break ;
710731default :
711732elog (ERROR ,"Bad AQO state" );
712733break ;
713734}
714735
736+ /*
737+ * Query hash provides an user the conveniently use of the AQO
738+ * auxiliary functions.
739+ */
740+ ExplainPropertyInteger ("Query hash" ,NULL ,query_context .query_hash ,es );
715741ExplainPropertyInteger ("JOINS" ,NULL ,njoins ,es );
716742}
717743query_context .explain_aqo = false;