Skip to content

Commit cdf2f99

Browse files
authored
Fix FOR XML RAW('') empty element name handling and all-NULL row output (#4680)
Changes are in tsql_row_to_xml_raw() in forxml.c. When element_name is empty, the opening and closing wrapper tags are now skipped in ELEMENTS mode (same approach as PATH mode). For attribute-centric mode (without ELEMENTS), an error is raised since empty row tag names are not valid. The self-closing tag logic for all-NULL rows was also corrected. Task: BABEL-6379 Signed-off-by: Japleen Kaur <amjj@amazon.com>
1 parent 0ede83d commit cdf2f99

File tree

5 files changed

+142
-24
lines changed

5 files changed

+142
-24
lines changed

contrib/babelfishpg_tsql/src/tsql_for/forxml.c

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
215215
TupleDesc tupdesc;
216216
HeapTupleData tmptup;
217217
HeapTuple tuple;
218-
218+
bool allnull = true;
219+
219220
td = DatumGetHeapTupleHeader(record);
220221

221222
/* Extract rowtype info and find a tupdesc */
@@ -228,19 +229,34 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
228229
tmptup.t_data = td;
229230
tuple = &tmptup;
230231

231-
/* Output opening tag */
232-
if (elements)
232+
/*
233+
* Empty element name without ELEMENTS mode is not allowed — attribute-centric
234+
* serialization requires a row tag name.
235+
*/
236+
if (element_name[0] == '\0' && !elements)
233237
{
234-
/* ELEMENTS mode: <row><col>value</col></row> */
235-
if (xsinil)
236-
appendStringInfo(state, "<%s " XML_XMLNS_XSI ">", element_name);
237-
else
238-
appendStringInfo(state, "<%s>", element_name);
238+
ereport(ERROR,
239+
(errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
240+
errmsg("Row tag omission (empty row tag name) cannot be used "
241+
"with attribute-centric FOR XML serialization.")));
239242
}
240-
else
243+
244+
/* Output opening tag (only when element_name is non-empty) */
245+
if (element_name[0] != '\0')
241246
{
242-
/* ATTRIBUTES mode: <row col="value"/> */
243-
appendStringInfo(state, "<%s", element_name);
247+
if (elements)
248+
{
249+
/* ELEMENTS mode: <row><col>value</col></row> */
250+
if (xsinil)
251+
appendStringInfo(state, "<%s " XML_XMLNS_XSI ">", element_name);
252+
else
253+
appendStringInfo(state, "<%s>", element_name);
254+
}
255+
else
256+
{
257+
/* ATTRIBUTES mode: <row col="value"/> */
258+
appendStringInfo(state, "<%s", element_name);
259+
}
244260
}
245261

246262
for (int i = 0; i < tupdesc->natts; i++)
@@ -265,6 +281,7 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
265281
/* ELEMENTS mode output */
266282
if (!isnull)
267283
{
284+
allnull = false;
268285
/* Normal element: <col>value</col> */
269286
appendStringInfo(state, "<%s>%s</%s>",
270287
colname,
@@ -273,6 +290,7 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
273290
}
274291
else if (xsinil)
275292
{
293+
allnull = false;
276294
/* XSINIL: <col xsi:nil="true"/> */
277295
appendStringInfo(state, "<%s " XML_XSI_NIL "/>", colname);
278296
}
@@ -292,7 +310,29 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
292310

293311
/* Output closing tag */
294312
if (elements)
295-
appendStringInfo(state, "</%s>", element_name);
313+
{
314+
if (element_name[0] == '\0')
315+
{
316+
/*
317+
* Empty element name with ELEMENTS: no wrapper tag needed.
318+
* Just output the child elements directly, same as PATH('').
319+
*/
320+
}
321+
else if (allnull)
322+
{
323+
/*
324+
* If all column values are NULL, produce a self-closing element
325+
* like TSQL does: <row/>. Replace the '>' in the already
326+
* appended opening tag with '/' and append '>'.
327+
*/
328+
state->data[state->len - 1] = '/';
329+
appendStringInfoChar(state, '>');
330+
}
331+
else
332+
{
333+
appendStringInfo(state, "</%s>", element_name);
334+
}
335+
}
296336
else
297337
appendStringInfoString(state, "/>");
298338

test/JDBC/expected/forxml-raw-elements-before-17_9-or-18_3-vu-verify.out

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS;
143143
GO
144144
~~START~~
145145
ntext
146-
<row></row>
146+
<row/>
147147
~~END~~
148148

149149

@@ -170,7 +170,7 @@ SELECT NULL AS a, NULL AS b, NULL AS c, NULL AS d FOR XML RAW, ELEMENTS;
170170
GO
171171
~~START~~
172172
ntext
173-
<row></row>
173+
<row/>
174174
~~END~~
175175

176176

@@ -198,7 +198,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS ABSENT;
198198
GO
199199
~~START~~
200200
ntext
201-
<row></row>
201+
<row/>
202202
~~END~~
203203

204204

@@ -510,7 +510,7 @@ SELECT name, salary FROM forxml_raw_elements_t1 FOR XML RAW, ELEMENTS;
510510
GO
511511
~~START~~
512512
ntext
513-
<row><name>John</name><salary>50000</salary></row><row><name>Jane</name><salary>60000</salary></row><row><name>Bob</name></row><row><name>Alice</name><salary>70000</salary></row><row></row>
513+
<row><name>John</name><salary>50000</salary></row><row><name>Jane</name><salary>60000</salary></row><row><name>Bob</name></row><row><name>Alice</name><salary>70000</salary></row><row/>
514514
~~END~~
515515

516516

@@ -950,7 +950,7 @@ SELECT CAST(NULL AS VARCHAR(10)) AS null_val FOR XML RAW, ELEMENTS;
950950
GO
951951
~~START~~
952952
ntext
953-
<row></row>
953+
<row/>
954954
~~END~~
955955

956956

@@ -971,7 +971,34 @@ SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
971971
GO
972972
~~START~~
973973
ntext
974-
<><a>1</a><b>2</b></>
974+
<a>1</a><b>2</b>
975+
~~END~~
976+
977+
978+
-- Empty element name without ELEMENTS (attribute-centric) - should error
979+
SELECT 1 AS a, 2 AS b FOR XML RAW('');
980+
GO
981+
~~START~~
982+
ntext
983+
~~ERROR (Code: 33557097)~~
984+
985+
~~ERROR (Message: Row tag omission (empty row tag name) cannot be used with attribute-centric FOR XML serialization.)~~
986+
987+
988+
-- All NULLs under attribute mode (no ELEMENTS)
989+
SELECT NULL AS a, NULL AS b FOR XML RAW;
990+
GO
991+
~~START~~
992+
ntext
993+
<row/>
994+
~~END~~
995+
996+
997+
-- All NULLs with empty element name and ELEMENTS
998+
SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
999+
GO
1000+
~~START~~
1001+
ntext
9751002
~~END~~
9761003

9771004

test/JDBC/expected/forxml-raw-elements-vu-verify.out

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS;
143143
GO
144144
~~START~~
145145
ntext
146-
<row></row>
146+
<row/>
147147
~~END~~
148148

149149

@@ -170,7 +170,7 @@ SELECT NULL AS a, NULL AS b, NULL AS c, NULL AS d FOR XML RAW, ELEMENTS;
170170
GO
171171
~~START~~
172172
ntext
173-
<row></row>
173+
<row/>
174174
~~END~~
175175

176176

@@ -198,7 +198,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS ABSENT;
198198
GO
199199
~~START~~
200200
ntext
201-
<row></row>
201+
<row/>
202202
~~END~~
203203

204204

@@ -510,7 +510,7 @@ SELECT name, salary FROM forxml_raw_elements_t1 FOR XML RAW, ELEMENTS;
510510
GO
511511
~~START~~
512512
ntext
513-
<row><name>John</name><salary>50000</salary></row><row><name>Jane</name><salary>60000</salary></row><row><name>Bob</name></row><row><name>Alice</name><salary>70000</salary></row><row></row>
513+
<row><name>John</name><salary>50000</salary></row><row><name>Jane</name><salary>60000</salary></row><row><name>Bob</name></row><row><name>Alice</name><salary>70000</salary></row><row/>
514514
~~END~~
515515

516516

@@ -950,7 +950,7 @@ SELECT CAST(NULL AS VARCHAR(10)) AS null_val FOR XML RAW, ELEMENTS;
950950
GO
951951
~~START~~
952952
ntext
953-
<row></row>
953+
<row/>
954954
~~END~~
955955

956956

@@ -971,7 +971,34 @@ SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
971971
GO
972972
~~START~~
973973
ntext
974-
<><a>1</a><b>2</b></>
974+
<a>1</a><b>2</b>
975+
~~END~~
976+
977+
978+
-- Empty element name without ELEMENTS (attribute-centric) - should error
979+
SELECT 1 AS a, 2 AS b FOR XML RAW('');
980+
GO
981+
~~START~~
982+
ntext
983+
~~ERROR (Code: 33557097)~~
984+
985+
~~ERROR (Message: Row tag omission (empty row tag name) cannot be used with attribute-centric FOR XML serialization.)~~
986+
987+
988+
-- All NULLs under attribute mode (no ELEMENTS)
989+
SELECT NULL AS a, NULL AS b FOR XML RAW;
990+
GO
991+
~~START~~
992+
ntext
993+
<row/>
994+
~~END~~
995+
996+
997+
-- All NULLs with empty element name and ELEMENTS
998+
SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
999+
GO
1000+
~~START~~
1001+
ntext
9751002
~~END~~
9761003

9771004

test/JDBC/input/forxml/forxml-raw-elements-before-17_9-or-18_3-vu-verify.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,18 @@ GO
414414
SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
415415
GO
416416

417+
-- Empty element name without ELEMENTS (attribute-centric) - should error
418+
SELECT 1 AS a, 2 AS b FOR XML RAW('');
419+
GO
420+
421+
-- All NULLs under attribute mode (no ELEMENTS)
422+
SELECT NULL AS a, NULL AS b FOR XML RAW;
423+
GO
424+
425+
-- All NULLs with empty element name and ELEMENTS
426+
SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
427+
GO
428+
417429
-- Element name with spaces
418430
SELECT 1 AS a FOR XML RAW('element name'), ELEMENTS;
419431
GO

test/JDBC/input/forxml/forxml-raw-elements-vu-verify.sql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,18 @@ GO
414414
SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
415415
GO
416416

417+
-- Empty element name without ELEMENTS (attribute-centric) - should error
418+
SELECT 1 AS a, 2 AS b FOR XML RAW('');
419+
GO
420+
421+
-- All NULLs under attribute mode (no ELEMENTS)
422+
SELECT NULL AS a, NULL AS b FOR XML RAW;
423+
GO
424+
425+
-- All NULLs with empty element name and ELEMENTS
426+
SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
427+
GO
428+
417429
-- Element name with spaces
418430
SELECT 1 AS a FOR XML RAW('element name'), ELEMENTS;
419431
GO

0 commit comments

Comments
 (0)