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
60 changes: 59 additions & 1 deletion parser/sqlfn.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,57 @@
_SQLFN = re.compile(r"@sqlfn\s+(\w+)\s*\(\)")
_SQLOP = re.compile(r"@sqlop\s+@p\s+(\S+)")
_DATUM = re.compile(r"Datum\s+(\w+)\s*\(\s*PG_FUNCTION_ARGS")
# `CREATE [OR REPLACE] FUNCTION name(` — the SQL-facing signature; the wrapper it
# binds is in the trailing `AS 'MODULE_PATHNAME', '<Wrapper>'`.
_CREATE_FN = re.compile(r"CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+(\w+)\s*\(", re.I)
_AS_WRAPPER = re.compile(r"AS\s+'[^']*'\s*,\s*'(\w+)'", re.I)


def _split_top_commas(s):
out, depth, cur = [], 0, ""
for ch in s:
if ch in "([":
depth += 1
elif ch in ")]":
depth -= 1
if ch == "," and depth == 0:
out.append(cur)
cur = ""
else:
cur += ch
if cur.strip():
out.append(cur)
return out


def _wrapper_sql_sigs(sql_src):
"""MobilityDB-C wrapper name -> (sqlArity, sqlArityMax) from the CREATE FUNCTION
definitions. The SQL signature is the binding-facing arity, NOT the C one: an arg
with a DEFAULT clause is optional, and any trailing C param absent from the SQL form
is a C-only out-param (e.g. the `size_t *` of the *_as_hexwkb family). So a generator
binding the @sqlfn name must expose sqlArity..sqlArityMax args, NOT the full C list.
Across overloads of one wrapper, take min-required / max-total."""
out = {}
sql_src = Path(sql_src)
if not sql_src.exists():
return out
for sf in sorted(sql_src.rglob("*.sql")):
text = sf.read_text(errors="ignore")
for m in _CREATE_FN.finditer(text):
i, depth, start = m.end(), 1, m.end()
while i < len(text) and depth:
depth += (text[i] == "(") - (text[i] == ")")
i += 1
wm = _AS_WRAPPER.search(text, i, i + 400)
if not wm:
continue # SQL-language ($$...$$) wrapper has no C symbol — skip
wrapper = wm.group(1)
args = [a for a in _split_top_commas(text[start:i - 1]) if a.strip()]
required = sum(1 for a in args if not re.search(r"\bDEFAULT\b", a, re.I))
prev = out.get(wrapper)
out[wrapper] = ((min(prev[0], required), max(prev[1], len(args)))
if prev else (required, len(args)))
return out


def _meos_to_mdb(meos_src):
Expand Down Expand Up @@ -85,9 +136,10 @@ def _mdb_to_sql(mdb_src):
return out


def attach_sqlfn_map(idl, meos_src, mdb_src):
def attach_sqlfn_map(idl, meos_src, mdb_src, sql_src=None):
m2d = _meos_to_mdb(meos_src)
d2s = _mdb_to_sql(mdb_src)
w2sig = _wrapper_sql_sigs(sql_src) if sql_src else {}
n = 0
for f in idl["functions"]:
wrappers = m2d.get(f["name"])
Expand All @@ -105,6 +157,12 @@ def attach_sqlfn_map(idl, meos_src, mdb_src):
continue
f["mdbC"] = wrappers[0]
f["sqlfn"] = pairs[0][0]
# The SQL-facing arity (required..total). Lets a generator expose the SQL
# signature instead of the wider C one: args beyond sqlArity are SQL-optional
# (DEFAULT), and C params beyond sqlArityMax are C-only out-params.
sig = w2sig.get(wrappers[0])
if sig:
f["sqlArity"], f["sqlArityMax"] = sig
if pairs[0][1]:
f["sqlop"] = pairs[0][1]
# Shared wrapper OR ever/always pair exposing >1 SQL name: record them all.
Expand Down
3 changes: 2 additions & 1 deletion run.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,9 @@ def main():
SRC_ROOT = Path(os.environ.get("MDB_SRC_ROOT", "./_mobilitydb"))
MEOS_SRC = SRC_ROOT / "meos" / "src"
MDB_SRC = SRC_ROOT / "mobilitydb" / "src"
SQL_SRC = SRC_ROOT / "mobilitydb" / "sql"
if MEOS_SRC.exists() and MDB_SRC.exists():
idl, nsql = attach_sqlfn_map(idl, MEOS_SRC, MDB_SRC)
idl, nsql = attach_sqlfn_map(idl, MEOS_SRC, MDB_SRC, SQL_SRC)
print(f"[4/4] Attached {nsql} @sqlfn SQL names", file=sys.stderr)
# Guard: a copy-paste @csqlfn in meos/src can point an ever/always function at
# the opposite-prefix wrapper (eintersects_* tagged #Aintersects_*), flipping its
Expand Down
Loading