Hi All,
Description of the problem on which, I was working on:
* New plan node types. There are some new node types in 8.4.
The graphical explain tool needs to learn about these, plus
we need new icons for them.
The new plan nodes are:
- Recursive Union
- Nested Loop Semi Join
- Nested Loop Anti Join
- Merge Semi Join
- Merge Anti Join
- Hash Semi Join
- Hash Anti Join
- CTE Scan
- WorkTable Scan
- HashSetOp Intersect
- HashSetOp Intersect All
- HashSetOp Except
- HashSetOp Except All
- HashSetOp ???
- WindowAgg
Please find the patch for that.
I have tried to draw icons for these node-types, but I find myself
a very bad artist. :p
I have also attached the queries, which I have used to test this.
--
Regards,
Ashesh Vashi
EnterpriseDB INDIA: http://www.enterprisedb.com
-- 1
-- CTE Scan
WITH RECURSIVE a(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM a WHERE i < 10) SELECT * FROM a;
-- 2
-- Nested Loop Semi Join
SELECT * FROM (values(1)) AS a(i) WHERE i IN (values(1));
-- 3
-- Hash Semi Join
SET enable_nestloop = off;
SELECT * FROM (values(1)) AS a(i) WHERE i IN (values(1));
-- 4
-- Merge Semi Join
SET enable_nestloop = off;
SET enable_hAShjoin = off;
SELECT * FROM (values(1)) AS a(i) WHERE i IN (values(1));
RESET all;
-- 5
-- HashSetOp Intersect
SELECT * FROM (values(1)) AS a(i) INTERSECT (values(1));
-- 6
-- HashSetOp Intersect All
SELECT * FROM (values(1)) AS a(i) INTERSECT ALL (values(1));
-- 7
-- HashSetOp Except
SELECT * FROM (values(1)) AS a(i) EXCEPT (values(1));
-- 8
-- HashSetOp Except All
SELECT * FROM (values(1)) AS a(i) EXCEPT ALL (values(1));
-- 9
-- Hash Anti Join
CREATE TABLE NewPlanTestTbl1 AS (SELECT * FROM generate_series(1,1000) AS a(i));
ANALYZE NewPlanTestTbl1;
SELECT * FROM (SELECT * FROM generate_series(1,100)) AS a(i) WHERE NOT EXISTS (SELECT 1 FROM NewPlanTestTbl1 WHERE i = a.i);
-- 10
-- Merge Anti Join
SET enable_hAShjoin = off;
SELECT * FROM (SELECT * FROM generate_series(1,100)) AS a(i) WHERE NOT EXISTS (SELECT 1 FROM NewPlanTestTbl1 WHERE i = a.i);
-- 11
-- Nested Loop Anti Join
SET enable_hasHjoin = off;
SET enable_mergejoin = off;
SELECT * FROM (SELECT * FROM generate_series(1,100)) AS a(i) WHERE NOT EXISTS (SELECT 1 FROM NewPlanTestTbl1 WHERE i = a.i);
DROP TABLE NewPlanTestTbl1;
CREATE TABLE NewPlanTestTbl2 (
id INT PRIMARY KEY, -- department ID
parent_NewPlanTestTbl2 INT REFERENCES NewPlanTestTbl2, -- upper department ID
name TEXT -- department name
);
INSERT INTO NewPlanTestTbl2 VALUES (0, NULL, 'ROOT');
INSERT INTO NewPlanTestTbl2 VALUES (1, 0, 'A');
INSERT INTO NewPlanTestTbl2 VALUES (2, 1, 'B');
INSERT INTO NewPlanTestTbl2 VALUES (3, 2, 'C');
INSERT INTO NewPlanTestTbl2 VALUES (4, 2, 'D');
INSERT INTO NewPlanTestTbl2 VALUES (5, 0, 'E');
INSERT INTO NewPlanTestTbl2 VALUES (6, 4, 'F');
INSERT INTO NewPlanTestTbl2 VALUES (7, 5, 'G');
-- NewPlanTestTbl2 structure represented here is AS follows:
--
-- ROOT-+->A-+->B-+->C
-- | |
-- | +->D-+->F
-- +->E-+->G
-- 12
-- Recursive Union
-- WorkTable Scan
WITH RECURSIVE subdepartment AS
(
non recursive term
-- SELECT * FROM NewPlanTestTbl2 WHERE name = 'A'
-- UNION ALL
recursive term
-- SELECT d.* FROM NewPlanTestTbl2 AS d, subdepartment AS sd
WHERE d.parent_department = sd.id
)
SELECT * FROM subdepartment ORDER BY name;
DROP TABLE NewPlanTestTbl2;
-- Window Functions
-- WindowAgg Node Type
SELECT dname, empno, sal, avg(sal) OVER (PARTITION BY dname) FROM empsalary;
SELECT sal, sum(sal) OVER () FROM empsalary;
SELECT sal, sum(sal) OVER (ORDER BY sal) FROM empsalary;
SELECT
dname, empno, sal
FROM
(SELECT
dname, empno, sal,
rank() OVER (PARTITION BY dname ORDER BY sal DESC, empno) AS pos
FROM empsalary
) AS ss
WHERE pos < 3;
SELECT
sum(sal) OVER w, avg(sal) OVER w
FROM empsalary
WINDOW w AS (PARTITION BY dname ORDER BY sal DESC);
SELECT * FROM
( SELECT
ROW_NUMBER() OVER (ORDER BY empno ASC) AS row_number,
*
FROM emp
) AS foo
WHERE row_number <= 10;
SELECT * FROM (
SELECT
RANK() OVER (ORDER BY sal ASC) AS ranking,
empno,
ename,
sal
FROM emp
) AS foo
WHERE ranking <= 10;
DROP TABLE IF EXISTS WindowFuncTestTbl;
CREATE TABLE WindowFuncTestTbl(
id NUMERIC(2),
value NUMERIC(6,2)
);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (1,9);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (2,2.11);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (3,3.44);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (4,-4.21);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (8,123.45);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (9,98.23);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (10,938.23);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (11,984.23);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (12,198.23);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (13,928.87);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (14,25.37);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (15,918.3);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (16,9.23);
INSERT INTO WindowFuncTestTbl(ID, value) VALUES (17,8.23);
SELECT
id, value,
AVG(value) OVER(ORDER BY id) ma
FROM WindowFuncTestTbl
ORDER BY id;
DROP TABLE WindowFuncTestTbl;
CREATE TABLE WindowFuncTestTbl(
ID CHARACTER VARYING(32) NOT NULL PRIMARY KEY,
fname CHARACTER VARYING(80),
lname CHARACTER VARYING(80),
joindate DATE,
retiredate DATE,
sal NUMERIC(8,2),
city CHARACTER VARYING(80),
state CHARACTER VARYING(80),
desc CHARACTER VARYING(120)
);
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES ('01','Jason', 'Martin', to_date('19960725','YYYYMMDD'), to_date('20060725','YYYYMMDD'), 1234.56, 'Toronto', 'Programmer');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('02','Alison', 'Mathews', to_date('19760321','YYYYMMDD'), to_date('19860221','YYYYMMDD'), 6661.78, 'Vancouver', 'Tester');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('03','James', 'Smith', to_date('19781212','YYYYMMDD'), to_date('19900315','YYYYMMDD'), 6544.78, 'Vancouver', 'Tester');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('04','Celia', 'Rice', to_date('19821024','YYYYMMDD'), to_date('19990421','YYYYMMDD'), 2344.78, 'Vancouver', 'Manager');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('05','Robert', 'Black', to_date('19840115','YYYYMMDD'), to_date('19980808','YYYYMMDD'), 2334.78, 'Vancouver', 'Tester');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('06','Linda', 'Green', to_date('19870730','YYYYMMDD'), to_date('19960104','YYYYMMDD'), 4322.78, 'New York', 'Tester');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('07','David', 'Larry', to_date('19901231','YYYYMMDD'), to_date('19980212','YYYYMMDD'), 7897.78, 'New York', 'Manager');
INSERT INTO WindowFuncTestTbl(ID, fname, lname, joindate, retiredate, sal, city, desc) VALUES('08','James', 'Cat', to_date('19960917','YYYYMMDD'), to_date('20020415','YYYYMMDD'), 1232.78, 'Vancouver', 'Tester');
SELECT
city, SUM(sal) AS city_sal,
SUM(SUM(sal)) OVER
(ORDER BY city)
AS cumulative_sal
FROM WindowFuncTestTbl
GROUP BY city
ORDER BY city;
SELECT
city, SUM(sal) AS city_sal,
AVG(SUM(sal)) OVER
(ORDER BY city)
AS moving_average
FROM WindowFuncTestTbl
GROUP BY city
ORDER BY city;
SELECT id, sal,
AVG(sal) OVER(ORDER BY id) ma
FROM WindowFuncTestTbl
ORDER BY id;
SELECT id, sal,
AVG(sal) OVER(ORDER BY id) ma,
SUM(sal) OVER(ORDER BY id) sum,
(SUM(sal) OVER(ORDER BY id))/3 "Sum/3"
FROM WindowFuncTestTbl
ORDER BY id;
SELECT id, sal,
COUNT(sal) OVER(ORDER BY id)
FROM WindowFuncTestTbl
ORDER BY id;
SELECT id, sal,
AVG(sal) OVER(ORDER BY id) ma,
FIRST_VALUE(sal) OVER(ORDER BY id) first,
LAST_VALUE(sal) OVER(ORDER BY id) last
FROM WindowFuncTestTbl
ORDER id;
-- clean the table
DROP TABLE WindowFuncTestTbl;
Index: ctl/explainShape.cpp
===================================================================
--- ctl/explainShape.cpp (revision 7529)
+++ ctl/explainShape.cpp (working copy)
@@ -45,6 +45,26 @@
#define BMP_BORDER 3
+
+// TODO::
+// Remove these defines, when appropriate xpm are generated for them
+#define ex_nested_loop_anti_join_xpm ex_join_xpm
+#define ex_nested_loop_semi_join_xpm ex_join_xpm
+#define ex_merge_anti_join ex_join_xpm
+#define ex_merge_semi_join ex_join_xpm
+#define ex_hash_anti_join_xpm ex_join_xpm
+#define ex_hash_semi_join_xpm ex_join_xpm
+#define ex_hashsetop_except_all_xpm ex_setop_xpm
+#define ex_hashsetop_except_xpm ex_setop_xpm
+#define ex_hashsetop_intersect_all_xpm ex_setop_xpm
+#define ex_hashsetop_intersect_xpm ex_setop_xpm
+#define ex_hashsetop_unknown_xpm ex_setop_xpm
+#define ex_worktable_scan_xpm ex_scan_xpm
+#define ex_cte_scan_xpm ex_scan_xpm
+#define ex_recursive_union_xpm ex_nested_xpm
+#define ex_windowagg_xpm ex_sort_xpm
+
+
ExplainShape::ExplainShape(const char *bmp[], const wxString &description, long tokenNo, long detailNo)
{
SetBitmap(wxBitmap(bmp));
@@ -149,6 +169,9 @@
wxString token = tokens.GetNextToken();
wxString token2 = tokens.GetNextToken();
wxString token3 = tokens.GetNextToken();
+ wxString token4;
+ if (tokens.HasMoreTokens())
+ token4 = tokens.GetNextToken();
wxString descr = costPos > 0 ? str.Left(costPos) : str;
// possible keywords can be found in postgresql/src/backend/commands/explain.c
@@ -157,20 +180,109 @@
else if (token == wxT("Trigger")) return 0;
else if (token == wxT("Result")) s = new ExplainShape(ex_result_xpm, descr);
else if (token == wxT("Append")) s = new ExplainShape(ex_append_xpm, descr);
- else if (token == wxT("Nested")) s = new ExplainShape(ex_nested_xpm, descr);
- else if (token == wxT("Merge")) s = new ExplainShape(ex_merge_xpm, descr);
+ else if (token == wxT("Nested"))
+ {
+ if (token2 == wxT("Loop") && token4 == wxT("Join"))
+ {
+ // Nested Loop Anti Join
+ if (token3 == wxT("Anti"))
+ {
+ s = new ExplainShape(ex_nested_loop_anti_join_xpm, descr);
+ }
+ // Nested Loop Semi Join
+ else
+ {
+ s = new ExplainShape(ex_nested_loop_semi_join_xpm, descr);
+ }
+ }
+ if (!s)
+ s = new ExplainShape(ex_nested_xpm, descr);
+ }
+ else if (token == wxT("Merge"))
+ {
+ if (token3 == wxT("Join"))
+ {
+ // Merge Anti Join
+ if (token2 == wxT("Anti"))
+ {
+ s = new ExplainShape(ex_merge_anti_join, descr);
+ }
+ // Merge Semi Join
+ else
+ {
+ s = new ExplainShape(ex_merge_semi_join, descr);
+ }
+ }
+ else
+ {
+ s = new ExplainShape(ex_merge_xpm, descr);
+ }
+ }
else if (token == wxT("Hash"))
{
if (token2 == wxT("Join"))
+ {
s = new ExplainShape(ex_join_xpm, descr);
+ }
else
{
if (token3 == wxT("Join"))
- s = new ExplainShape(ex_join_xpm, descr);
+ {
+ // Hash Anti Join
+ if (token2 == wxT("Anti"))
+ {
+ s = new ExplainShape(ex_hash_anti_join_xpm, descr);
+ }
+ // Hash Semi Join
+ else if (token2 == wxT("Semi"))
+ {
+ s = new ExplainShape(ex_hash_semi_join_xpm, descr);
+ }
+ else
+ {
+ s = new ExplainShape(ex_hash_xpm, descr);
+ }
+ }
else
+ {
s = new ExplainShape(ex_hash_xpm, descr);
+ }
}
}
+ else if (token == wxT("HashSetOp"))
+ {
+ if (token2 == wxT("Except"))
+ {
+ // HashSetOp Except ALL
+ if (token3 == wxT("ALL"))
+ {
+ s = new ExplainShape(ex_hashsetop_except_all_xpm, descr);
+ }
+ // HashSetOp Except
+ else
+ {
+ s = new ExplainShape(ex_hashsetop_except_xpm, descr);
+ }
+ }
+ else if (token2 == wxT("Intersect"))
+ {
+ // HashSetOp Intersect ALL
+ if (token3 == wxT("ALL"))
+ {
+ s = new ExplainShape(ex_hashsetop_intersect_all_xpm, descr);
+ }
+ // HashSetOp Intersect
+ else
+ {
+ s = new ExplainShape(ex_hashsetop_intersect_xpm, descr);
+ }
+ }
+ else
+ {
+ // HashSetOp ???
+ s = new ExplainShape(ex_hashsetop_unknown_xpm, descr);
+ }
+ }
else if (token == wxT("Subquery")) s = new ExplainShape(ex_subplan_xpm, descr, 0, 2);
else if (token == wxT("Function")) s = new ExplainShape(ex_result_xpm, descr, 0, 2);
else if (token == wxT("Materialize")) s = new ExplainShape(ex_materialize_xpm, descr);
@@ -180,10 +292,7 @@
s = new ExplainShape(ex_aggregate_xpm, descr);
else if (token == wxT("Unique")) s = new ExplainShape(ex_unique_xpm, descr);
else if (token == wxT("SetOp")) s = new ExplainShape(ex_setop_xpm, descr);
-
-
else if (token == wxT("Limit")) s = new ExplainShape(ex_limit_xpm, descr);
-
else if (token == wxT("Bitmap"))
{
if (token2 == wxT("Index")) s = new ExplainShape(ex_bmp_index_xpm, descr, 4, 3);
@@ -192,16 +301,29 @@
else if (token2 == wxT("Scan"))
{
if (token == wxT("Index"))
- {
- if (token3 == wxT("Backward"))
- s = new ExplainShape(ex_index_scan_xpm, descr, 4, 3);
- else
- s = new ExplainShape(ex_index_scan_xpm, descr, 3, 2);
- }
- else if (token == wxT("Tid")) s = new ExplainShape(ex_tid_scan_xpm, descr, 3, 2);
- else s = new ExplainShape(ex_scan_xpm, descr, 3, 2);
+ // Scan Index Backword
+ if (token3 == wxT("Backward"))
+ s = new ExplainShape(ex_index_scan_xpm, descr, 4, 3);
+ else
+ s = new ExplainShape(ex_index_scan_xpm, descr, 3, 2);
+ // Tid Scan
+ else if (token == wxT("Tid"))
+ s = new ExplainShape(ex_tid_scan_xpm, descr, 3, 2);
+ // WorkTable scan
+ else if (token == wxT("WorkTable"))
+ s = new ExplainShape(ex_worktable_scan_xpm, descr, 3, 2);
+ // CTE Scan
+ else if (token == wxT("CTE"))
+ s = new ExplainShape(ex_cte_scan_xpm, descr, 3, 2);
+ else
+ s = new ExplainShape(ex_scan_xpm, descr, 3, 2);
}
else if (token2 == wxT("Seek")) s = new ExplainShape(ex_seek_xpm, descr, 3, 2);
+ // Recursive Union
+ else if (token == wxT("Recursive") && token2 == wxT("Union"))
+ s = new ExplainShape(ex_recursive_union_xpm, descr);
+ else if (token == wxT("WindowAgg"))
+ s = new ExplainShape(ex_windowagg_xpm, descr);
if (!s)
s = new ExplainShape(ex_unknown_xpm, descr);
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers