1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
# A script to create the automatic casts for overlaps and intersects
# between MOCs and spolys/scircles.
#
# This has originally been used to create pg_sphere--1.2.0--1.2.1.sql.
# Before 1.2.1 is release, this can be fixed to improve that SQL.
# After the 1.2.1 release, this is just documentation on how MOC
# casts were generated that is perhaps just a bit more readable than
# that bunch of SQL.
import datetime
import re
import sys
OVERLAP_DEFS = [
# func_stem, operator, commutator
('subset', '<@', '@>'),
('not_subset', '!<@', '!@>'),
('superset', '@>', '<@'),
('not_superset', '!@>', '!<@'),
]
INTERSECT_DEFS = [
# func_stem, operator, commutator
('intersect', '&&', '&&'),
('not_intersect', '!&&', '!&&'),
]
GEO_TYPES = ["scircle", "spoly"]
OP_DEFS = OVERLAP_DEFS
class Accum:
"""an accumulator for our output.
"""
def __init__(self):
self.parts = []
@property
def content(self):
return "".join(self.parts)
def write(self, s):
self.parts.append(s)
def writeln(self, *strings):
self.parts.append("\n".join(strings)+"\n")
def replace_last(self, subs):
"""replaces the last non-whitespace char with the string subs.
"""
for index, part in enumerate(reversed(self.parts)):
if part.strip():
break
else:
# nothing to replace
return
index = -1-index
self.parts[index] = re.sub("[^\s](\s*)$",
lambda mat: subs+mat.group(1),
self.parts[index])
def introduce_section(self, sec_name):
self.writeln()
self.writeln("-- #################################")
self.writeln(f"-- {sec_name}")
def emit_drop_code(accum):
accum.introduce_section("Cleanup")
accum.writeln("DROP OPERATOR IF EXISTS")
for _, op, _ in OP_DEFS:
for geo_type in GEO_TYPES:
accum.writeln(f" {op} (smoc, {geo_type}),")
accum.writeln(f" {op} ({geo_type}, smoc),")
accum.replace_last(";")
def make_negator(op):
if op.startswith("!"):
return op[1:]
else:
return "!"+op
def emit_op_def(accum, operator, leftarg, rightarg, procedure, commutator):
accum.writeln(
f"CREATE OPERATOR {operator} (",
f" LEFTARG = {leftarg},",
f" RIGHTARG = {rightarg},",
f" PROCEDURE = {procedure},",
f" COMMUTATOR = '{commutator}',",
f" NEGATOR = '{make_negator(operator)}',",
f" RESTRICT = contsel,",
f" JOIN = contjoinsel",
f");")
def emit_op_and_func(accum, op_def):
func_stem, operator, commutator = op_def
for geo_type in GEO_TYPES:
func_name = f"{geo_type}_{func_stem}_smoc"
accum.writeln(
f"CREATE OR REPLACE FUNCTION {func_name}(",
f" geo_arg {geo_type}, a_moc smoc) RETURNS BOOL AS $body$",
f" SELECT smoc(max_order(a_moc), geo_arg) {operator} a_moc",
f" $body$ LANGUAGE SQL STABLE;")
emit_op_def(accum, operator,
geo_type, "smoc",
func_name,
commutator)
accum.writeln()
func_name = f"smoc_{func_stem}_{geo_type}"
accum.writeln(
f"CREATE OR REPLACE FUNCTION {func_name}(",
f" a_moc smoc, geo_arg {geo_type}) RETURNS BOOL AS $body$",
f" SELECT a_moc {operator} smoc(max_order(a_moc), geo_arg)",
f" $body$ LANGUAGE SQL STABLE;")
emit_op_def(accum, operator,
"smoc", geo_type,
func_name,
commutator)
accum.writeln()
def main():
accum = Accum()
accum.writeln("-- MOC/geometry automatic casts.")
accum.writeln(f"-- Generated {datetime.date.today()} by {sys.argv[0]}.")
accum.writeln(f"-- Re-generation needs to be triggered manually.")
accum.writeln()
emit_drop_code(accum)
accum.introduce_section(" smoc/geo OVERLAPS")
for op_def in OVERLAP_DEFS:
emit_op_and_func(accum, op_def)
accum.writeln()
accum.introduce_section(" smoc/geo INTERSECTS")
for op_def in INTERSECT_DEFS:
emit_op_and_func(accum, op_def)
accum.writeln()
print(accum.content)
if __name__=="__main__":
main()
|