|
10 | 10 | from sqlalchemy.sql import util as sql_util |
11 | 11 | from sqlalchemy.sql import between |
12 | 12 | from sqlalchemy.sql import func |
| 13 | +from sqlalchemy.sql.functions import ReturnTypeFromArgs |
13 | 14 | from sqlalchemy.sql import expression |
| 15 | +from sqlalchemy.sql import schema |
14 | 16 | from sqlalchemy import sql, text |
15 | 17 | from sqlalchemy import util |
16 | 18 | from sqlalchemy import types as sqltypes |
@@ -406,59 +408,89 @@ def _use_top(self, select): |
406 | 408 | or select._simple_int_clause(select._fetch_clause) |
407 | 409 | ) |
408 | 410 |
|
409 | | - def translate_select_structure(self, select_stmt, **kwargs): |
410 | | - """Look for ``LIMIT`` and OFFSET in a select statement, and if |
411 | | - so tries to wrap it in a subquery with ``row_number()`` criterion. |
| 411 | + def visit_irisexact_func(self, fn, **kw): |
| 412 | + return "%EXACT" + self.function_argspec(fn) |
412 | 413 |
|
| 414 | + def _use_exact_for_ordered_string(self, select): |
| 415 | + """ |
| 416 | + `SELECT string_value FROM some_table ORDER BY string_value` |
| 417 | + Will return `string_value` in uppercase |
| 418 | + So, this method fixes query to use %EXACT() function |
| 419 | + `SELECT %EXACT(string_value) AS string_value FROM some_table ORDER BY string_value` |
413 | 420 | """ |
| 421 | + def _add_exact(column): |
| 422 | + if isinstance(column.type, sqltypes.String): |
| 423 | + return IRISExact(column).label(column._label if column._label else column.name) |
| 424 | + return column |
| 425 | + |
| 426 | + _order_by_clauses = [ |
| 427 | + sql_util.unwrap_label_reference(elem) |
| 428 | + for elem in select._order_by_clause.clauses |
| 429 | + ] |
| 430 | + if _order_by_clauses: |
| 431 | + select._raw_columns = [ |
| 432 | + (_add_exact(c) if isinstance(c, schema.Column) and c in _order_by_clauses else c) |
| 433 | + for c in select._raw_columns |
| 434 | + ] |
| 435 | + |
| 436 | + return select |
| 437 | + |
| 438 | + def translate_select_structure(self, select_stmt, **kwargs): |
414 | 439 | select = select_stmt |
| 440 | + if getattr(select, "_iris_visit", None) is True: |
| 441 | + return select |
415 | 442 |
|
416 | | - if ( |
| 443 | + select._iris_visit = True |
| 444 | + select = select._generate() |
| 445 | + |
| 446 | + select = self._use_exact_for_ordered_string(select) |
| 447 | + |
| 448 | + if not ( |
417 | 449 | select._has_row_limiting_clause |
418 | 450 | and not self._use_top(select) |
419 | | - and not getattr(select, "_iris_visit", None) |
420 | 451 | ): |
421 | | - _order_by_clauses = [ |
422 | | - sql_util.unwrap_label_reference(elem) |
423 | | - for elem in select._order_by_clause.clauses |
424 | | - ] |
425 | | - |
426 | | - if not _order_by_clauses: |
427 | | - _order_by_clauses = [text('%id')] |
| 452 | + return select |
428 | 453 |
|
429 | | - limit_clause = self._get_limit_or_fetch(select) |
430 | | - offset_clause = select._offset_clause |
| 454 | + """Look for ``LIMIT`` and OFFSET in a select statement, and if |
| 455 | + so tries to wrap it in a subquery with ``row_number()`` criterion. |
431 | 456 |
|
432 | | - select = select._generate() |
433 | | - select._iris_visit = True |
434 | | - label = "iris_rn" |
435 | | - select = ( |
436 | | - select.add_columns( |
437 | | - sql.func.ROW_NUMBER() |
438 | | - .over(order_by=_order_by_clauses) |
439 | | - .label(label) |
440 | | - ) |
441 | | - .order_by(None) |
442 | | - .alias() |
| 457 | + """ |
| 458 | + _order_by_clauses = [ |
| 459 | + sql_util.unwrap_label_reference(elem) |
| 460 | + for elem in select._order_by_clause.clauses |
| 461 | + ] |
| 462 | + if not _order_by_clauses: |
| 463 | + _order_by_clauses = [text('%id')] |
| 464 | + |
| 465 | + limit_clause = self._get_limit_or_fetch(select) |
| 466 | + offset_clause = select._offset_clause |
| 467 | + |
| 468 | + label = "iris_rn" |
| 469 | + select = ( |
| 470 | + select.add_columns( |
| 471 | + sql.func.ROW_NUMBER() |
| 472 | + .over(order_by=_order_by_clauses) |
| 473 | + .label(label) |
443 | 474 | ) |
| 475 | + .order_by(None) |
| 476 | + .alias() |
| 477 | + ) |
444 | 478 |
|
445 | | - iris_rn = sql.column(label) |
446 | | - limitselect = sql.select( |
447 | | - *[c for c in select.c if c.key != label] |
448 | | - ) |
449 | | - if offset_clause is not None: |
450 | | - if limit_clause is not None: |
451 | | - limitselect = limitselect.where( |
452 | | - between(iris_rn, offset_clause + 1, |
453 | | - limit_clause + offset_clause) |
454 | | - ) |
455 | | - else: |
456 | | - limitselect = limitselect.where(iris_rn > offset_clause) |
| 479 | + iris_rn = sql.column(label) |
| 480 | + limitselect = sql.select( |
| 481 | + *[c for c in select.c if c.key != label] |
| 482 | + ) |
| 483 | + if offset_clause is not None: |
| 484 | + if limit_clause is not None: |
| 485 | + limitselect = limitselect.where( |
| 486 | + between(iris_rn, offset_clause + 1, |
| 487 | + limit_clause + offset_clause) |
| 488 | + ) |
457 | 489 | else: |
458 | | - limitselect = limitselect.where(iris_rn <= (limit_clause)) |
459 | | - return limitselect |
| 490 | + limitselect = limitselect.where(iris_rn > offset_clause) |
460 | 491 | else: |
461 | | - return select |
| 492 | + limitselect = limitselect.where(iris_rn <= (limit_clause)) |
| 493 | + return limitselect |
462 | 494 |
|
463 | 495 | def order_by_clause(self, select, **kw): |
464 | 496 | order_by = self.process(select._order_by_clause, **kw) |
@@ -488,6 +520,7 @@ def visit_drop_schema(self, drop, **kw): |
488 | 520 |
|
489 | 521 | def visit_check_constraint(self, constraint, **kw): |
490 | 522 | raise exc.CompileError("Check CONSTRAINT not supported") |
| 523 | + # pass |
491 | 524 |
|
492 | 525 | def visit_computed_column(self, generated, **kwargs): |
493 | 526 | text = self.sql_compiler.process( |
@@ -591,6 +624,12 @@ def create_cursor(self): |
591 | 624 | } |
592 | 625 |
|
593 | 626 |
|
| 627 | +class IRISExact(ReturnTypeFromArgs): |
| 628 | + """The IRIS SQL %EXACT() function.""" |
| 629 | + |
| 630 | + inherit_cache = True |
| 631 | + |
| 632 | + |
594 | 633 | class IRISDialect(default.DefaultDialect): |
595 | 634 |
|
596 | 635 | name = 'iris' |
|
0 commit comments