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", 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, "", 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, "", 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 -John50000Jane60000BobAlice70000 +John50000Jane60000BobAlice70000 ~~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 -John50000Jane60000BobAlice70000 +John50000Jane60000BobAlice70000 ~~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