diff --git a/contrib/babelfishpg_tsql/src/tsql_for/forxml.c b/contrib/babelfishpg_tsql/src/tsql_for/forxml.c
index 5fb09662869..515c10384f2 100644
--- a/contrib/babelfishpg_tsql/src/tsql_for/forxml.c
+++ b/contrib/babelfishpg_tsql/src/tsql_for/forxml.c
@@ -215,7 +215,8 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
TupleDesc tupdesc;
HeapTupleData tmptup;
HeapTuple tuple;
-
+ bool allnull = true;
+
td = DatumGetHeapTupleHeader(record);
/* 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
tmptup.t_data = td;
tuple = &tmptup;
- /* Output opening tag */
- if (elements)
+ /*
+ * Empty element name without ELEMENTS mode is not allowed — attribute-centric
+ * serialization requires a row tag name.
+ */
+ if (element_name[0] == '\0' && !elements)
{
- /* ELEMENTS mode: value
*/
- if (xsinil)
- appendStringInfo(state, "<%s " XML_XMLNS_XSI ">", element_name);
- else
- appendStringInfo(state, "<%s>", element_name);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_XML_PROCESSING_INSTRUCTION),
+ errmsg("Row tag omission (empty row tag name) cannot be used "
+ "with attribute-centric FOR XML serialization.")));
}
- else
+
+ /* Output opening tag (only when element_name is non-empty) */
+ if (element_name[0] != '\0')
{
- /* ATTRIBUTES mode:
*/
- appendStringInfo(state, "<%s", element_name);
+ if (elements)
+ {
+ /* ELEMENTS mode: value
*/
+ if (xsinil)
+ appendStringInfo(state, "<%s " XML_XMLNS_XSI ">", element_name);
+ else
+ appendStringInfo(state, "<%s>", element_name);
+ }
+ else
+ {
+ /* ATTRIBUTES mode:
*/
+ appendStringInfo(state, "<%s", element_name);
+ }
}
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
/* ELEMENTS mode output */
if (!isnull)
{
+ allnull = false;
/* Normal element:
value */
appendStringInfo(state, "<%s>%s%s>",
colname,
@@ -273,6 +290,7 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
}
else if (xsinil)
{
+ allnull = false;
/* XSINIL: */
appendStringInfo(state, "<%s " XML_XSI_NIL "/>", colname);
}
@@ -292,7 +310,29 @@ tsql_row_to_xml_raw(StringInfo state, Datum record, const char *element_name, bo
/* Output closing tag */
if (elements)
- appendStringInfo(state, "%s>", element_name);
+ {
+ if (element_name[0] == '\0')
+ {
+ /*
+ * Empty element name with ELEMENTS: no wrapper tag needed.
+ * Just output the child elements directly, same as PATH('').
+ */
+ }
+ else if (allnull)
+ {
+ /*
+ * If all column values are NULL, produce a self-closing element
+ * like TSQL does:
. Replace the '>' in the already
+ * appended opening tag with '/' and append '>'.
+ */
+ state->data[state->len - 1] = '/';
+ appendStringInfoChar(state, '>');
+ }
+ else
+ {
+ appendStringInfo(state, "%s>", element_name);
+ }
+ }
else
appendStringInfoString(state, "/>");
diff --git a/test/JDBC/expected/forxml-raw-elements-before-17_9-or-18_3-vu-verify.out b/test/JDBC/expected/forxml-raw-elements-before-17_9-or-18_3-vu-verify.out
index 145297f9ae3..fb8156f11a1 100644
--- a/test/JDBC/expected/forxml-raw-elements-before-17_9-or-18_3-vu-verify.out
+++ b/test/JDBC/expected/forxml-raw-elements-before-17_9-or-18_3-vu-verify.out
@@ -143,7 +143,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-
+
~~END~~
@@ -170,7 +170,7 @@ SELECT NULL AS a, NULL AS b, NULL AS c, NULL AS d FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-
+
~~END~~
@@ -198,7 +198,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS ABSENT;
GO
~~START~~
ntext
-
+
~~END~~
@@ -510,7 +510,7 @@ SELECT name, salary FROM forxml_raw_elements_t1 FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-John50000
Jane60000
Bob
Alice70000
+John50000
Jane60000
Bob
Alice70000
~~END~~
@@ -950,7 +950,7 @@ SELECT CAST(NULL AS VARCHAR(10)) AS null_val FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-
+
~~END~~
@@ -971,7 +971,34 @@ SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
GO
~~START~~
ntext
-<>12>
+12
+~~END~~
+
+
+-- Empty element name without ELEMENTS (attribute-centric) - should error
+SELECT 1 AS a, 2 AS b FOR XML RAW('');
+GO
+~~START~~
+ntext
+~~ERROR (Code: 33557097)~~
+
+~~ERROR (Message: Row tag omission (empty row tag name) cannot be used with attribute-centric FOR XML serialization.)~~
+
+
+-- All NULLs under attribute mode (no ELEMENTS)
+SELECT NULL AS a, NULL AS b FOR XML RAW;
+GO
+~~START~~
+ntext
+
+~~END~~
+
+
+-- All NULLs with empty element name and ELEMENTS
+SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
+GO
+~~START~~
+ntext
~~END~~
diff --git a/test/JDBC/expected/forxml-raw-elements-vu-verify.out b/test/JDBC/expected/forxml-raw-elements-vu-verify.out
index ca130b869f0..2f11d2ce97e 100644
--- a/test/JDBC/expected/forxml-raw-elements-vu-verify.out
+++ b/test/JDBC/expected/forxml-raw-elements-vu-verify.out
@@ -143,7 +143,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-
+
~~END~~
@@ -170,7 +170,7 @@ SELECT NULL AS a, NULL AS b, NULL AS c, NULL AS d FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-
+
~~END~~
@@ -198,7 +198,7 @@ SELECT NULL AS a, NULL AS b FOR XML RAW, ELEMENTS ABSENT;
GO
~~START~~
ntext
-
+
~~END~~
@@ -510,7 +510,7 @@ SELECT name, salary FROM forxml_raw_elements_t1 FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-John50000
Jane60000
Bob
Alice70000
+John50000
Jane60000
Bob
Alice70000
~~END~~
@@ -950,7 +950,7 @@ SELECT CAST(NULL AS VARCHAR(10)) AS null_val FOR XML RAW, ELEMENTS;
GO
~~START~~
ntext
-
+
~~END~~
@@ -971,7 +971,34 @@ SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
GO
~~START~~
ntext
-<>12>
+12
+~~END~~
+
+
+-- Empty element name without ELEMENTS (attribute-centric) - should error
+SELECT 1 AS a, 2 AS b FOR XML RAW('');
+GO
+~~START~~
+ntext
+~~ERROR (Code: 33557097)~~
+
+~~ERROR (Message: Row tag omission (empty row tag name) cannot be used with attribute-centric FOR XML serialization.)~~
+
+
+-- All NULLs under attribute mode (no ELEMENTS)
+SELECT NULL AS a, NULL AS b FOR XML RAW;
+GO
+~~START~~
+ntext
+
+~~END~~
+
+
+-- All NULLs with empty element name and ELEMENTS
+SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
+GO
+~~START~~
+ntext
~~END~~
diff --git a/test/JDBC/input/forxml/forxml-raw-elements-before-17_9-or-18_3-vu-verify.sql b/test/JDBC/input/forxml/forxml-raw-elements-before-17_9-or-18_3-vu-verify.sql
index e6500d5f9eb..5fa2deb7fd9 100644
--- a/test/JDBC/input/forxml/forxml-raw-elements-before-17_9-or-18_3-vu-verify.sql
+++ b/test/JDBC/input/forxml/forxml-raw-elements-before-17_9-or-18_3-vu-verify.sql
@@ -414,6 +414,18 @@ GO
SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
GO
+-- Empty element name without ELEMENTS (attribute-centric) - should error
+SELECT 1 AS a, 2 AS b FOR XML RAW('');
+GO
+
+-- All NULLs under attribute mode (no ELEMENTS)
+SELECT NULL AS a, NULL AS b FOR XML RAW;
+GO
+
+-- All NULLs with empty element name and ELEMENTS
+SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
+GO
+
-- Element name with spaces
SELECT 1 AS a FOR XML RAW('element name'), ELEMENTS;
GO
diff --git a/test/JDBC/input/forxml/forxml-raw-elements-vu-verify.sql b/test/JDBC/input/forxml/forxml-raw-elements-vu-verify.sql
index 06b3beceb58..986111c36d2 100644
--- a/test/JDBC/input/forxml/forxml-raw-elements-vu-verify.sql
+++ b/test/JDBC/input/forxml/forxml-raw-elements-vu-verify.sql
@@ -414,6 +414,18 @@ GO
SELECT 1 AS a, 2 AS b FOR XML RAW(''), ELEMENTS;
GO
+-- Empty element name without ELEMENTS (attribute-centric) - should error
+SELECT 1 AS a, 2 AS b FOR XML RAW('');
+GO
+
+-- All NULLs under attribute mode (no ELEMENTS)
+SELECT NULL AS a, NULL AS b FOR XML RAW;
+GO
+
+-- All NULLs with empty element name and ELEMENTS
+SELECT NULL AS a, NULL AS b FOR XML RAW(''), ELEMENTS;
+GO
+
-- Element name with spaces
SELECT 1 AS a FOR XML RAW('element name'), ELEMENTS;
GO