@@ -9,8 +9,9 @@ SELECT true AS success FROM aqo_reset();
99SET aqo.wide_search = 'on';
1010SET aqo.mode = 'learn';
1111SET aqo.show_details = 'on';
12- set aqo.show_hash = 'off';
12+ SET aqo.show_hash = 'off';
1313SET aqo.min_neighbors_for_predicting = 1;
14+ SET aqo.predict_with_few_neighbors = 'off';
1415SET enable_nestloop = 'off';
1516SET enable_mergejoin = 'off';
1617SET enable_material = 'off';
@@ -553,9 +554,131 @@ WHERE str NOT LIKE 'Query Identifier%' and str NOT LIKE '%Memory%' and str NOT L
553554 JOINS: 2
554555(24 rows)
555556
557+ -- Next few test cases focus on fss corresponding to (x1 > ? AND x2 < ? AND x3 < ?). We will denote
558+ -- it by fss0. At this moment there is exactly one fs with (fs, fss0, dbid) record in aqo_data. We'll
559+ -- refer to it as fs0.
560+ -- Let's create another fs for fss0. We'll call this fs fs1. Since aqo.wide_search='on',
561+ -- aqo.min_neighbors_for_predicting=1, and there is (fs0, fss0, dbid) data record, AQO must be used here.
562+ SELECT str AS result
563+ FROM expln('
564+ SELECT * FROM A WHERE x1 > -100 AND x2 < 10 AND x3 < 10;') AS str
565+ WHERE str NOT LIKE 'Query Identifier%' and str NOT LIKE '%Memory%' and str NOT LIKE '%Sort Method%';
566+ result
567+ ----------------------------------------------------------------------
568+ Seq Scan on public.a (actual rows=100 loops=1)
569+ AQO: rows=20, error=-400%
570+ Output: x1, x2, x3
571+ Filter: ((a.x1 > '-100'::integer) AND (a.x2 < 10) AND (a.x3 < 10))
572+ Using aqo: true
573+ AQO mode: LEARN
574+ JOINS: 0
575+ (7 rows)
576+
577+ -- Now there are 2 data records for fss0: one for (fs0, fss0, dbid) and one for (fs1, fss0, dbid)
578+ -- We repeat previous query, but set aqo.min_neighbors_for_predicting to 2. Since aqo.predict_with_few_neighbors
579+ -- is 'off', AQO is obliged to use both data records for fss0.
580+ SET aqo.min_neighbors_for_predicting = 2;
581+ SELECT str AS result
582+ FROM expln('
583+ SELECT * FROM A WHERE x1 > 1 AND x2 < 10 AND x3 < 10;') AS str
584+ WHERE str NOT LIKE 'Query Identifier%' and str NOT LIKE '%Memory%' and str NOT LIKE '%Sort Method%';
585+ result
586+ --------------------------------------------------------
587+ Seq Scan on public.a (actual rows=80 loops=1)
588+ AQO: rows=77, error=-4%
589+ Output: x1, x2, x3
590+ Filter: ((a.x1 > 1) AND (a.x2 < 10) AND (a.x3 < 10))
591+ Rows Removed by Filter: 20
592+ Using aqo: true
593+ AQO mode: LEARN
594+ JOINS: 0
595+ (8 rows)
596+
597+ -- Now there are 3 data records for fss0: 1 for (fs0, fss0, dbid) and 2 for (fs1, fss0, dbid)
598+ -- Lastly, we run invoke query with previously unseen fs with fss0 feature subspace. AQO must use
599+ -- three data records from two neighbors for this one.
600+ SET aqo.min_neighbors_for_predicting = 3;
601+ SELECT str AS result
602+ FROM expln('
603+ SELECT x2 FROM A WHERE x1 > 3 AND x2 < 10 AND x3 < 10 GROUP BY(x2);') AS str
604+ WHERE str NOT LIKE 'Query Identifier%' and str NOT LIKE '%Memory%' and str NOT LIKE '%Sort Method%';
605+ result
606+ --------------------------------------------------------------
607+ HashAggregate (actual rows=6 loops=1)
608+ AQO not used
609+ Output: x2
610+ Group Key: a.x2
611+ -> Seq Scan on public.a (actual rows=60 loops=1)
612+ AQO: rows=71, error=15%
613+ Output: x1, x2, x3
614+ Filter: ((a.x1 > 3) AND (a.x2 < 10) AND (a.x3 < 10))
615+ Rows Removed by Filter: 40
616+ Using aqo: true
617+ AQO mode: LEARN
618+ JOINS: 0
619+ (12 rows)
620+
621+ -----
622+ DROP TABLE IF EXISTS t;
623+ NOTICE: table "t" does not exist, skipping
624+ CREATE TABLE t AS SELECT x, x AS y, x AS z FROM generate_series(1, 10000) x;
625+ ANALYZE t;
626+ SELECT true AS success FROM aqo_reset();
627+ success
628+ ---------
629+ t
630+ (1 row)
631+
632+ -- Test that when there are less records than aqo.min_neighbors_for_predicting for given (fs, fss, dbid)
633+ -- and aqo.predict_with_few_neighbors is off, those records have higher precedence for cardinality estimation
634+ -- than neighbors' records.
635+ SELECT str AS result
636+ FROM expln('
637+ select * from t where x <= 10000 and y <= 10000 and z <= 10000;') AS str
638+ WHERE str NOT LIKE 'Query Identifier%' and str NOT LIKE '%Memory%' and str NOT LIKE '%Sort Method%';
639+ result
640+ ------------------------------------------------------------------
641+ Seq Scan on public.t (actual rows=10000 loops=1)
642+ AQO not used
643+ Output: x, y, z
644+ Filter: ((t.x <= 10000) AND (t.y <= 10000) AND (t.z <= 10000))
645+ Using aqo: true
646+ AQO mode: LEARN
647+ JOINS: 0
648+ (7 rows)
649+
650+ DO
651+ $$
652+ BEGIN
653+ for counter in 1..20 loop
654+ EXECUTE format('explain analyze select *, 1 from t where x <= 1 and y <= 1 and z <= %L;', 10 * counter);
655+ EXECUTE format('explain analyze select *, 1 from t where x <= 1 and y <= %L and z <= 1;', 10 * counter);
656+ EXECUTE format('explain analyze select *, 1 from t where x <= %L and y <= 1 and z <= 1;', 10 * counter);
657+ end loop;
658+ END;
659+ $$ LANGUAGE PLPGSQL;
660+ -- AQO should predict ~1000 rows to indicate that the record from previous invocation was used.
661+ SELECT str AS result
662+ FROM expln('
663+ select * from t where x <= 10000 and y <= 10000 and z <= 10000;') AS str
664+ WHERE str NOT LIKE 'Query Identifier%' and str NOT LIKE '%Memory%' and str NOT LIKE '%Sort Method%';
665+ result
666+ ------------------------------------------------------------------
667+ Seq Scan on public.t (actual rows=10000 loops=1)
668+ AQO: rows=9987, error=-0%
669+ Output: x, y, z
670+ Filter: ((t.x <= 10000) AND (t.y <= 10000) AND (t.z <= 10000))
671+ Using aqo: true
672+ AQO mode: LEARN
673+ JOINS: 0
674+ (7 rows)
675+
556676RESET aqo.wide_search;
677+ RESET aqo.predict_with_few_neighbors;
678+ RESET aqo.min_neighbors_for_predicting;
557679DROP EXTENSION aqo CASCADE;
558680DROP TABLE a;
559681DROP TABLE b;
560682DROP TABLE c;
683+ DROP TABLE t;
561684DROP FUNCTION expln;
0 commit comments