Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions tasks/docs/generate/test_xml_to_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,49 @@ def memberdef(schema, fn):

print("✓ Internal-schema private detection test passed")

def test_schema_name_misparse_is_skipped():
"""Name-dropped CREATE FUNCTION mis-parses are skipped, operators are kept.

Doxygen drops the real name of `CREATE FUNCTION <schema>.<name>(... a
<schema>.<domain> ...)`, leaving the schema as <name>. These internal
"Unsupported operator blocker" helpers carry the word "operator" in their
brief, so the skip must key on <definition> (CREATE FUNCTION), not the
brief — otherwise they'd be mis-remapped to a junk operator name and
mislabeled public. A genuine CREATE OPERATOR is still recovered.
"""
from xml.etree import ElementTree as ET

process_function = _load_process_function()

# Mis-parsed CREATE FUNCTION (schema as name, "operator" in the brief).
misfn = ET.fromstring('''
<memberdef kind="function">
<name>eql_v3_internal</name>
<definition>CREATE FUNCTION eql_v3_internal</definition>
<type>CREATE FUNCTION</type>
Comment on lines +218 to +221
<argsstring>(a jsonb, b public.text_ord) RETURNS jsonb</argsstring>
<briefdescription><para>Unsupported operator blocker for public.text_ord.</para></briefdescription>
<detaileddescription></detaileddescription>
</memberdef>
''')
assert process_function(misfn) is None, "name-dropped CREATE FUNCTION must be skipped"

# Genuine CREATE OPERATOR — recover the symbol from the brief, keep it.
op = ET.fromstring('''
<memberdef kind="function">
<name>public</name>
<definition>CREATE OPERATOR public.-&gt;&gt;</definition>
<type>CREATE OPERATOR</type>
<argsstring>(public.json, text)</argsstring>
<briefdescription><para>-&gt;&gt; operator with text selector.</para></briefdescription>
<detaileddescription></detaileddescription>
</memberdef>
''')
res = process_function(op)
assert res is not None and res["name"] == "->>", "CREATE OPERATOR symbol must be recovered"

print("✓ Schema-name mis-parse skip test passed")

if __name__ == '__main__':
print("Running xml-to-markdown tests...\n")

Expand All @@ -208,6 +251,7 @@ def memberdef(schema, fn):
test_param_name_type_swap()
test_schema_qualified_type()
test_internal_schema_is_private()
test_schema_name_misparse_is_skipped()

print("\n✅ All tests passed!")
sys.exit(0)
Expand Down
38 changes: 27 additions & 11 deletions tasks/docs/generate/xml-to-markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,17 +191,34 @@ def process_function(memberdef):
if func_name.upper() in sql_intrinsics:
return None

# For SQL operators, Doxygen uses schema name as function name
# Extract actual operator from brief description
# Doxygen puts a SCHEMA where the function name should be in two cases,
# told apart by the <definition> (CREATE OPERATOR vs CREATE FUNCTION):
# 1. CREATE OPERATOR — Doxygen names it by schema and puts the operator
# symbol in the brief ("->> operator with ..."); recover it.
# 2. CREATE FUNCTION <schema>.<name>(... <schema>.<domain> ...) where a
# schema-qualified operand type derails the C++ parser: it drops the
# real name, leaving the schema as <name>. Unrecoverable, and these are
# the internal "Unsupported operator blocker" helpers — skip them.
# NB their brief contains the word "operator" ("Unsupported operator
# blocker for ..."), so the skip must key on <definition>, NOT the
# brief, or they'd be mis-remapped to a junk operator name. Left in,
# ~hundreds surface as bogus `eql_v3_internal` functions mislabeled
# public.
brief_elem = memberdef.find('briefdescription')
if func_name in ['eql_v2', 'eql_v3', 'public'] and brief_elem is not None:
brief_para = brief_elem.find('para')
if brief_para is not None and brief_para.text:
# Check if brief starts with an operator (like "->>" or "->")
import re
op_match = re.match(r'^([^\s]+)\s+operator', brief_para.text.strip())
if op_match:
func_name = op_match.group(1) # Use operator as function name
if func_name in ['eql_v2', 'eql_v3', 'eql_v3_internal', 'public']:
definition = extract_para_text(memberdef.find('definition'))
if definition.upper().startswith('CREATE FUNCTION'):
return None # name-dropped CREATE FUNCTION mis-parse
Comment on lines +209 to +211
brief_para = brief_elem.find('para') if brief_elem is not None else None
op_match = (
re.match(r'^([^\s]+)\s+operator', brief_para.text.strip())
if brief_para is not None and brief_para.text
else None
)
if op_match:
func_name = op_match.group(1) # CREATE OPERATOR: use the operator symbol
else:
return None # unrecoverable schema-named mis-parse

# Check if this is a private/internal function.
# Internal functions live in the `eql_v3_internal` schema. Doxygen puts the
Expand Down Expand Up @@ -286,7 +303,6 @@ def process_function(memberdef):

if argsstring is not None and argsstring.text:
# Look for RETURNS keyword in argsstring
import re
returns_match = re.search(r'RETURNS\s+([^\s]+)', argsstring.text)
if returns_match:
return_type_text = returns_match.group(1)
Expand Down
Loading