Fixes: #5787 In prepared statements, map any unused parameters

to a generic type.
master-failure-dnm
Teja Mupparti 2022-04-04 19:32:20 -07:00 committed by Teja Mupparti
parent 544e6c7428
commit e56fc34404
5 changed files with 193 additions and 0 deletions

View File

@ -152,6 +152,7 @@
#include "distributed/multi_partitioning_utils.h"
#include "distributed/multi_physical_planner.h"
#include "distributed/multi_server_executor.h"
#include "distributed/param_utils.h"
#include "distributed/placement_access.h"
#include "distributed/placement_connection.h"
#include "distributed/relation_access_tracking.h"
@ -830,6 +831,19 @@ AdaptiveExecutor(CitusScanState *scanState)
distributedPlan->modLevel, taskList, excludeFromXact);
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(
distributedPlan->modLevel,
taskList,

View File

@ -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, &paramBitmap);
/* Check for any missing parameters */
for (int parameterNum = 1; parameterNum <= parameterCount; parameterNum++)
{
if (!bms_is_member(parameterNum, paramBitmap))
{
boundParams->params[parameterNum - 1].ptype = 0;
}
}
}

View File

@ -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 */

View File

@ -211,6 +211,24 @@ ORDER BY
user_id,
time
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;
user_id | time | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------
@ -883,6 +901,42 @@ EXECUTE prepared_test_8;
(10 rows)
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);
user_id | time | value_1 | value_2 | value_3 | value_4
---------------------------------------------------------------------

View File

@ -225,6 +225,21 @@ ORDER BY
time
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;
@ -301,6 +316,13 @@ EXECUTE prepared_test_8;
EXECUTE prepared_test_8;
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(2);
EXECUTE prepared_partition_column_insert(3);