Skip to content

Commit 7f436b1

Browse files
authored
Merge pull request #54 from mfenniak/non-const-crash
Fix crash conditions in EXPLAIN
2 parents 62ac39e + d75e4e4 commit 7f436b1

File tree

7 files changed

+155
-118
lines changed

7 files changed

+155
-118
lines changed

flake.nix

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
install -D multicorn.control -t $out/share/postgresql/extension
7575
runHook postInstall
7676
'';
77+
78+
separateDebugInfo = true;
7779
};
7880

7981
makeMulticornPythonPackage = target_python: target_postgresql: target_python.pkgs.buildPythonPackage rec {
@@ -100,6 +102,8 @@
100102
'';
101103

102104
nativeBuildInputs = [ target_postgresql ];
105+
106+
separateDebugInfo = true;
103107
};
104108

105109
makePostgresWithPlPython = test_python: test_postgresql:

python/multicorn/testfdw.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(self, options, columns):
2020
self._modify_batch_size = int(options.get('modify_batch_size', 1))
2121
self._row_id_column = options.get('row_id_column',
2222
list(self.columns.keys())[0])
23+
self.noisy_explain = options.get('noisy_explain', 'False').lower() == 'true'
2324
log_to_postgres(str(sorted(options.items())))
2425
log_to_postgres(str(sorted([(key, column.type_name) for key, column in
2526
columns.items()])))
@@ -105,6 +106,15 @@ def execute(self, quals, columns, sortkeys=None):
105106
reverse=k.is_reversed)
106107
return self._as_generator(quals, columns)
107108

109+
def explain(self, quals, columns, sortkeys=None, verbose=False):
110+
if self.noisy_explain:
111+
log_to_postgres("EXPLAIN quals=%r" % (sorted(quals),))
112+
log_to_postgres("EXPLAIN columns=%r" % (sorted(columns),))
113+
log_to_postgres("EXPLAIN sortkeys=%r" % (sortkeys,))
114+
log_to_postgres("EXPLAIN verbose=%r" % (verbose,))
115+
return ['EXPLAIN ROW 1', 'EXPLAIN ROW 2']
116+
return []
117+
108118
def get_rel_size(self, quals, columns):
109119
if self.test_type == 'planner':
110120
return (10000000, len(columns) * 10)

src/python.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -948,11 +948,17 @@ execute(ForeignScanState *node, ExplainState *es)
948948
newqual->base.isArray = qual->isArray;
949949
newqual->base.useOr = qual->useOr;
950950

951-
#if PG_VERSION_NUM >= 100000
952-
newqual->value = ExecEvalExpr(expr_state, econtext, &isNull);
953-
#else
954-
newqual->value = ExecEvalExpr(expr_state, econtext, &isNull, NULL);
955-
#endif
951+
// Don't attempt to evaluate the expression if we're running an EXPLAIN
952+
if (es == NULL)
953+
{
954+
expr_state = ExecInitExpr(((MulticornParamQual *) qual)->expr,
955+
(PlanState *) node);
956+
#if PG_VERSION_NUM >= 100000
957+
newqual->value = ExecEvalExpr(expr_state, econtext, &isNull);
958+
#else
959+
newqual->value = ExecEvalExpr(expr_state, econtext, &isNull, NULL);
960+
#endif
961+
}
956962
newqual->base.typeoid = ((Param*) ((MulticornParamQual *) qual)->expr)->paramtype;
957963
newqual->isnull = isNull;
958964
break;

test-3.9/expected/multicorn_planner_test.out

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ CREATE server multicorn_srv foreign data wrapper multicorn options (
1212
wrapper 'multicorn.testfdw.TestForeignDataWrapper'
1313
);
1414
CREATE user mapping FOR current_user server multicorn_srv options (usermapping 'test');
15-
-- Test for two thing: first, that when a low total row count,
15+
-- Test for two thing: first, that when a low total row count,
1616
-- a full seq scan is used on a join.
1717
CREATE foreign table testmulticorn (
1818
test1 character varying,
@@ -31,7 +31,7 @@ NOTICE: [('test1', 'character varying'), ('test2', 'character varying')]
3131
explain select * from testmulticorn m1 inner join testmulticorn m2 on m1.test1 = m2.test1;
3232
QUERY PLAN
3333
-------------------------------------------------------------------------------------
34-
Nested Loop (cost=20.00..806.05 rows=2 width=128)
34+
Nested Loop (cost=20.00..806.05 rows=20 width=128)
3535
Join Filter: ((m1.test1)::text = (m2.test1)::text)
3636
-> Foreign Scan on testmulticorn m1 (cost=10.00..400.00 rows=20 width=20)
3737
-> Materialize (cost=10.00..400.10 rows=20 width=20)
@@ -49,40 +49,92 @@ explain select * from testmulticorn m1 left outer join testmulticorn m2 on m1.te
4949
(5 rows)
5050

5151
DROP foreign table testmulticorn;
52-
-- Second, when a total row count is high
52+
-- Second, when a total row count is high
5353
-- a parameterized path is used on the test1 attribute.
5454
CREATE foreign table testmulticorn (
5555
test1 character varying,
5656
test2 character varying
5757
) server multicorn_srv options (
5858
option1 'option1',
59-
test_type 'planner'
59+
test_type 'planner',
60+
noisy_explain 'true'
6061
);
6162
explain select * from testmulticorn;
62-
NOTICE: [('option1', 'option1'), ('test_type', 'planner'), ('usermapping', 'test')]
63+
NOTICE: [('noisy_explain', 'true'), ('option1', 'option1'), ('test_type', 'planner'), ('usermapping', 'test')]
6364
NOTICE: [('test1', 'character varying'), ('test2', 'character varying')]
65+
NOTICE: EXPLAIN quals=[]
66+
NOTICE: EXPLAIN columns=['test1', 'test2']
67+
NOTICE: EXPLAIN sortkeys=None
68+
NOTICE: EXPLAIN verbose=False
6469
QUERY PLAN
6570
----------------------------------------------------------------------------------
6671
Foreign Scan on testmulticorn (cost=10.00..200000000.00 rows=10000000 width=20)
67-
(1 row)
72+
Multicorn: EXPLAIN ROW 1
73+
Multicorn: EXPLAIN ROW 2
74+
(3 rows)
6875

6976
explain select * from testmulticorn m1 inner join testmulticorn m2 on m1.test1 = m2.test1;
77+
NOTICE: EXPLAIN quals=[]
78+
NOTICE: EXPLAIN columns=['test1', 'test2']
79+
NOTICE: EXPLAIN sortkeys=None
80+
NOTICE: EXPLAIN verbose=False
81+
NOTICE: EXPLAIN quals=[test1 = ?]
82+
NOTICE: EXPLAIN columns=['test1', 'test2']
83+
NOTICE: EXPLAIN sortkeys=None
84+
NOTICE: EXPLAIN verbose=False
7085
QUERY PLAN
7186
-------------------------------------------------------------------------------------------
7287
Nested Loop (cost=20.00..400100000.00 rows=500000000000 width=128)
7388
-> Foreign Scan on testmulticorn m1 (cost=10.00..200000000.00 rows=10000000 width=20)
89+
Multicorn: EXPLAIN ROW 1
90+
Multicorn: EXPLAIN ROW 2
7491
-> Foreign Scan on testmulticorn m2 (cost=10.00..20.00 rows=1 width=20)
7592
Filter: ((m1.test1)::text = (test1)::text)
76-
(4 rows)
93+
Multicorn: EXPLAIN ROW 1
94+
Multicorn: EXPLAIN ROW 2
95+
(8 rows)
7796

7897
explain select * from testmulticorn m1 left outer join testmulticorn m2 on m1.test1 = m2.test1;
98+
NOTICE: EXPLAIN quals=[]
99+
NOTICE: EXPLAIN columns=['test1', 'test2']
100+
NOTICE: EXPLAIN sortkeys=None
101+
NOTICE: EXPLAIN verbose=False
102+
NOTICE: EXPLAIN quals=[test1 = ?]
103+
NOTICE: EXPLAIN columns=['test1', 'test2']
104+
NOTICE: EXPLAIN sortkeys=None
105+
NOTICE: EXPLAIN verbose=False
79106
QUERY PLAN
80107
-------------------------------------------------------------------------------------------
81108
Nested Loop Left Join (cost=20.00..400100000.00 rows=500000000000 width=128)
82109
-> Foreign Scan on testmulticorn m1 (cost=10.00..200000000.00 rows=10000000 width=20)
110+
Multicorn: EXPLAIN ROW 1
111+
Multicorn: EXPLAIN ROW 2
83112
-> Foreign Scan on testmulticorn m2 (cost=10.00..20.00 rows=1 width=20)
84113
Filter: ((m1.test1)::text = (test1)::text)
85-
(4 rows)
114+
Multicorn: EXPLAIN ROW 1
115+
Multicorn: EXPLAIN ROW 2
116+
(8 rows)
117+
118+
explain select * from testmulticorn m1 left outer join testmulticorn m2 on upper(m1.test1) = m2.test1;
119+
NOTICE: EXPLAIN quals=[]
120+
NOTICE: EXPLAIN columns=['test1', 'test2']
121+
NOTICE: EXPLAIN sortkeys=None
122+
NOTICE: EXPLAIN verbose=False
123+
NOTICE: EXPLAIN quals=[test1 = ?]
124+
NOTICE: EXPLAIN columns=['test1', 'test2']
125+
NOTICE: EXPLAIN sortkeys=None
126+
NOTICE: EXPLAIN verbose=False
127+
QUERY PLAN
128+
-------------------------------------------------------------------------------------------
129+
Nested Loop Left Join (cost=20.00..400100000.00 rows=500000000000 width=128)
130+
-> Foreign Scan on testmulticorn m1 (cost=10.00..200000000.00 rows=10000000 width=20)
131+
Multicorn: EXPLAIN ROW 1
132+
Multicorn: EXPLAIN ROW 2
133+
-> Foreign Scan on testmulticorn m2 (cost=10.00..20.00 rows=1 width=20)
134+
Filter: (upper((m1.test1)::text) = (test1)::text)
135+
Multicorn: EXPLAIN ROW 1
136+
Multicorn: EXPLAIN ROW 2
137+
(8 rows)
86138

87139
DROP USER MAPPING FOR current_user SERVER multicorn_srv;
88140
DROP EXTENSION multicorn cascade;

test-3.9/expected/multicorn_planner_test_1.out

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ CREATE server multicorn_srv foreign data wrapper multicorn options (
1212
wrapper 'multicorn.testfdw.TestForeignDataWrapper'
1313
);
1414
CREATE user mapping FOR current_user server multicorn_srv options (usermapping 'test');
15-
-- Test for two thing: first, that when a low total row count,
15+
-- Test for two thing: first, that when a low total row count,
1616
-- a full seq scan is used on a join.
1717
CREATE foreign table testmulticorn (
1818
test1 character varying,
@@ -49,40 +49,95 @@ explain select * from testmulticorn m1 left outer join testmulticorn m2 on m1.te
4949
(5 rows)
5050

5151
DROP foreign table testmulticorn;
52-
-- Second, when a total row count is high
52+
-- Second, when a total row count is high
5353
-- a parameterized path is used on the test1 attribute.
5454
CREATE foreign table testmulticorn (
5555
test1 character varying,
5656
test2 character varying
5757
) server multicorn_srv options (
5858
option1 'option1',
59-
test_type 'planner'
59+
test_type 'planner',
60+
noisy_explain 'true'
6061
);
6162
explain select * from testmulticorn;
62-
NOTICE: [('option1', 'option1'), ('test_type', 'planner'), ('usermapping', 'test')]
63+
NOTICE: [('noisy_explain', 'true'), ('option1', 'option1'), ('test_type', 'planner'), ('usermapping', 'test')]
6364
NOTICE: [('test1', 'character varying'), ('test2', 'character varying')]
65+
NOTICE: EXPLAIN quals=[]
66+
NOTICE: EXPLAIN columns=['test1', 'test2']
67+
NOTICE: EXPLAIN sortkeys=None
68+
NOTICE: EXPLAIN verbose=False
6469
QUERY PLAN
6570
----------------------------------------------------------------------------------
6671
Foreign Scan on testmulticorn (cost=10.00..200000000.00 rows=10000000 width=20)
67-
(1 row)
72+
Multicorn: EXPLAIN ROW 1
73+
Multicorn: EXPLAIN ROW 2
74+
(3 rows)
6875

6976
explain select * from testmulticorn m1 inner join testmulticorn m2 on m1.test1 = m2.test1;
77+
NOTICE: EXPLAIN quals=[]
78+
NOTICE: EXPLAIN columns=['test1', 'test2']
79+
NOTICE: EXPLAIN sortkeys=None
80+
NOTICE: EXPLAIN verbose=False
81+
NOTICE: EXPLAIN quals=[test1 = ?]
82+
NOTICE: EXPLAIN columns=['test1', 'test2']
83+
NOTICE: EXPLAIN sortkeys=None
84+
NOTICE: EXPLAIN verbose=False
7085
QUERY PLAN
7186
-------------------------------------------------------------------------------------------
72-
Nested Loop (cost=20.00..400100000.00 rows=500000000000 width=128)
87+
Nested Loop (cost=20.00..400125000.00 rows=500000000000 width=128)
88+
Join Filter: ((m1.test1)::text = (m2.test1)::text)
7389
-> Foreign Scan on testmulticorn m1 (cost=10.00..200000000.00 rows=10000000 width=20)
90+
Multicorn: EXPLAIN ROW 1
91+
Multicorn: EXPLAIN ROW 2
7492
-> Foreign Scan on testmulticorn m2 (cost=10.00..20.00 rows=1 width=20)
7593
Filter: ((m1.test1)::text = (test1)::text)
76-
(4 rows)
94+
Multicorn: EXPLAIN ROW 1
95+
Multicorn: EXPLAIN ROW 2
96+
(9 rows)
7797

7898
explain select * from testmulticorn m1 left outer join testmulticorn m2 on m1.test1 = m2.test1;
99+
NOTICE: EXPLAIN quals=[]
100+
NOTICE: EXPLAIN columns=['test1', 'test2']
101+
NOTICE: EXPLAIN sortkeys=None
102+
NOTICE: EXPLAIN verbose=False
103+
NOTICE: EXPLAIN quals=[test1 = ?]
104+
NOTICE: EXPLAIN columns=['test1', 'test2']
105+
NOTICE: EXPLAIN sortkeys=None
106+
NOTICE: EXPLAIN verbose=False
79107
QUERY PLAN
80108
-------------------------------------------------------------------------------------------
81-
Nested Loop Left Join (cost=20.00..400100000.00 rows=500000000000 width=128)
109+
Nested Loop Left Join (cost=20.00..400125000.00 rows=500000000000 width=128)
110+
Join Filter: ((m1.test1)::text = (m2.test1)::text)
82111
-> Foreign Scan on testmulticorn m1 (cost=10.00..200000000.00 rows=10000000 width=20)
112+
Multicorn: EXPLAIN ROW 1
113+
Multicorn: EXPLAIN ROW 2
83114
-> Foreign Scan on testmulticorn m2 (cost=10.00..20.00 rows=1 width=20)
84115
Filter: ((m1.test1)::text = (test1)::text)
85-
(4 rows)
116+
Multicorn: EXPLAIN ROW 1
117+
Multicorn: EXPLAIN ROW 2
118+
(9 rows)
119+
120+
explain select * from testmulticorn m1 left outer join testmulticorn m2 on upper(m1.test1) = m2.test1;
121+
NOTICE: EXPLAIN quals=[]
122+
NOTICE: EXPLAIN columns=['test1', 'test2']
123+
NOTICE: EXPLAIN sortkeys=None
124+
NOTICE: EXPLAIN verbose=False
125+
NOTICE: EXPLAIN quals=[test1 = ?]
126+
NOTICE: EXPLAIN columns=['test1', 'test2']
127+
NOTICE: EXPLAIN sortkeys=None
128+
NOTICE: EXPLAIN verbose=False
129+
QUERY PLAN
130+
-------------------------------------------------------------------------------------------
131+
Nested Loop Left Join (cost=20.00..400150000.00 rows=500000000000 width=128)
132+
Join Filter: (upper((m1.test1)::text) = (m2.test1)::text)
133+
-> Foreign Scan on testmulticorn m1 (cost=10.00..200000000.00 rows=10000000 width=20)
134+
Multicorn: EXPLAIN ROW 1
135+
Multicorn: EXPLAIN ROW 2
136+
-> Foreign Scan on testmulticorn m2 (cost=10.00..20.00 rows=1 width=20)
137+
Filter: (upper((m1.test1)::text) = (test1)::text)
138+
Multicorn: EXPLAIN ROW 1
139+
Multicorn: EXPLAIN ROW 2
140+
(9 rows)
86141

87142
DROP USER MAPPING FOR current_user SERVER multicorn_srv;
88143
DROP EXTENSION multicorn cascade;

test-3.9/expected/multicorn_planner_test_2.out

Lines changed: 0 additions & 93 deletions
This file was deleted.

0 commit comments

Comments
 (0)