mirror of https://github.com/citusdata/citus.git
Fixes: #5787 In prepared statements, map any unused parameters
to a generic type.master-failure-dnm
parent
544e6c7428
commit
e56fc34404
|
@ -152,6 +152,7 @@
|
||||||
#include "distributed/multi_partitioning_utils.h"
|
#include "distributed/multi_partitioning_utils.h"
|
||||||
#include "distributed/multi_physical_planner.h"
|
#include "distributed/multi_physical_planner.h"
|
||||||
#include "distributed/multi_server_executor.h"
|
#include "distributed/multi_server_executor.h"
|
||||||
|
#include "distributed/param_utils.h"
|
||||||
#include "distributed/placement_access.h"
|
#include "distributed/placement_access.h"
|
||||||
#include "distributed/placement_connection.h"
|
#include "distributed/placement_connection.h"
|
||||||
#include "distributed/relation_access_tracking.h"
|
#include "distributed/relation_access_tracking.h"
|
||||||
|
@ -830,6 +831,19 @@ AdaptiveExecutor(CitusScanState *scanState)
|
||||||
distributedPlan->modLevel, taskList, excludeFromXact);
|
distributedPlan->modLevel, taskList, excludeFromXact);
|
||||||
|
|
||||||
bool localExecutionSupported = true;
|
bool localExecutionSupported = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In some rare cases, we have prepared statements that pass a parameter
|
||||||
|
* and never used in the query, mark such parameters' type as Invalid(0),
|
||||||
|
* which will be used later in ExtractParametersFromParamList() to map them
|
||||||
|
* to a generic datatype. Skip for dynamic parameters.
|
||||||
|
*/
|
||||||
|
if (paramListInfo && !paramListInfo->paramFetch)
|
||||||
|
{
|
||||||
|
paramListInfo = copyParamList(paramListInfo);
|
||||||
|
MarkUnreferencedExternParams((Node *) job->jobQuery, paramListInfo);
|
||||||
|
}
|
||||||
|
|
||||||
DistributedExecution *execution = CreateDistributedExecution(
|
DistributedExecution *execution = CreateDistributedExecution(
|
||||||
distributedPlan->modLevel,
|
distributedPlan->modLevel,
|
||||||
taskList,
|
taskList,
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* param_utils.c
|
||||||
|
* Utilities to process paramaters.
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include <nodes/bitmapset.h>
|
||||||
|
#include <nodes/nodeFuncs.h>
|
||||||
|
#include <nodes/parsenodes.h>
|
||||||
|
#include <nodes/params.h>
|
||||||
|
#include <nodes/primnodes.h>
|
||||||
|
#include <nodes/nodes.h>
|
||||||
|
#include "distributed/param_utils.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsExternParamUsedInQuery returns true if the passed in paramId
|
||||||
|
* is used in the query, false otherwise.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
GetParamsUsedInQuery(Node *expression, Bitmapset **paramBitmap)
|
||||||
|
{
|
||||||
|
if (expression == NULL)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsA(expression, Param))
|
||||||
|
{
|
||||||
|
Param *param = (Param *) expression;
|
||||||
|
int paramId = param->paramid;
|
||||||
|
|
||||||
|
/* only care about user supplied parameters */
|
||||||
|
if (param->paramkind != PARAM_EXTERN)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Found a parameter, mark it in the bitmap and continue */
|
||||||
|
*paramBitmap = bms_add_member(*paramBitmap, paramId);
|
||||||
|
|
||||||
|
/* Continue searching */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* keep traversing */
|
||||||
|
if (IsA(expression, Query))
|
||||||
|
{
|
||||||
|
return query_tree_walker((Query *) expression,
|
||||||
|
GetParamsUsedInQuery,
|
||||||
|
paramBitmap,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return expression_tree_walker(expression,
|
||||||
|
GetParamsUsedInQuery,
|
||||||
|
paramBitmap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MarkUnreferencedExternParams marks parameter's type to zero if the
|
||||||
|
* parameter is not used in the query.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
MarkUnreferencedExternParams(Node *expression, ParamListInfo boundParams)
|
||||||
|
{
|
||||||
|
int parameterCount = boundParams->numParams;
|
||||||
|
Bitmapset *paramBitmap = NULL;
|
||||||
|
|
||||||
|
/* Fetch all parameters used in the query */
|
||||||
|
GetParamsUsedInQuery(expression, ¶mBitmap);
|
||||||
|
|
||||||
|
/* Check for any missing parameters */
|
||||||
|
for (int parameterNum = 1; parameterNum <= parameterCount; parameterNum++)
|
||||||
|
{
|
||||||
|
if (!bms_is_member(parameterNum, paramBitmap))
|
||||||
|
{
|
||||||
|
boundParams->params[parameterNum - 1].ptype = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* param_utils.h
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARAM_UTILS_H
|
||||||
|
#define PARAM_UTILS_H
|
||||||
|
|
||||||
|
extern bool GetParamsUsedInQuery(Node *expression, Bitmapset **paramBitmap);
|
||||||
|
extern void MarkUnreferencedExternParams(Node *expression, ParamListInfo boundParams);
|
||||||
|
|
||||||
|
#endif /* PARAM_UTILS_H */
|
|
@ -211,6 +211,24 @@ ORDER BY
|
||||||
user_id,
|
user_id,
|
||||||
time
|
time
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
|
--
|
||||||
|
-- Test a prepared statement with unused argument
|
||||||
|
--
|
||||||
|
CREATE TYPE foo as (x int, y int);
|
||||||
|
CREATE TABLE footest (x int, y int, z foo);
|
||||||
|
SELECT create_distributed_table('footest','x');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
INSERT INTO footest VALUES(1, 2, (3,4));
|
||||||
|
-- Add a redundant parameter
|
||||||
|
PREPARE prepared_test_9(foo,foo) AS
|
||||||
|
WITH a AS (
|
||||||
|
SELECT * FROM footest WHERE z = $1 AND x = 1 OFFSET 0
|
||||||
|
)
|
||||||
|
SELECT * FROM a;
|
||||||
EXECUTE prepared_test_1;
|
EXECUTE prepared_test_1;
|
||||||
user_id | time | value_1 | value_2 | value_3 | value_4
|
user_id | time | value_1 | value_2 | value_3 | value_4
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
@ -883,6 +901,42 @@ EXECUTE prepared_test_8;
|
||||||
(10 rows)
|
(10 rows)
|
||||||
|
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
x | y | z
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | (3,4)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
x | y | z
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | (3,4)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
x | y | z
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | (3,4)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
x | y | z
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | (3,4)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
x | y | z
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | (3,4)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
x | y | z
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1 | 2 | (3,4)
|
||||||
|
(1 row)
|
||||||
|
|
||||||
EXECUTE prepared_partition_column_insert(1);
|
EXECUTE prepared_partition_column_insert(1);
|
||||||
user_id | time | value_1 | value_2 | value_3 | value_4
|
user_id | time | value_1 | value_2 | value_3 | value_4
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
|
|
|
@ -225,6 +225,21 @@ ORDER BY
|
||||||
time
|
time
|
||||||
LIMIT 10;
|
LIMIT 10;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test a prepared statement with unused argument
|
||||||
|
--
|
||||||
|
CREATE TYPE foo as (x int, y int);
|
||||||
|
CREATE TABLE footest (x int, y int, z foo);
|
||||||
|
SELECT create_distributed_table('footest','x');
|
||||||
|
INSERT INTO footest VALUES(1, 2, (3,4));
|
||||||
|
|
||||||
|
-- Add a redundant parameter
|
||||||
|
PREPARE prepared_test_9(foo,foo) AS
|
||||||
|
WITH a AS (
|
||||||
|
SELECT * FROM footest WHERE z = $1 AND x = 1 OFFSET 0
|
||||||
|
)
|
||||||
|
SELECT * FROM a;
|
||||||
|
|
||||||
EXECUTE prepared_test_1;
|
EXECUTE prepared_test_1;
|
||||||
EXECUTE prepared_test_1;
|
EXECUTE prepared_test_1;
|
||||||
EXECUTE prepared_test_1;
|
EXECUTE prepared_test_1;
|
||||||
|
@ -301,6 +316,13 @@ EXECUTE prepared_test_8;
|
||||||
EXECUTE prepared_test_8;
|
EXECUTE prepared_test_8;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
EXECUTE prepared_test_9('(3,4)','(2,3)');
|
||||||
|
|
||||||
EXECUTE prepared_partition_column_insert(1);
|
EXECUTE prepared_partition_column_insert(1);
|
||||||
EXECUTE prepared_partition_column_insert(2);
|
EXECUTE prepared_partition_column_insert(2);
|
||||||
EXECUTE prepared_partition_column_insert(3);
|
EXECUTE prepared_partition_column_insert(3);
|
||||||
|
|
Loading…
Reference in New Issue