Produce a traversal of the given expression, delivering column comparisons to the given function. The function is of the form:: def my_fn(binary, left, right): ... For each binary expression located which has a comparison operator, the product of "left" and "right" wil
(
fn: Callable[
[BinaryExpression[Any], ColumnElement[Any], ColumnElement[Any]], None
],
expr: ColumnElement[Any],
)
| 258 | |
| 259 | |
| 260 | def visit_binary_product( |
| 261 | fn: Callable[ |
| 262 | [BinaryExpression[Any], ColumnElement[Any], ColumnElement[Any]], None |
| 263 | ], |
| 264 | expr: ColumnElement[Any], |
| 265 | ) -> None: |
| 266 | """Produce a traversal of the given expression, delivering |
| 267 | column comparisons to the given function. |
| 268 | |
| 269 | The function is of the form:: |
| 270 | |
| 271 | def my_fn(binary, left, right): ... |
| 272 | |
| 273 | For each binary expression located which has a |
| 274 | comparison operator, the product of "left" and |
| 275 | "right" will be delivered to that function, |
| 276 | in terms of that binary. |
| 277 | |
| 278 | Hence an expression like:: |
| 279 | |
| 280 | and_((a + b) == q + func.sum(e + f), j == r) |
| 281 | |
| 282 | would have the traversal: |
| 283 | |
| 284 | .. sourcecode:: text |
| 285 | |
| 286 | a <eq> q |
| 287 | a <eq> e |
| 288 | a <eq> f |
| 289 | b <eq> q |
| 290 | b <eq> e |
| 291 | b <eq> f |
| 292 | j <eq> r |
| 293 | |
| 294 | That is, every combination of "left" and |
| 295 | "right" that doesn't further contain |
| 296 | a binary comparison is passed as pairs. |
| 297 | |
| 298 | """ |
| 299 | stack: List[BinaryExpression[Any]] = [] |
| 300 | |
| 301 | def visit(element: ClauseElement) -> Iterator[ColumnElement[Any]]: |
| 302 | if isinstance(element, ScalarSelect): |
| 303 | # we don't want to dig into correlated subqueries, |
| 304 | # those are just column elements by themselves |
| 305 | yield element |
| 306 | elif element.__visit_name__ == "binary" and operators.is_comparison( |
| 307 | element.operator # type: ignore |
| 308 | ): |
| 309 | stack.insert(0, element) # type: ignore |
| 310 | for l in visit(element.left): # type: ignore |
| 311 | for r in visit(element.right): # type: ignore |
| 312 | fn(stack[0], l, r) |
| 313 | stack.pop(0) |
| 314 | for elem in element.get_children(): |
| 315 | visit(elem) |
| 316 | else: |
| 317 | if isinstance(element, ColumnClause): |