Skip to content

Commit 88a8e73

Browse files
author
Rucha Kulkarni
committed
Enable index scans for OID cast patterns in system views
Signed-off-by: Rucha Kulkarni <ruchask@amazon.com>
1 parent f860f9e commit 88a8e73

68 files changed

Lines changed: 1118 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

contrib/babelfishpg_tsql/src/hooks.c

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@
5353
#include "nodes/nodeFuncs.h"
5454
#include "optimizer/clauses.h"
5555
#include "optimizer/optimizer.h"
56+
#include "optimizer/paths.h"
5657
#include "optimizer/planner.h"
58+
#include "optimizer/restrictinfo.h"
59+
#include "access/stratnum.h"
5760
#include "parser/analyze.h"
5861
#include "parser/parse_clause.h"
5962
#include "parser/parse_coerce.h"
@@ -395,6 +398,9 @@ ParallelQueryMain_hook_type prev_ParallelQueryMain_hook = NULL;
395398
#ifdef USE_LIBXML
396399
static openxml_set_namespaces_hook_type prev_openxml_set_namespaces_hook = NULL;
397400
#endif
401+
static match_opclause_to_indexcol_hook_type prev_match_opclause_to_indexcol_hook = NULL;
402+
static IndexClause *match_oid_cast_to_indexcol(PlannerInfo *root, RestrictInfo *rinfo, int indexcol, IndexOptInfo *index, Node *cast_arg, Node *val_arg, Oid opfamily);
403+
static IndexClause *bbf_match_opclause_to_indexcol(PlannerInfo *root, RestrictInfo *rinfo, int indexcol, IndexOptInfo *index);
398404

399405
/*****************************************
400406
* Install / Uninstall
@@ -668,6 +674,9 @@ InstallExtendedHooks(void)
668674
walk_view_rule_hook = mark_nodes_inside_view;
669675

670676
handle_target_view_hook = tsql_handle_target_view_hook;
677+
678+
prev_match_opclause_to_indexcol_hook = match_opclause_to_indexcol_hook;
679+
match_opclause_to_indexcol_hook = bbf_match_opclause_to_indexcol;
671680
}
672681

673682
void
@@ -765,6 +774,7 @@ UninstallExtendedHooks(void)
765774
get_domain_typmodin_hook = NULL;
766775
walk_view_rule_hook = NULL;
767776
handle_target_view_hook = NULL;
777+
match_opclause_to_indexcol_hook = prev_match_opclause_to_indexcol_hook;
768778
}
769779

770780
/*****************************************
@@ -9289,3 +9299,117 @@ pltsql_post_transform_expr_recurse(ParseState *pstate, Node *expr)
92899299

92909300
return expr;
92919301
}
9302+
9303+
static IndexClause *
9304+
match_oid_cast_to_indexcol(PlannerInfo *root, RestrictInfo *rinfo,
9305+
int indexcol, IndexOptInfo *index,
9306+
Node *cast_arg, Node *val_arg, Oid opfamily)
9307+
{
9308+
if (IsA(cast_arg, RelabelType))
9309+
{
9310+
RelabelType *relabel = (RelabelType *) cast_arg;
9311+
Node *inner_arg = (Node *) relabel->arg;
9312+
Oid orig_type = exprType(inner_arg);
9313+
9314+
if (orig_type == OIDOID && relabel->resulttype == INT4OID &&
9315+
match_index_to_operand(inner_arg, indexcol, index))
9316+
{
9317+
Oid idx_op;
9318+
9319+
if (contain_volatile_functions(val_arg))
9320+
return NULL;
9321+
9322+
idx_op = get_opfamily_member(opfamily, orig_type, orig_type,
9323+
BTEqualStrategyNumber);
9324+
if (OidIsValid(idx_op))
9325+
{
9326+
RelabelType *new_val;
9327+
OpExpr *new_clause;
9328+
RestrictInfo *new_rinfo;
9329+
IndexClause *iclause;
9330+
OpExpr *clause = (OpExpr *) rinfo->clause;
9331+
9332+
new_val = makeRelabelType((Expr *) copyObject(val_arg),
9333+
orig_type, -1, InvalidOid,
9334+
COERCE_IMPLICIT_CAST);
9335+
9336+
new_clause = makeNode(OpExpr);
9337+
new_clause->opno = idx_op;
9338+
new_clause->opfuncid = get_opcode(idx_op);
9339+
new_clause->opresulttype = BOOLOID;
9340+
new_clause->opretset = false;
9341+
new_clause->opcollid = InvalidOid;
9342+
new_clause->inputcollid = clause->inputcollid;
9343+
new_clause->args = list_make2(copyObject(inner_arg), new_val);
9344+
new_clause->location = clause->location;
9345+
9346+
new_rinfo = make_simple_restrictinfo(root,
9347+
(Expr *) new_clause);
9348+
new_rinfo->security_level = rinfo->security_level;
9349+
9350+
iclause = makeNode(IndexClause);
9351+
iclause->rinfo = rinfo;
9352+
iclause->indexquals = list_make1(new_rinfo);
9353+
iclause->lossy = false;
9354+
iclause->indexcol = indexcol;
9355+
iclause->indexcols = NIL;
9356+
return iclause;
9357+
}
9358+
}
9359+
}
9360+
return NULL;
9361+
}
9362+
9363+
/*
9364+
* bbf_match_opclause_to_indexcol
9365+
*
9366+
* Enables index usage for Babelfish equality patterns where an OID column
9367+
* is cast to integer, preventing index matching. Handles both operand orders:
9368+
* (oid)::integer = value => oid = value::oid
9369+
* value = (oid)::integer => oid = value::oid
9370+
*/
9371+
static IndexClause *
9372+
bbf_match_opclause_to_indexcol(PlannerInfo *root,
9373+
RestrictInfo *rinfo,
9374+
int indexcol,
9375+
IndexOptInfo *index)
9376+
{
9377+
if (sql_dialect == SQL_DIALECT_TSQL && IsA(rinfo->clause, OpExpr))
9378+
{
9379+
OpExpr *clause = (OpExpr *) rinfo->clause;
9380+
9381+
if (list_length(clause->args) == 2 &&
9382+
clause->opno == Int4EqualOperator &&
9383+
index->opcintype[indexcol] == OIDOID)
9384+
{
9385+
Node *leftop = (Node *) linitial(clause->args);
9386+
Node *rightop = (Node *) lsecond(clause->args);
9387+
Oid opfamily = index->opfamily[indexcol];
9388+
IndexClause *iclause;
9389+
9390+
/* Try left operand as the cast: (oid)::int4 = value */
9391+
if (!bms_is_member(index->rel->relid, rinfo->right_relids))
9392+
{
9393+
iclause = match_oid_cast_to_indexcol(root, rinfo, indexcol,
9394+
index, leftop, rightop,
9395+
opfamily);
9396+
if (iclause)
9397+
return iclause;
9398+
}
9399+
9400+
/* Try right operand as the cast: value = (oid)::int4 */
9401+
if (!bms_is_member(index->rel->relid, rinfo->left_relids))
9402+
{
9403+
iclause = match_oid_cast_to_indexcol(root, rinfo, indexcol,
9404+
index, rightop, leftop,
9405+
opfamily);
9406+
if (iclause)
9407+
return iclause;
9408+
}
9409+
}
9410+
}
9411+
9412+
if (prev_match_opclause_to_indexcol_hook)
9413+
return prev_match_opclause_to_indexcol_hook(root, rinfo, indexcol, index);
9414+
return NULL;
9415+
}

contrib/babelfishpg_tsql/src/pltsql.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2519,6 +2519,23 @@ extern bool is_numeric_datatype(Oid typid);
25192519
*/
25202520
extern char *tsql_format_type_extended(Oid type_oid, int32 typemod, bits16 flags);
25212521

2522+
/*
2523+
* INSERT EXEC functions (pl_insert_exec.c)
2524+
*/
2525+
typedef struct InsertExecContext
2526+
{
2527+
Oid temp_table_oid; /* OID of temp table for buffering */
2528+
Oid target_rel_oid; /* OID of target table - lock held to detect schema changes */
2529+
bool is_target_relation_modified; /* Set by bbf_object_access_hook when target table is altered */
2530+
} InsertExecContext;
2531+
2532+
extern InsertExecContext insert_exec_ctx;
2533+
2534+
extern Oid create_insert_exec_temp_table(const char *target_table, const char *column_list, const char *schema_name_in);
2535+
extern void flush_insert_exec_temp_table(PLtsql_execstate *estate,
2536+
const char *column_list);
2537+
extern DestReceiver *CreateInsertExecDestReceiver(void);
2538+
25222539
#define NUM_DB_OBJECTS 11
25232540

25242541
extern const char *shipped_objects_not_in_sys_db[NUM_DB_OBJECTS][2];
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
DROP FUNCTION babel_6814_func1;
2+
GO
3+
4+
-- Drop extra tables created for consistent EXPLAIN plan behavior
5+
DECLARE @i INT = 1;
6+
DECLARE @sql NVARCHAR(200);
7+
WHILE @i <= 100
8+
BEGIN
9+
SET @sql = 'DROP TABLE babel_6814_extra_' + CONVERT(VARCHAR, @i);
10+
EXEC(@sql);
11+
SET @i = @i + 1;
12+
END
13+
GO
14+
15+
-- Drop extra schemas
16+
DECLARE @j INT = 1;
17+
DECLARE @sql2 NVARCHAR(200);
18+
WHILE @j <= 500
19+
BEGIN
20+
SET @sql2 = 'DROP SCHEMA babel_6814_schema_' + CONVERT(VARCHAR, @j);
21+
EXEC(@sql2);
22+
SET @j = @j + 1;
23+
END
24+
GO
25+
26+
DROP VIEW babel_6814_view1;
27+
GO
28+
29+
DROP PROCEDURE babel_6814_proc1;
30+
GO
31+
32+
DROP INDEX babel_6814_idx3 ON babel_6814_t2;
33+
GO
34+
35+
DROP INDEX babel_6814_idx2 ON babel_6814_t1;
36+
GO
37+
38+
DROP INDEX babel_6814_idx1 ON babel_6814_t1;
39+
GO
40+
41+
DROP TABLE babel_6814_t2;
42+
GO
43+
44+
DROP TABLE babel_6814_t1;
45+
GO
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
CREATE TABLE babel_6814_t1(a int, b varchar(50));
2+
GO
3+
4+
CREATE TABLE babel_6814_t2(c int, d varchar(50));
5+
GO
6+
7+
CREATE INDEX babel_6814_idx1 ON babel_6814_t1(a);
8+
GO
9+
10+
CREATE INDEX babel_6814_idx2 ON babel_6814_t1(b);
11+
GO
12+
13+
CREATE INDEX babel_6814_idx3 ON babel_6814_t2(c);
14+
GO
15+
16+
CREATE PROCEDURE babel_6814_proc1 AS SELECT 1;
17+
GO
18+
19+
CREATE VIEW babel_6814_view1 AS SELECT * FROM babel_6814_t1;
20+
GO
21+
22+
CREATE FUNCTION babel_6814_func1()
23+
RETURNS int
24+
AS
25+
BEGIN
26+
RETURN 1;
27+
END
28+
GO
29+
30+
-- Create extra tables and indexes to ensure pg_index/pg_class have enough rows
31+
-- for consistent planner behavior in EXPLAIN tests
32+
DECLARE @i INT = 1;
33+
DECLARE @sql NVARCHAR(200);
34+
WHILE @i <= 100
35+
BEGIN
36+
SET @sql = 'CREATE TABLE babel_6814_extra_' + CONVERT(VARCHAR, @i) + ' (id INT)';
37+
EXEC(@sql);
38+
SET @sql = 'CREATE INDEX babel_6814_extra_idx_' + CONVERT(VARCHAR, @i) + ' ON babel_6814_extra_' + CONVERT(VARCHAR, @i) + '(id)';
39+
EXEC(@sql);
40+
SET @i = @i + 1;
41+
END
42+
GO
43+
44+
-- Create extra schemas to ensure pg_namespace has enough rows for consistent plans
45+
DECLARE @j INT = 1;
46+
DECLARE @sql2 NVARCHAR(200);
47+
WHILE @j <= 500
48+
BEGIN
49+
SET @sql2 = 'CREATE SCHEMA babel_6814_schema_' + CONVERT(VARCHAR, @j);
50+
EXEC(@sql2);
51+
SET @j = @j + 1;
52+
END
53+
GO

0 commit comments

Comments
 (0)