添加" LIMIT 1"到LATERAL JOIN导致Postgres 9.5.7与mongo_fdw扩展名崩溃

时间:2017-07-24 06:50:37

标签: mongodb postgresql crash

我有以下非常简单的测试用例。它需要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,从不设置参数的值。我不确定如果没有帮助我可以继续这样做。

0 个答案:

没有答案