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
126 changes: 126 additions & 0 deletions meta/type-scope.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"$schema": "./type-scope.schema.json",
"description": "The SQL type scope of MEOS functions whose scope MEOS itself does not state. A function sharing a PG wrapper with others receives only the wrapper's signatures that its scope covers; parser/typescope.py derives that scope from a VALIDATE_* macro, MeosType literals, a class predicate or the C parameter types, and every claimant it cannot derive must be declared here. A scope of \"*\" means the function genuinely serves every overload its wrapper declares.",
"scopes": {
"temporal_append_tinstant": {
"types": "*",
"note": "Generic over Temporal *: appends to any temporal type."
},
"temporal_at_values": {
"types": "*",
"note": "Generic over Temporal *: restricts any temporal type to a value set."
},
"temporal_minus_values": {
"types": "*",
"note": "Generic over Temporal *: the complement of temporal_at_values."
},
"temporal_end_instant": {
"types": "*",
"note": "Generic over Temporal *: accessor on any temporal type."
},
"temporal_start_instant": {
"types": "*",
"note": "Generic over Temporal *: accessor on any temporal type."
},
"temporal_end_sequence": {
"types": "*",
"note": "Generic over Temporal *: accessor on any temporal type."
},
"temporal_start_sequence": {
"types": "*",
"note": "Generic over Temporal *: accessor on any temporal type."
},
"temporal_set_interp": {
"types": "*",
"note": "Generic over Temporal *: sets the interpolation of any temporal type."
},
"temporal_shift_time": {
"types": "*",
"note": "Generic over Temporal *: shifts the time of any temporal type."
},
"temporal_shift_scale_time": {
"types": "*",
"note": "Generic over Temporal *: shifts and scales the time of any temporal type."
},
"spanset_span": {
"types": "*",
"note": "Generic over SpanSet *: returns the bounding span of any spanset type."
},
"span_lower_inc": {
"types": "*",
"note": "Generic over Span *: reports whether the lower bound of any span type is inclusive."
},
"span_upper_inc": {
"types": "*",
"note": "Generic over Span *: reports whether the upper bound of any span type is inclusive."
},

"trgeometry_at_values": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone. It states no validity check of its own, so the scope cannot be read from the body — see the rgeo restrict functions, which delegate without validating."
},
"trgeometry_minus_values": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},
"trgeometry_at_tstzspan": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},
"trgeometry_at_tstzset": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},
"trgeometry_at_tstzspanset": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},
"trgeometry_minus_tstzspan": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},
"trgeometry_minus_tstzset": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},
"trgeometry_minus_tstzspanset": {
"types": ["trgeometry"],
"note": "Serves trgeometry alone; states no validity check."
},

"tintbox_shift_scale": {
"types": ["tbox"],
"note": "Shifts and scales a tbox. Its body names the value span it moves (T_INTSPAN), so the span is what a body-read derives — the type it serves is the box."
},
"tbigintbox_shift_scale": {
"types": ["tbox"],
"note": "Shifts and scales a tbox; the body names its value span, not the box."
},
"tfloatbox_shift_scale": {
"types": ["tbox"],
"note": "Shifts and scales a tbox; the body names its value span, not the box."
},

"nad_tint_tbox": {
"types": ["tint"],
"note": "The tint arm of the tnumber/tbox nearest-approach pair."
},
"nad_tfloat_tbox": {
"types": ["tfloat"],
"note": "The tfloat arm of the tnumber/tbox nearest-approach pair."
},
"nad_tcbuffer_stbox": {
"types": ["tcbuffer"],
"note": "Serves tcbuffer. Its @csqlfn names NAD_tcbuffer_geo, the wrapper of nad_tcbuffer_geo; the stbox form has its own wrapper NAD_tcbuffer_stbox. Upstream tag fix, recorded here so generation stays deterministic meanwhile."
},

"span_to_spanset": {
"types": [],
"note": "Converts span to spanset, while its @csqlfn names Spanset_to_span — the wrapper of the opposite conversion, whose overloads are all spanset-argument. No overload of that wrapper is served, hence the empty scope. Upstream tag fix."
},
"contained_span_span": {
"types": [],
"note": "Compares span with span, while its @csqlfn names Contained_value_span, whose overloads all take a base value. No overload of that wrapper is served. Upstream tag fix."
}
}
}
40 changes: 40 additions & 0 deletions meta/type-scope.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "type-scope.schema.json",
"title": "MEOS function type scopes",
"description": "Declared SQL type scopes for MEOS functions whose scope MEOS itself does not state. Consumed by parser/typescope.py, which fails generation on any underivable claimant absent from this file.",
"type": "object",
"required": ["scopes"],
"additionalProperties": false,
"properties": {
"$schema": { "type": "string" },
"description": { "type": "string" },
"scopes": {
"type": "object",
"description": "Keyed by MEOS function name.",
"additionalProperties": {
"type": "object",
"required": ["types", "note"],
"additionalProperties": false,
"properties": {
"types": {
"description": "The SQL types the function serves: a list of type names, or \"*\" when it serves every overload its wrapper declares. An empty list means it serves none of them, which is what a mistagged @csqlfn looks like.",
"oneOf": [
{ "type": "string", "const": "*" },
{
"type": "array",
"items": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" },
"uniqueItems": true
}
]
},
"note": {
"type": "string",
"description": "Why the scope is what it is, and why MEOS does not state it.",
"minLength": 1
}
}
}
}
}
}
47 changes: 46 additions & 1 deletion parser/sqlfn.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
import re
from pathlib import Path

from parser.typescope import (TypeFacts, declared_scopes, read_bodies,
require_scopes, resolve_scope, signatures_for)

# A @csqlfn tag carries one OR MORE #Wrapper() references — comma- or
# space-separated, and possibly continued across doxygen lines — because a single
# MEOS function can back several wrappers (the ever/always pair eDisjoint/aDisjoint
Expand Down Expand Up @@ -285,6 +288,32 @@ def attach_sqlfn_map(idl, meos_src, mdb_src, sql_src=None):
w2sig = _wrapper_sql_sigs(sql_src) if sql_src else {}
direct = _meos_direct_sql(meos_src)
n = 0
# One wrapper commonly backs a whole per-type family — `Set_values` is the body
# behind getValues(intset), getValues(cbufferset) and fourteen more — so a
# wrapper's signature list is the union over its claimants, not the surface of
# any one of them. Each function therefore keeps only the signatures its own
# TYPE SCOPE covers; scopes MEOS does not state are declared in
# meta/type-scope.json, and an underivable claimant fails generation rather
# than silently taking the union or nothing.
scope_facts = scope_bodies = scope_params = None
declared = {}
shared_wrappers = set()
if w2sig:
meos_root = Path(meos_src).parent
scope_facts = TypeFacts(meos_root)
scope_bodies, scope_params = read_bodies(meos_root)
declared = declared_scopes()
claimed = {}
for f in idl["functions"]:
if f.get("api") != "public":
continue
for w in m2d.get(f["name"]) or ():
claimed.setdefault(w, []).append(f["name"])
break
shared_wrappers = {w for w, names in claimed.items()
if len(names) > 1 and len(w2sig.get(w) or ()) > 1}
require_scopes([n for w in shared_wrappers for n in claimed[w]],
scope_facts, scope_bodies, scope_params, declared)
# Transient map: MEOS function name -> every SQL name it resolves to, for the
# functions that fan out (a shared wrapper / ever-always pair). This is NOT
# catalog output — every binding reads only the primary `sqlfn` — it is working
Expand Down Expand Up @@ -320,6 +349,16 @@ def attach_sqlfn_map(idl, meos_src, mdb_src, sql_src=None):
# signature instead of the wider C one: args beyond sqlArity are SQL-optional
# (DEFAULT), and C params beyond sqlArityMax are C-only out-params.
sigs = w2sig.get(wrappers[0])
# Only a public claimant of a shared wrapper is filtered: those are the
# functions a binding projects, and the ones require_scopes has proven a
# scope for. An internal function is not part of any binding surface.
scoped = False
if sigs and wrappers[0] in shared_wrappers and f.get("api") == "public":
scope, _ = resolve_scope(f["name"], scope_facts, scope_bodies,
scope_params, declared)
if scope is not None:
sigs = signatures_for(f["name"], sigs, scope)
scoped = True
if sigs:
f["sqlArity"] = min(s["required"] for s in sigs)
f["sqlArityMax"] = max(len(s["args"]) for s in sigs)
Expand Down Expand Up @@ -349,8 +388,14 @@ def attach_sqlfn_map(idl, meos_src, mdb_src, sql_src=None):
# attached ONLY for a signature that actually has an optional arg, so a binding
# can render the shorter overload of a SQL-optional argument with its omitted
# value; default-free signatures stay {args, ret} unchanged.
# Scope filtering already reduced `sigs` to the overloads this function
# serves, and a per-type function's own overload carries its OWN SQL name
# (`bigintset_in`), not the representative the @sqlfn tag names
# (`intset_in`). Dropping a name that differs from `sqlfn` would discard
# exactly the signature the filter just proved belongs here, so a filtered
# function keeps all of them and stamps the name whenever it differs.
fam_names = {s["sqlName"] for s in sigs}
multiname = len(fam_names) > 1
multiname = len(fam_names) > 1 or (scoped and fam_names != {f["sqlfn"]})
own = []
for s in sigs:
if not multiname and s["sqlName"] != f["sqlfn"]:
Expand Down
Loading
Loading