Skip to content

Commit b360440

Browse files
Handle alias-qualified star references in UNPIVOT(babelfish-for-postgresql#3798)
Enhanced UNPIVOT column filtering to properly handle alias.* references in the target list. Modified source column tracking to maintain alias context and updated filtering logic to handle both plain * and alias.* patterns. Previously only plain `SELECT *` was supported for filtering out the unpivot source columns. Task: BABEL-5763 (cherry picked from commit 7e8ce05) Signed-off-by: Manisha Deshpande <[email protected]>
1 parent 0f62bc3 commit b360440

File tree

7 files changed

+267
-52
lines changed

7 files changed

+267
-52
lines changed

contrib/babelfishpg_tsql/src/hooks.c

Lines changed: 109 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@
8888
#include "bbf_parallel_query.h"
8989

9090
#define TDS_NUMERIC_MAX_PRECISION 38
91+
92+
/* Constants for UNPIVOT info list structure */
93+
#define UNPIVOT_TAG_INDEX 0 /* "UNPIVOT" string tag */
94+
#define UNPIVOT_ALIAS_INDEX 1 /* Alias name for the unpivot operation */
95+
#define UNPIVOT_DIMENSION_COL_INDEX 2 /* Dimension column name */
96+
#define UNPIVOT_MEASURE_COL_INDEX 3 /* Measure column name */
97+
#define UNPIVOT_SOURCE_ALIAS_INDEX 4 /* Source table alias */
98+
#define UNPIVOT_SOURCE_COLS_INDEX 5 /* List of source columns */
99+
#define UNPIVOT_NODE_INDEX 6 /* Transformed node (JoinExpr) */
100+
91101
extern bool babelfish_dump_restore;
92102
extern char *babelfish_dump_restore_min_oid;
93103
extern bool pltsql_quoted_identifier;
@@ -5560,8 +5570,10 @@ transform_unpivot_clause_recursive(Node **node_ptr, List **measure_cols, List **
55605570
JoinExpr *join;
55615571
List *unpivot_info;
55625572
char *measure_col;
5573+
char *unpivot_alias;
55635574
Node *transformed_node;
55645575
List *cols;
5576+
List *alias_cols_pair;
55655577
bool found_unpivot;
55665578

55675579
found_unpivot = false;
@@ -5583,18 +5595,22 @@ transform_unpivot_clause_recursive(Node **node_ptr, List **measure_cols, List **
55835595
IsA(linitial(unpivot_info), String) &&
55845596
strcmp(strVal(linitial(unpivot_info)), "UNPIVOT") == 0)
55855597
{
5586-
measure_col = strVal(list_nth(unpivot_info,3));
5587-
transformed_node = list_nth(unpivot_info, 6);
5598+
measure_col = strVal(list_nth(unpivot_info, UNPIVOT_MEASURE_COL_INDEX));
5599+
unpivot_alias = strVal(list_nth(unpivot_info, UNPIVOT_ALIAS_INDEX));
5600+
transformed_node = list_nth(unpivot_info, UNPIVOT_NODE_INDEX);
55885601

55895602
/* Add this measure column to the list */
55905603
*measure_cols = lappend(*measure_cols, makeString(measure_col));
55915604

55925605
/* Get source columns */
55935606
cols = (List *)list_nth(unpivot_info, 5);
5607+
/* Create pair of (unpivot_alias, source_cols) */
5608+
alias_cols_pair = list_make2(makeString(unpivot_alias),
5609+
copyObject(cols));
55945610
if (*unpivot_src_cols == NIL)
5595-
*unpivot_src_cols = copyObject(cols);
5611+
*unpivot_src_cols = list_make1(alias_cols_pair);
55965612
else
5597-
*unpivot_src_cols = list_concat(*unpivot_src_cols, copyObject(cols));
5613+
*unpivot_src_cols = lappend(*unpivot_src_cols, alias_cols_pair);
55985614

55995615
/* Replace UNPIVOT info with transformed node */
56005616
*node_ptr = transformed_node;
@@ -5622,23 +5638,92 @@ filter_star_targetlist_for_unpivot(ParseState *pstate, SelectStmt *stmt, List **
56225638
{
56235639
Query *temp_query;
56245640
List *result_targetlist;
5641+
List *columns_to_filter;
56255642
ListCell *lc;
5626-
5627-
/*
5628-
* Return if not 'SELECT *'
5629-
*
5630-
* TODO [BABEL-5677]: Handle aliased unpivot source columns syntax
5631-
* Does not check: `SELECT unpivot_alias.* ...`
5632-
* Validate against more variations of targetlist
5633-
*/
5634-
if (stmt->targetList == NIL ||
5635-
!IsA(((ResTarget *)linitial(stmt->targetList))->val, ColumnRef) ||
5636-
!IsA(linitial(((ColumnRef *)((ResTarget *)linitial(stmt->targetList))->val)->fields), A_Star))
5637-
{
5643+
bool has_star;
5644+
5645+
/* Return if no * in target list */
5646+
if (stmt->targetList == NIL)
56385647
return stmt->targetList;
5639-
}
56405648

56415649
result_targetlist = NIL;
5650+
columns_to_filter = NIL;
5651+
has_star = false;
5652+
5653+
/* Check for * patterns */
5654+
foreach(lc, stmt->targetList)
5655+
{
5656+
Node *last;
5657+
ColumnRef *cref;
5658+
ResTarget *rt = (ResTarget *)lfirst(lc);
5659+
5660+
if (!IsA(rt->val, ColumnRef))
5661+
continue;
5662+
5663+
cref = (ColumnRef *)rt->val;
5664+
last = llast(cref->fields);
5665+
5666+
/*
5667+
* Only process star expressions (table.* or *) for filtering out
5668+
* unpivot source columns from result targetlist.
5669+
* Other targetlist items are left unmodified and processed normally
5670+
*/
5671+
if (IsA(last, A_Star))
5672+
{
5673+
has_star = true;
5674+
5675+
/* Build list of columns to filter based on star type */
5676+
if (list_length(cref->fields) == 1)
5677+
{
5678+
/* For plain SELECT *, collect columns from all unpivots */
5679+
ListCell *info_lc;
5680+
foreach(info_lc, *source_cols)
5681+
{
5682+
ListCell *col_lc;
5683+
List *info = (List *) lfirst(info_lc);
5684+
List *unpivot_col_list = (List *) lsecond(info);
5685+
5686+
/* Add each column to filter list */
5687+
foreach(col_lc, unpivot_col_list)
5688+
columns_to_filter = lappend(columns_to_filter, lfirst(col_lc));
5689+
}
5690+
}
5691+
else if (list_length(cref->fields) == 2)
5692+
{
5693+
/* For alias.*, only filter if it's an unpivot alias */
5694+
ListCell *info_lc;
5695+
char *alias = strVal(linitial(cref->fields));
5696+
bool found_match = false;
5697+
5698+
/* Find matching unpivot alias */
5699+
foreach(info_lc, *source_cols)
5700+
{
5701+
List *info = (List *) lfirst(info_lc);
5702+
5703+
if (strcmp(alias, strVal(linitial(info))) == 0)
5704+
{
5705+
/* Found matching unpivot - collect its columns */
5706+
ListCell *col_lc;
5707+
List *unpivot_col_list = (List *) lsecond(info);
5708+
5709+
foreach(col_lc, unpivot_col_list)
5710+
columns_to_filter = lappend(columns_to_filter, lfirst(col_lc));
5711+
5712+
found_match = true;
5713+
break;
5714+
}
5715+
}
5716+
5717+
/* If not an unpivot alias, skip filtering */
5718+
if (!found_match)
5719+
continue;
5720+
}
5721+
}
5722+
}
5723+
5724+
/* If no star or no columns to filter, return original target list */
5725+
if (!has_star || columns_to_filter == NIL)
5726+
return stmt->targetList;
56425727

56435728
/* Analyze to expand * */
56445729
temp_query = parse_sub_analyze((Node *)copyObject(stmt),
@@ -5658,13 +5743,12 @@ filter_star_targetlist_for_unpivot(ParseState *pstate, SelectStmt *stmt, List **
56585743
te = (TargetEntry *)lfirst(lc);
56595744
skip_column = false;
56605745

5661-
/* Check if this column is in source_cols */
5662-
foreach(source_lc, *source_cols) {
5746+
/* Check if column should be excluded */
5747+
foreach(source_lc, columns_to_filter)
5748+
{
56635749
source_col = (String *)lfirst(source_lc);
56645750
if (strcmp(te->resname, strVal(source_col)) == 0) {
56655751
skip_column = true;
5666-
/* Remove the matched source column to avoid duplicate removal */
5667-
*source_cols = foreach_delete_current(*source_cols, source_lc);
56685752
break;
56695753
}
56705754
}
@@ -5676,7 +5760,10 @@ filter_star_targetlist_for_unpivot(ParseState *pstate, SelectStmt *stmt, List **
56765760
result_targetlist = lappend(result_targetlist, rt);
56775761
}
56785762
}
5679-
5763+
5764+
/* Free the temporary filter list */
5765+
list_free(columns_to_filter);
5766+
56805767
return result_targetlist;
56815768
}
56825769

test/JDBC/expected/unpivot-vu-cleanup.out

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
DROP VIEW sales.sales_analysis_view;
33
GO
44

5+
DROP VIEW sales.quarterly_view;
6+
GO
7+
58
-- Drop tables
69
DROP TABLE customer_quarterly_sales;
710
GO

test/JDBC/expected/unpivot-vu-prepare.out

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,11 @@ GO
296296
~~ROW COUNT: 3~~
297297

298298

299+
CREATE VIEW sales.quarterly_view AS
300+
SELECT customer_id, q1 AS q1_sales, q2, q3, q4
301+
FROM customer_turnover;
302+
GO
303+
299304
CREATE TABLE customer_history (
300305
customer_id INT,
301306
q1 INT,

test/JDBC/expected/unpivot-vu-verify.out

Lines changed: 105 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,87 @@ int#!#varchar#!#char#!#int#!#nvarchar
9898
~~END~~
9999

100100

101+
SELECT unpvt.* FROM customer_turnover
102+
UNPIVOT (turnover FOR quarter IN (q1, q2, q3, q4)) AS unpvt;
103+
GO
104+
~~START~~
105+
int#!#varchar#!#char#!#int#!#nvarchar
106+
3#!#Cust C#!#R#!#0#!#q2
107+
3#!#Cust C#!#R#!#400#!#q3
108+
3#!#Cust C#!#R#!#150#!#q4
109+
1#!#Cust A#!#R#!#100#!#q1
110+
1#!#Cust A#!#R#!#200#!#q2
111+
1#!#Cust A#!#R#!#100#!#q3
112+
1#!#Cust A#!#R#!#400#!#q4
113+
2#!#Cust B#!#P#!#150#!#q1
114+
2#!#Cust B#!#P#!#250#!#q2
115+
2#!#Cust B#!#P#!#200#!#q3
116+
~~END~~
117+
118+
119+
SELECT unpvt.*
120+
FROM (customer_turnover c JOIN product_sales p ON p.product_id = c.customer_id)
121+
UNPIVOT (turnover FOR quarter IN (q1, q2, q3, q4)) AS unpvt;
122+
GO
123+
~~START~~
124+
int#!#varchar#!#char#!#int#!#varchar#!#int#!#numeric#!#int#!#numeric#!#int#!#nvarchar
125+
3#!#Cust C#!#R#!#3#!#XYZ#!#0#!#0.00#!#<NULL>#!#<NULL>#!#0#!#q2
126+
3#!#Cust C#!#R#!#3#!#XYZ#!#0#!#0.00#!#<NULL>#!#<NULL>#!#400#!#q3
127+
3#!#Cust C#!#R#!#3#!#XYZ#!#0#!#0.00#!#<NULL>#!#<NULL>#!#150#!#q4
128+
1#!#Cust A#!#R#!#1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#100#!#q1
129+
1#!#Cust A#!#R#!#1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#200#!#q2
130+
1#!#Cust A#!#R#!#1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#100#!#q3
131+
1#!#Cust A#!#R#!#1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#400#!#q4
132+
2#!#Cust B#!#P#!#2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#150#!#q1
133+
2#!#Cust B#!#P#!#2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#250#!#q2
134+
2#!#Cust B#!#P#!#2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#200#!#q3
135+
~~END~~
136+
137+
138+
SELECT
139+
p.*,
140+
unpvt.*
141+
FROM customer_turnover
142+
UNPIVOT (turnover FOR quarter IN (q1, q2, q3, q4)) AS unpvt
143+
JOIN product_sales p ON p.product_id = unpvt.customer_id;
144+
GO
145+
~~START~~
146+
int#!#varchar#!#int#!#numeric#!#int#!#numeric#!#int#!#varchar#!#char#!#int#!#nvarchar
147+
3#!#XYZ#!#0#!#0.00#!#<NULL>#!#<NULL>#!#3#!#Cust C#!#R#!#0#!#q2
148+
3#!#XYZ#!#0#!#0.00#!#<NULL>#!#<NULL>#!#3#!#Cust C#!#R#!#400#!#q3
149+
3#!#XYZ#!#0#!#0.00#!#<NULL>#!#<NULL>#!#3#!#Cust C#!#R#!#150#!#q4
150+
1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#1#!#Cust A#!#R#!#100#!#q1
151+
1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#1#!#Cust A#!#R#!#200#!#q2
152+
1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#1#!#Cust A#!#R#!#100#!#q3
153+
1#!#ABC#!#100#!#1000.00#!#150#!#1500.00#!#1#!#Cust A#!#R#!#400#!#q4
154+
2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#2#!#Cust B#!#P#!#150#!#q1
155+
2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#2#!#Cust B#!#P#!#250#!#q2
156+
2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#2#!#Cust B#!#P#!#200#!#q3
157+
~~END~~
158+
159+
160+
SELECT
161+
p.*,
162+
u1.*,
163+
u2.*
164+
FROM customer_turnover t1
165+
UNPIVOT (turnover FOR quarter IN (q1, q2, q3, q4)) AS u1
166+
JOIN product_sales p
167+
ON p.product_id = u1.customer_id
168+
AND p.revenue_q1 > 1000.00
169+
JOIN revenue_data t2
170+
UNPIVOT (revenue FOR quarter2 IN (q1_sales, q2_sales)) AS u2
171+
ON u1.customer_id = u2.id
172+
AND u1.quarter = SUBSTRING(u2.quarter2, 1, 2)
173+
AND turnover > 100;
174+
GO
175+
~~START~~
176+
int#!#varchar#!#int#!#numeric#!#int#!#numeric#!#int#!#varchar#!#char#!#int#!#nvarchar#!#int#!#int#!#nvarchar
177+
2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#2#!#Cust B#!#P#!#150#!#q1#!#2#!#200#!#q1_sales
178+
2#!#PQR#!#80#!#1600.00#!#90#!#1800.00#!#2#!#Cust B#!#P#!#250#!#q2#!#2#!#250#!#q2_sales
179+
~~END~~
180+
181+
101182
-- Mixed join and unpivoted columns
102183
SELECT c.customer_name, u.turnover, u.quarter
103184
FROM customer_info c
@@ -989,6 +1070,23 @@ int#!#varchar#!#numeric#!#nvarchar#!#varchar
9891070
~~END~~
9901071

9911072

1073+
SELECT * FROM sales.quarterly_view
1074+
UNPIVOT (turnover FOR quarter IN (q1_sales, q2, q3, q4)) AS unpvt;
1075+
GO
1076+
~~START~~
1077+
int#!#int#!#nvarchar
1078+
3#!#0#!#q2
1079+
3#!#400#!#q3
1080+
3#!#150#!#q4
1081+
1#!#100#!#q1_sales
1082+
1#!#200#!#q2
1083+
1#!#100#!#q3
1084+
1#!#400#!#q4
1085+
2#!#150#!#q1_sales
1086+
2#!#250#!#q2
1087+
2#!#200#!#q3
1088+
~~END~~
1089+
9921090

9931091
-- SUBQURIES
9941092
-- 1. IN/EXISTS
@@ -1068,16 +1166,16 @@ AS
10681166
RETURN ( SELECT product_id, q1_sales, q2_sales FROM sales_data );
10691167
GO
10701168

1071-
SELECT product_id, quarter, sales FROM dbo.GetSalesData()
1169+
SELECT * FROM dbo.GetSalesData()
10721170
UNPIVOT ( sales FOR quarter IN (q1_sales, q2_sales)) AS unpvt;
10731171
GO
10741172
~~START~~
1075-
int#!#nvarchar#!#int
1076-
1#!#q1_sales#!#100
1077-
1#!#q2_sales#!#150
1078-
2#!#q1_sales#!#200
1079-
2#!#q2_sales#!#250
1080-
3#!#q2_sales#!#350
1173+
int#!#int#!#nvarchar
1174+
1#!#100#!#q1_sales
1175+
1#!#150#!#q2_sales
1176+
2#!#200#!#q1_sales
1177+
2#!#250#!#q2_sales
1178+
3#!#350#!#q2_sales
10811179
~~END~~
10821180

10831181

@@ -1688,24 +1786,6 @@ GO
16881786

16891787
-- KNOWN ISSUES
16901788
-- BABEL-5677 - Support more variations of UNPIVOT Syntax
1691-
-- Extra columns in result set when `SELECT alias.*`
1692-
SELECT unpvt.* FROM customer_turnover c
1693-
UNPIVOT (turnover FOR quarter IN (q1, q2, q3, q4)) AS unpvt;
1694-
GO
1695-
~~START~~
1696-
int#!#varchar#!#char#!#int#!#int#!#int#!#int#!#int#!#nvarchar
1697-
3#!#Cust C#!#R#!#<NULL>#!#0#!#400#!#150#!#0#!#q2
1698-
3#!#Cust C#!#R#!#<NULL>#!#0#!#400#!#150#!#400#!#q3
1699-
3#!#Cust C#!#R#!#<NULL>#!#0#!#400#!#150#!#150#!#q4
1700-
1#!#Cust A#!#R#!#100#!#200#!#100#!#400#!#100#!#q1
1701-
1#!#Cust A#!#R#!#100#!#200#!#100#!#400#!#200#!#q2
1702-
1#!#Cust A#!#R#!#100#!#200#!#100#!#400#!#100#!#q3
1703-
1#!#Cust A#!#R#!#100#!#200#!#100#!#400#!#400#!#q4
1704-
2#!#Cust B#!#P#!#150#!#250#!#200#!#<NULL>#!#150#!#q1
1705-
2#!#Cust B#!#P#!#150#!#250#!#200#!#<NULL>#!#250#!#q2
1706-
2#!#Cust B#!#P#!#150#!#250#!#200#!#<NULL>#!#200#!#q3
1707-
~~END~~
1708-
17091789
-- Uppercase column names in unpivot source list
17101790
SELECT [ID_番号], [Amount_金額], [Quarter_四半期]
17111791
FROM [Global_データ_Sales]

test/JDBC/input/unpivot-vu-cleanup.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
DROP VIEW sales.sales_analysis_view;
33
GO
44

5+
DROP VIEW sales.quarterly_view;
6+
GO
7+
58
-- Drop tables
69
DROP TABLE customer_quarterly_sales;
710
GO

test/JDBC/input/unpivot-vu-prepare.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ INSERT INTO sales.quarterly_data VALUES
274274
(3, 'Product C', 175.25, 225.75, NULL, 250.00);
275275
GO
276276

277+
CREATE VIEW sales.quarterly_view AS
278+
SELECT customer_id, q1 AS q1_sales, q2, q3, q4
279+
FROM customer_turnover;
280+
GO
281+
277282
CREATE TABLE customer_history (
278283
customer_id INT,
279284
q1 INT,

0 commit comments

Comments
 (0)