我有以下非常简单的测试用例。它需要Postgresql 9.5.7,一个Mongo数据库并使用Mongo FDW扩展。使用只有一条记录的表将失败。以下是:
首先,在mongo shell中,运行以下命令来创建一些数据:
db.table2.insert({sessionKey:ObjectId('555233a2af8f312d060e57be'), catalog:'test'});
然后,在PSQL中运行以下内容:
CREATE TABLE table1 AS SELECT column1 AS sid
FROM (VALUES ('555233a2af8f312d060e57be'::name)) AS data;
CREATE EXTENSION mongo_fdw;
CREATE SERVER mongo_data FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address 'localhost', port '27017');
CREATE USER MAPPING FOR postgres SERVER mongo_data;
CREATE FOREIGN TABLE table2 (
"sessionKey" NAME,
"catalog" TEXT
) SERVER mongo_data OPTIONS (DATABASE 'test', COLLECTION 'table2');
运行以下查询时,服务器将抛出分段错误。
SELECT t1.sid, t2.catalog
FROM table1 t1
LEFT OUTER JOIN LATERAL (
SELECT catalog FROM table2 WHERE "sessionKey" = t1.sid LIMIT 1
) t2 ON TRUE;
但是,如果您运行的查询没有" LIMIT 1"它没有问题。
查看执行计划,唯一的区别是引入了过滤器表达式。
# explain SELECT t1.sid, t2.catalog
FROM table1 t1
LEFT OUTER JOIN LATERAL (
SELECT catalog FROM table2 WHERE "sessionKey" = t1.sid
) t2 ON TRUE;
QUERY PLAN
-----------------------------------------------------------------
Nested Loop Left Join (cost=5.00..6.08 rows=1 width=96)
Join Filter: (table2."sessionKey" = t1.sid)
-> Seq Scan on table1 t1 (cost=0.00..1.01 rows=1 width=64)
-> Foreign Scan on table2 (cost=5.00..5.06 rows=1 width=96)
Foreign Namespace: test.table2
(5 rows)
# explain SELECT t1.sid, t2.catalog
FROM table1 t1
LEFT OUTER JOIN LATERAL (
SELECT catalog FROM table2 WHERE "sessionKey" = t1.sid LIMIT 1
) t2 ON TRUE;
QUERY PLAN
-----------------------------------------------------------------------
Nested Loop Left Join (cost=5.00..6.09 rows=1 width=96)
-> Seq Scan on table1 t1 (cost=0.00..1.01 rows=1 width=64)
-> Limit (cost=5.00..5.06 rows=1 width=32)
-> Foreign Scan on table2 (cost=5.00..5.06 rows=1 width=32)
Filter: ("sessionKey" = t1.sid)
Foreign Namespace: test.table2
(6 rows)
似乎对此过滤器表达式的评估是导致分段错误的原因。
- 更新1 -
运行GDB,我收到了以下回溯:
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) bt
#0 strlen () at ../sysdeps/x86_64/strlen.S:106
#1 0x000056452ce080db in MemoryContextStrdup ()
#2 0x000056452cdec0e7 in FunctionCall1Coll ()
#3 0x000056452cded6b7 in OutputFunctionCall ()
#4 0x000056452cded904 in OidOutputFunctionCall ()
#5 0x00007f056ee694f3 in AppenMongoValue
(queryDocument=queryDocument@entry=0x56452e217980,
keyName=keyName@entry=0x56452e242278 "sessionKey", value=0,
isnull=<optimized out>, id=<optimized out>) at mongo_query.c:533
#6 0x00007f056ee69e02 in AppendParamValue (scanStateNode=0x56452e23abd8,
paramNode=<optimized out>, keyName=0x56452e242278 "sessionKey",
queryDocument=0x56452e217980)
at mongo_query.c:410
#7 QueryDocument (relationId=relationId@entry=2207239, opExpressionList=
<optimized out>, scanStateNode=scanStateNode@entry=0x56452e23abd8) at
mongo_query.c:218
#8 0x00007f056ee67f9f in MongoBeginForeignScan (scanState=0x56452e23abd8,
executorFlags=<optimized out>) at mongo_fdw.c:516
#9 0x000056452cc02315 in ExecInitForeignScan ()
#10 0x000056452cbe1a43 in ExecInitNode ()
#11 0x000056452cbf75bb in ExecInitLimit ()
#12 0x000056452cbe192d in ExecInitNode ()
#13 0x000056452cbfc537 in ExecInitNestLoop ()
#14 0x000056452cbe1a29 in ExecInitNode ()
#15 0x000056452cbdffba in standard_ExecutorStart ()
#16 0x000056452ccec1af in PortalStart ()
#17 0x000056452cce938d in PostgresMain ()
#18 0x000056452ca8305c in ?? ()
#19 0x000056452cc8d1b3 in PostmasterMain ()
#20 0x000056452ca84251 in main ()
查看Mongo FDW代码,我可以看到段错误发生在mongo_query.c中:
/* Prepare for parameter expression evaluation */
param_expr = ExecInitExpr((Expr *) paramNode, (PlanState *)scanStateNode);
/* Evaluate the parameter expression */
param_value = ExecEvalExpr(param_expr, econtext, &isNull, NULL);
AppenMongoValue(queryDocument, keyName, param_value, isNull,
paramNode->paramtype);
我可以按预期使用paramNode-&gt; paramtype = 19(NAMEOID)。但是,param_value = 0(这是导致AppenMongoValue崩溃的原因)。我不确定这是mongo_fdw扩展名还是Posgresql 9.5.7中的错误,但我需要一些帮助才能找到解决此问题的方法。
- 更新2 -
我深入研究了PostgreSQL代码(特别是execQual.c),我发现没有与参数&#34; sessionKey&#34;相关联的执行计划。
static Datum
ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
Param *expression = (Param *) exprstate->expr;
int thisParamId = expression->paramid;
ParamExecData *prm;
if (isDone)
*isDone = ExprSingleResult;
/*
* PARAM_EXEC params (internal executor parameters) are stored in the
* ecxt_param_exec_vals array, and can be accessed by array index.
*/
prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
if (prm->execPlan != NULL)
{
/* Parameter not evaluated yet, so go do it */
ExecSetParamPlan(prm->execPlan, econtext);
/* ExecSetParamPlan should have processed this param... */
Assert(prm->execPlan == NULL);
}
*isNull = prm->isnull;
return prm->value;
}
因为&#34; prm-&gt; execPlan&#34;为NULL,从不设置参数的值。我不确定如果没有帮助我可以继续这样做。