1from sqlalchemy.testing import assert_raises_message, eq_, \ 2 AssertsCompiledSQL, is_ 3from sqlalchemy.testing import fixtures 4from sqlalchemy.orm import relationships, foreign, remote 5from sqlalchemy import MetaData, Table, Column, ForeignKey, Integer, \ 6 select, ForeignKeyConstraint, exc, func, and_, String, Boolean 7from sqlalchemy.orm.interfaces import ONETOMANY, MANYTOONE, MANYTOMANY 8from sqlalchemy.testing import mock 9 10 11class _JoinFixtures(object): 12 @classmethod 13 def setup_class(cls): 14 m = MetaData() 15 cls.left = Table('lft', m, 16 Column('id', Integer, primary_key=True), 17 Column('x', Integer), 18 Column('y', Integer)) 19 cls.right = Table('rgt', m, 20 Column('id', Integer, primary_key=True), 21 Column('lid', Integer, ForeignKey('lft.id')), 22 Column('x', Integer), 23 Column('y', Integer)) 24 cls.right_multi_fk = Table('rgt_multi_fk', m, 25 Column('id', Integer, primary_key=True), 26 Column('lid1', Integer, 27 ForeignKey('lft.id')), 28 Column('lid2', Integer, 29 ForeignKey('lft.id'))) 30 31 cls.selfref = Table('selfref', m, 32 Column('id', Integer, primary_key=True), 33 Column('sid', Integer, ForeignKey('selfref.id'))) 34 cls.composite_selfref = Table('composite_selfref', m, 35 Column('id', Integer, primary_key=True), 36 Column('group_id', Integer, 37 primary_key=True), 38 Column('parent_id', Integer), 39 ForeignKeyConstraint( 40 ['parent_id', 'group_id'], 41 ['composite_selfref.id', 42 'composite_selfref.group_id'])) 43 cls.m2mleft = Table('m2mlft', m, 44 Column('id', Integer, primary_key=True)) 45 cls.m2mright = Table('m2mrgt', m, 46 Column('id', Integer, primary_key=True)) 47 cls.m2msecondary = Table('m2msecondary', m, 48 Column('lid', Integer, ForeignKey( 49 'm2mlft.id'), primary_key=True), 50 Column('rid', Integer, ForeignKey( 51 'm2mrgt.id'), primary_key=True)) 52 cls.m2msecondary_no_fks = Table('m2msecondary_no_fks', m, 53 Column('lid', Integer, 54 primary_key=True), 55 Column('rid', Integer, 56 primary_key=True)) 57 cls.m2msecondary_ambig_fks = Table('m2msecondary_ambig_fks', m, 58 Column('lid1', Integer, ForeignKey( 59 'm2mlft.id'), primary_key=True), 60 Column('rid1', Integer, ForeignKey( 61 'm2mrgt.id'), primary_key=True), 62 Column('lid2', Integer, ForeignKey( 63 'm2mlft.id'), primary_key=True), 64 Column('rid2', Integer, ForeignKey( 65 'm2mrgt.id'), primary_key=True)) 66 cls.base_w_sub_rel = Table('base_w_sub_rel', m, 67 Column('id', Integer, primary_key=True), 68 Column('sub_id', Integer, 69 ForeignKey('rel_sub.id'))) 70 cls.rel_sub = Table('rel_sub', m, 71 Column('id', Integer, 72 ForeignKey('base_w_sub_rel.id'), 73 primary_key=True)) 74 cls.base = Table('base', m, 75 Column('id', Integer, primary_key=True), 76 Column('flag', Boolean)) 77 cls.sub = Table('sub', m, 78 Column('id', Integer, ForeignKey('base.id'), 79 primary_key=True)) 80 cls.sub_w_base_rel = Table('sub_w_base_rel', m, 81 Column('id', Integer, ForeignKey('base.id'), 82 primary_key=True), 83 Column('base_id', Integer, 84 ForeignKey('base.id'))) 85 cls.sub_w_sub_rel = Table('sub_w_sub_rel', m, 86 Column('id', Integer, ForeignKey('base.id'), 87 primary_key=True), 88 Column('sub_id', Integer, 89 ForeignKey('sub.id')) 90 ) 91 cls.right_w_base_rel = Table('right_w_base_rel', m, 92 Column('id', Integer, primary_key=True), 93 Column('base_id', Integer, 94 ForeignKey('base.id'))) 95 96 cls.three_tab_a = Table('three_tab_a', m, 97 Column('id', Integer, primary_key=True)) 98 cls.three_tab_b = Table('three_tab_b', m, 99 Column('id', Integer, primary_key=True), 100 Column('aid', Integer, ForeignKey( 101 'three_tab_a.id'))) 102 cls.three_tab_c = Table('three_tab_c', m, 103 Column('id', Integer, primary_key=True), 104 Column('aid', Integer, ForeignKey( 105 'three_tab_a.id')), 106 Column('bid', Integer, ForeignKey( 107 'three_tab_b.id'))) 108 109 cls.composite_target = Table('composite_target', m, 110 Column('uid', Integer, primary_key=True), 111 Column('oid', Integer, primary_key=True)) 112 113 cls.composite_multi_ref = Table( 114 'composite_multi_ref', m, 115 Column('uid1', Integer), 116 Column('uid2', Integer), 117 Column('oid', Integer), 118 ForeignKeyConstraint(("uid1", "oid"), 119 ("composite_target.uid", 120 "composite_target.oid")), 121 ForeignKeyConstraint(("uid2", "oid"), 122 ("composite_target.uid", 123 "composite_target.oid"))) 124 125 cls.purely_single_col = Table('purely_single_col', m, 126 Column('path', String)) 127 128 def _join_fixture_overlapping_three_tables(self, **kw): 129 def _can_sync(*cols): 130 for c in cols: 131 if self.three_tab_c.c.contains_column(c): 132 return False 133 else: 134 return True 135 return relationships.JoinCondition( 136 self.three_tab_a, 137 self.three_tab_b, 138 self.three_tab_a, 139 self.three_tab_b, 140 support_sync=False, 141 can_be_synced_fn=_can_sync, 142 primaryjoin=and_( 143 self.three_tab_a.c.id == self.three_tab_b.c.aid, 144 self.three_tab_c.c.bid == self.three_tab_b.c.id, 145 self.three_tab_c.c.aid == self.three_tab_a.c.id 146 ) 147 ) 148 149 def _join_fixture_m2m(self, **kw): 150 return relationships.JoinCondition( 151 self.m2mleft, 152 self.m2mright, 153 self.m2mleft, 154 self.m2mright, 155 secondary=self.m2msecondary, 156 **kw 157 ) 158 159 def _join_fixture_m2m_backref(self, **kw): 160 """return JoinCondition in the same way RelationshipProperty 161 calls it for a backref on an m2m. 162 163 """ 164 j1 = self._join_fixture_m2m() 165 return j1, relationships.JoinCondition( 166 self.m2mright, 167 self.m2mleft, 168 self.m2mright, 169 self.m2mleft, 170 secondary=self.m2msecondary, 171 primaryjoin=j1.secondaryjoin_minus_local, 172 secondaryjoin=j1.primaryjoin_minus_local 173 ) 174 175 def _join_fixture_o2m(self, **kw): 176 return relationships.JoinCondition( 177 self.left, 178 self.right, 179 self.left, 180 self.right, 181 **kw 182 ) 183 184 def _join_fixture_m2o(self, **kw): 185 return relationships.JoinCondition( 186 self.right, 187 self.left, 188 self.right, 189 self.left, 190 **kw 191 ) 192 193 def _join_fixture_o2m_selfref(self, **kw): 194 return relationships.JoinCondition( 195 self.selfref, 196 self.selfref, 197 self.selfref, 198 self.selfref, 199 **kw 200 ) 201 202 def _join_fixture_m2o_selfref(self, **kw): 203 return relationships.JoinCondition( 204 self.selfref, 205 self.selfref, 206 self.selfref, 207 self.selfref, 208 remote_side=set([self.selfref.c.id]), 209 **kw 210 ) 211 212 def _join_fixture_o2m_composite_selfref(self, **kw): 213 return relationships.JoinCondition( 214 self.composite_selfref, 215 self.composite_selfref, 216 self.composite_selfref, 217 self.composite_selfref, 218 **kw 219 ) 220 221 def _join_fixture_m2o_composite_selfref(self, **kw): 222 return relationships.JoinCondition( 223 self.composite_selfref, 224 self.composite_selfref, 225 self.composite_selfref, 226 self.composite_selfref, 227 remote_side=set([self.composite_selfref.c.id, 228 self.composite_selfref.c.group_id]), 229 **kw 230 ) 231 232 def _join_fixture_o2m_composite_selfref_func(self, **kw): 233 return relationships.JoinCondition( 234 self.composite_selfref, 235 self.composite_selfref, 236 self.composite_selfref, 237 self.composite_selfref, 238 primaryjoin=and_( 239 self.composite_selfref.c.group_id == 240 func.foo(self.composite_selfref.c.group_id), 241 self.composite_selfref.c.parent_id == 242 self.composite_selfref.c.id 243 ), 244 **kw 245 ) 246 247 def _join_fixture_o2m_composite_selfref_func_remote_side(self, **kw): 248 return relationships.JoinCondition( 249 self.composite_selfref, 250 self.composite_selfref, 251 self.composite_selfref, 252 self.composite_selfref, 253 primaryjoin=and_( 254 self.composite_selfref.c.group_id == 255 func.foo(self.composite_selfref.c.group_id), 256 self.composite_selfref.c.parent_id == 257 self.composite_selfref.c.id 258 ), 259 remote_side=set([self.composite_selfref.c.parent_id]), 260 **kw 261 ) 262 263 def _join_fixture_o2m_composite_selfref_func_annotated(self, **kw): 264 return relationships.JoinCondition( 265 self.composite_selfref, 266 self.composite_selfref, 267 self.composite_selfref, 268 self.composite_selfref, 269 primaryjoin=and_( 270 remote(self.composite_selfref.c.group_id) == 271 func.foo(self.composite_selfref.c.group_id), 272 remote(self.composite_selfref.c.parent_id) == 273 self.composite_selfref.c.id 274 ), 275 **kw 276 ) 277 278 def _join_fixture_compound_expression_1(self, **kw): 279 return relationships.JoinCondition( 280 self.left, 281 self.right, 282 self.left, 283 self.right, 284 primaryjoin=(self.left.c.x + self.left.c.y) == 285 relationships.remote(relationships.foreign( 286 self.right.c.x * self.right.c.y 287 )), 288 **kw 289 ) 290 291 def _join_fixture_compound_expression_2(self, **kw): 292 return relationships.JoinCondition( 293 self.left, 294 self.right, 295 self.left, 296 self.right, 297 primaryjoin=(self.left.c.x + self.left.c.y) == 298 relationships.foreign( 299 self.right.c.x * self.right.c.y 300 ), 301 **kw 302 ) 303 304 def _join_fixture_compound_expression_1_non_annotated(self, **kw): 305 return relationships.JoinCondition( 306 self.left, 307 self.right, 308 self.left, 309 self.right, 310 primaryjoin=(self.left.c.x + self.left.c.y) == 311 ( 312 self.right.c.x * self.right.c.y 313 ), 314 **kw 315 ) 316 317 def _join_fixture_base_to_joined_sub(self, **kw): 318 # see test/orm/inheritance/test_abc_inheritance:TestaTobM2O 319 # and others there 320 right = self.base_w_sub_rel.join( 321 self.rel_sub, 322 self.base_w_sub_rel.c.id == self.rel_sub.c.id 323 ) 324 return relationships.JoinCondition( 325 self.base_w_sub_rel, 326 right, 327 self.base_w_sub_rel, 328 self.rel_sub, 329 primaryjoin=self.base_w_sub_rel.c.sub_id == 330 self.rel_sub.c.id, 331 **kw 332 ) 333 334 def _join_fixture_o2m_joined_sub_to_base(self, **kw): 335 left = self.base.join(self.sub_w_base_rel, 336 self.base.c.id == self.sub_w_base_rel.c.id) 337 return relationships.JoinCondition( 338 left, 339 self.base, 340 self.sub_w_base_rel, 341 self.base, 342 primaryjoin=self.sub_w_base_rel.c.base_id == self.base.c.id 343 ) 344 345 def _join_fixture_m2o_joined_sub_to_sub_on_base(self, **kw): 346 # this is a late add - a variant of the test case 347 # in #2491 where we join on the base cols instead. only 348 # m2o has a problem at the time of this test. 349 left = self.base.join(self.sub, self.base.c.id == self.sub.c.id) 350 right = self.base.join(self.sub_w_base_rel, 351 self.base.c.id == self.sub_w_base_rel.c.id) 352 return relationships.JoinCondition( 353 left, 354 right, 355 self.sub, 356 self.sub_w_base_rel, 357 primaryjoin=self.sub_w_base_rel.c.base_id == self.base.c.id, 358 ) 359 360 def _join_fixture_o2m_joined_sub_to_sub(self, **kw): 361 left = self.base.join(self.sub, self.base.c.id == self.sub.c.id) 362 right = self.base.join(self.sub_w_sub_rel, 363 self.base.c.id == self.sub_w_sub_rel.c.id) 364 return relationships.JoinCondition( 365 left, 366 right, 367 self.sub, 368 self.sub_w_sub_rel, 369 primaryjoin=self.sub.c.id == self.sub_w_sub_rel.c.sub_id 370 ) 371 372 def _join_fixture_m2o_sub_to_joined_sub(self, **kw): 373 # see test.orm.test_mapper:MapperTest.test_add_column_prop_deannotate, 374 right = self.base.join(self.right_w_base_rel, 375 self.base.c.id == self.right_w_base_rel.c.id) 376 return relationships.JoinCondition( 377 self.right_w_base_rel, 378 right, 379 self.right_w_base_rel, 380 self.right_w_base_rel, 381 ) 382 383 def _join_fixture_m2o_sub_to_joined_sub_func(self, **kw): 384 # see test.orm.test_mapper:MapperTest.test_add_column_prop_deannotate, 385 right = self.base.join(self.right_w_base_rel, 386 self.base.c.id == self.right_w_base_rel.c.id) 387 return relationships.JoinCondition( 388 self.right_w_base_rel, 389 right, 390 self.right_w_base_rel, 391 self.right_w_base_rel, 392 primaryjoin=self.right_w_base_rel.c.base_id == 393 func.foo(self.base.c.id) 394 ) 395 396 def _join_fixture_o2o_joined_sub_to_base(self, **kw): 397 left = self.base.join(self.sub, 398 self.base.c.id == self.sub.c.id) 399 400 # see test_relationships->AmbiguousJoinInterpretedAsSelfRef 401 return relationships.JoinCondition( 402 left, 403 self.sub, 404 left, 405 self.sub, 406 ) 407 408 def _join_fixture_o2m_to_annotated_func(self, **kw): 409 return relationships.JoinCondition( 410 self.left, 411 self.right, 412 self.left, 413 self.right, 414 primaryjoin=self.left.c.id == 415 foreign(func.foo(self.right.c.lid)), 416 **kw 417 ) 418 419 def _join_fixture_o2m_to_oldstyle_func(self, **kw): 420 return relationships.JoinCondition( 421 self.left, 422 self.right, 423 self.left, 424 self.right, 425 primaryjoin=self.left.c.id == 426 func.foo(self.right.c.lid), 427 consider_as_foreign_keys=[self.right.c.lid], 428 **kw 429 ) 430 431 def _join_fixture_overlapping_composite_fks(self, **kw): 432 return relationships.JoinCondition( 433 self.composite_target, 434 self.composite_multi_ref, 435 self.composite_target, 436 self.composite_multi_ref, 437 consider_as_foreign_keys=[self.composite_multi_ref.c.uid2, 438 self.composite_multi_ref.c.oid], 439 **kw 440 ) 441 442 cls.left = Table('lft', m, 443 Column('id', Integer, primary_key=True), 444 Column('x', Integer), 445 Column('y', Integer)) 446 cls.right = Table('rgt', m, 447 Column('id', Integer, primary_key=True), 448 Column('lid', Integer, ForeignKey('lft.id')), 449 Column('x', Integer), 450 Column('y', Integer)) 451 452 def _join_fixture_o2m_o_side_none(self, **kw): 453 return relationships.JoinCondition( 454 self.left, 455 self.right, 456 self.left, 457 self.right, 458 primaryjoin=and_(self.left.c.id == self.right.c.lid, 459 self.left.c.x == 5), 460 **kw 461 ) 462 463 def _join_fixture_purely_single_o2m(self, **kw): 464 return relationships.JoinCondition( 465 self.purely_single_col, 466 self.purely_single_col, 467 self.purely_single_col, 468 self.purely_single_col, 469 support_sync=False, 470 primaryjoin=self.purely_single_col.c.path.like( 471 remote( 472 foreign( 473 self.purely_single_col.c.path.concat('%') 474 ) 475 ) 476 ) 477 ) 478 479 def _join_fixture_purely_single_m2o(self, **kw): 480 return relationships.JoinCondition( 481 self.purely_single_col, 482 self.purely_single_col, 483 self.purely_single_col, 484 self.purely_single_col, 485 support_sync=False, 486 primaryjoin=remote(self.purely_single_col.c.path).like( 487 foreign(self.purely_single_col.c.path.concat('%')) 488 ) 489 ) 490 491 def _join_fixture_remote_local_multiple_ref(self, **kw): 492 def fn(a, b): return ((a == b) | (b == a)) 493 return relationships.JoinCondition( 494 self.selfref, self.selfref, 495 self.selfref, self.selfref, 496 support_sync=False, 497 primaryjoin=fn( 498 # we're putting a do-nothing annotation on 499 # "a" so that the left/right is preserved; 500 # annotation vs. non seems to affect __eq__ behavior 501 self.selfref.c.sid._annotate({"foo": "bar"}), 502 foreign(remote(self.selfref.c.sid))) 503 ) 504 505 def _join_fixture_inh_selfref_w_entity(self, **kw): 506 fake_logger = mock.Mock(info=lambda *arg, **kw: None) 507 prop = mock.Mock( 508 parent=mock.Mock(), 509 mapper=mock.Mock(), 510 logger=fake_logger 511 ) 512 local_selectable = self.base.join(self.sub) 513 remote_selectable = self.base.join(self.sub_w_sub_rel) 514 515 sub_w_sub_rel__sub_id = self.sub_w_sub_rel.c.sub_id._annotate( 516 {'parentmapper': prop.mapper}) 517 sub__id = self.sub.c.id._annotate({'parentmapper': prop.parent}) 518 sub_w_sub_rel__flag = self.base.c.flag._annotate( 519 {"parentmapper": prop.mapper}) 520 return relationships.JoinCondition( 521 local_selectable, remote_selectable, 522 local_selectable, remote_selectable, 523 primaryjoin=and_( 524 sub_w_sub_rel__sub_id == sub__id, 525 sub_w_sub_rel__flag == True # noqa 526 ), 527 prop=prop 528 ) 529 530 def _assert_non_simple_warning(self, fn): 531 assert_raises_message( 532 exc.SAWarning, 533 "Non-simple column elements in " 534 "primary join condition for property " 535 r"None - consider using remote\(\) " 536 "annotations to mark the remote side.", 537 fn 538 ) 539 540 def _assert_raises_no_relevant_fks(self, fn, expr, relname, 541 primary, *arg, **kw): 542 assert_raises_message( 543 exc.ArgumentError, 544 r"Could not locate any relevant foreign key columns " 545 r"for %s join condition '%s' on relationship %s. " 546 r"Ensure that referencing columns are associated with " 547 r"a ForeignKey or ForeignKeyConstraint, or are annotated " 548 r"in the join condition with the foreign\(\) annotation." 549 % ( 550 primary, expr, relname 551 ), 552 fn, *arg, **kw 553 ) 554 555 def _assert_raises_no_equality(self, fn, expr, relname, 556 primary, *arg, **kw): 557 assert_raises_message( 558 exc.ArgumentError, 559 "Could not locate any simple equality expressions " 560 "involving locally mapped foreign key columns for %s join " 561 "condition '%s' on relationship %s. " 562 "Ensure that referencing columns are associated with a " 563 "ForeignKey or ForeignKeyConstraint, or are annotated in " 564 r"the join condition with the foreign\(\) annotation. " 565 "To allow comparison operators other than '==', " 566 "the relationship can be marked as viewonly=True." % ( 567 primary, expr, relname 568 ), 569 fn, *arg, **kw 570 ) 571 572 def _assert_raises_ambig_join(self, fn, relname, secondary_arg, 573 *arg, **kw): 574 if secondary_arg is not None: 575 assert_raises_message( 576 exc.AmbiguousForeignKeysError, 577 "Could not determine join condition between " 578 "parent/child tables on relationship %s - " 579 "there are multiple foreign key paths linking the " 580 "tables via secondary table '%s'. " 581 "Specify the 'foreign_keys' argument, providing a list " 582 "of those columns which should be counted as " 583 "containing a foreign key reference from the " 584 "secondary table to each of the parent and child tables." 585 % (relname, secondary_arg), 586 fn, *arg, **kw) 587 else: 588 assert_raises_message( 589 exc.AmbiguousForeignKeysError, 590 "Could not determine join condition between " 591 "parent/child tables on relationship %s - " 592 "there are no foreign keys linking these tables. " 593 % (relname,), 594 fn, *arg, **kw) 595 596 def _assert_raises_no_join(self, fn, relname, secondary_arg, 597 *arg, **kw): 598 if secondary_arg is not None: 599 assert_raises_message( 600 exc.NoForeignKeysError, 601 "Could not determine join condition between " 602 "parent/child tables on relationship %s - " 603 "there are no foreign keys linking these tables " 604 "via secondary table '%s'. " 605 "Ensure that referencing columns are associated " 606 "with a ForeignKey " 607 "or ForeignKeyConstraint, or specify 'primaryjoin' and " 608 "'secondaryjoin' expressions" 609 % (relname, secondary_arg), 610 fn, *arg, **kw) 611 else: 612 assert_raises_message( 613 exc.NoForeignKeysError, 614 "Could not determine join condition between " 615 "parent/child tables on relationship %s - " 616 "there are no foreign keys linking these tables. " 617 "Ensure that referencing columns are associated " 618 "with a ForeignKey " 619 "or ForeignKeyConstraint, or specify a 'primaryjoin' " 620 "expression." 621 % (relname,), 622 fn, *arg, **kw) 623 624 625class ColumnCollectionsTest(_JoinFixtures, fixtures.TestBase, 626 AssertsCompiledSQL): 627 def test_determine_local_remote_pairs_o2o_joined_sub_to_base(self): 628 joincond = self._join_fixture_o2o_joined_sub_to_base() 629 eq_( 630 joincond.local_remote_pairs, 631 [(self.base.c.id, self.sub.c.id)] 632 ) 633 634 def test_determine_synchronize_pairs_o2m_to_annotated_func(self): 635 joincond = self._join_fixture_o2m_to_annotated_func() 636 eq_( 637 joincond.synchronize_pairs, 638 [(self.left.c.id, self.right.c.lid)] 639 ) 640 641 def test_determine_synchronize_pairs_o2m_to_oldstyle_func(self): 642 joincond = self._join_fixture_o2m_to_oldstyle_func() 643 eq_( 644 joincond.synchronize_pairs, 645 [(self.left.c.id, self.right.c.lid)] 646 ) 647 648 def test_determinelocal_remote_m2o_joined_sub_to_sub_on_base(self): 649 joincond = self._join_fixture_m2o_joined_sub_to_sub_on_base() 650 eq_( 651 joincond.local_remote_pairs, 652 [(self.base.c.id, self.sub_w_base_rel.c.base_id)] 653 ) 654 655 def test_determine_local_remote_base_to_joined_sub(self): 656 joincond = self._join_fixture_base_to_joined_sub() 657 eq_( 658 joincond.local_remote_pairs, 659 [ 660 (self.base_w_sub_rel.c.sub_id, self.rel_sub.c.id) 661 ] 662 ) 663 664 def test_determine_local_remote_o2m_joined_sub_to_base(self): 665 joincond = self._join_fixture_o2m_joined_sub_to_base() 666 eq_( 667 joincond.local_remote_pairs, 668 [ 669 (self.sub_w_base_rel.c.base_id, self.base.c.id) 670 ] 671 ) 672 673 def test_determine_local_remote_m2o_sub_to_joined_sub(self): 674 joincond = self._join_fixture_m2o_sub_to_joined_sub() 675 eq_( 676 joincond.local_remote_pairs, 677 [ 678 (self.right_w_base_rel.c.base_id, self.base.c.id) 679 ] 680 ) 681 682 def test_determine_remote_columns_o2m_joined_sub_to_sub(self): 683 joincond = self._join_fixture_o2m_joined_sub_to_sub() 684 eq_( 685 joincond.local_remote_pairs, 686 [ 687 (self.sub.c.id, self.sub_w_sub_rel.c.sub_id) 688 ] 689 ) 690 691 def test_determine_remote_columns_compound_1(self): 692 joincond = self._join_fixture_compound_expression_1( 693 support_sync=False) 694 eq_( 695 joincond.remote_columns, 696 set([self.right.c.x, self.right.c.y]) 697 ) 698 699 def test_determine_local_remote_compound_1(self): 700 joincond = self._join_fixture_compound_expression_1( 701 support_sync=False) 702 eq_( 703 joincond.local_remote_pairs, 704 [ 705 (self.left.c.x, self.right.c.x), 706 (self.left.c.x, self.right.c.y), 707 (self.left.c.y, self.right.c.x), 708 (self.left.c.y, self.right.c.y) 709 ] 710 ) 711 712 def test_determine_local_remote_compound_2(self): 713 joincond = self._join_fixture_compound_expression_2( 714 support_sync=False) 715 eq_( 716 joincond.local_remote_pairs, 717 [ 718 (self.left.c.x, self.right.c.x), 719 (self.left.c.x, self.right.c.y), 720 (self.left.c.y, self.right.c.x), 721 (self.left.c.y, self.right.c.y) 722 ] 723 ) 724 725 def test_determine_local_remote_compound_3(self): 726 joincond = self._join_fixture_compound_expression_1() 727 eq_( 728 joincond.local_remote_pairs, 729 [ 730 (self.left.c.x, self.right.c.x), 731 (self.left.c.x, self.right.c.y), 732 (self.left.c.y, self.right.c.x), 733 (self.left.c.y, self.right.c.y), 734 ] 735 ) 736 737 def test_err_local_remote_compound_1(self): 738 self._assert_raises_no_relevant_fks( 739 self._join_fixture_compound_expression_1_non_annotated, 740 r'lft.x \+ lft.y = rgt.x \* rgt.y', 741 "None", "primary" 742 ) 743 744 def test_determine_remote_columns_compound_2(self): 745 joincond = self._join_fixture_compound_expression_2( 746 support_sync=False) 747 eq_( 748 joincond.remote_columns, 749 set([self.right.c.x, self.right.c.y]) 750 ) 751 752 def test_determine_remote_columns_o2m(self): 753 joincond = self._join_fixture_o2m() 754 eq_( 755 joincond.remote_columns, 756 set([self.right.c.lid]) 757 ) 758 759 def test_determine_remote_columns_o2m_selfref(self): 760 joincond = self._join_fixture_o2m_selfref() 761 eq_( 762 joincond.remote_columns, 763 set([self.selfref.c.sid]) 764 ) 765 766 def test_determine_local_remote_pairs_o2m_composite_selfref(self): 767 joincond = self._join_fixture_o2m_composite_selfref() 768 eq_( 769 joincond.local_remote_pairs, 770 [ 771 (self.composite_selfref.c.group_id, 772 self.composite_selfref.c.group_id), 773 (self.composite_selfref.c.id, 774 self.composite_selfref.c.parent_id), 775 ] 776 ) 777 778 def test_determine_local_remote_pairs_o2m_composite_selfref_func_warning( 779 self): 780 self._assert_non_simple_warning( 781 self._join_fixture_o2m_composite_selfref_func 782 ) 783 784 def test_determine_local_remote_pairs_o2m_composite_selfref_func_rs(self): 785 # no warning 786 self._join_fixture_o2m_composite_selfref_func_remote_side() 787 788 def test_determine_local_remote_pairs_o2m_overlap_func_warning(self): 789 self._assert_non_simple_warning( 790 self._join_fixture_m2o_sub_to_joined_sub_func 791 ) 792 793 def test_determine_local_remote_pairs_o2m_composite_selfref_func_annotated( 794 self): 795 joincond = self._join_fixture_o2m_composite_selfref_func_annotated() 796 eq_( 797 joincond.local_remote_pairs, 798 [ 799 (self.composite_selfref.c.group_id, 800 self.composite_selfref.c.group_id), 801 (self.composite_selfref.c.id, 802 self.composite_selfref.c.parent_id), 803 ] 804 ) 805 806 def test_determine_remote_columns_m2o_composite_selfref(self): 807 joincond = self._join_fixture_m2o_composite_selfref() 808 eq_( 809 joincond.remote_columns, 810 set([self.composite_selfref.c.id, 811 self.composite_selfref.c.group_id]) 812 ) 813 814 def test_determine_remote_columns_m2o(self): 815 joincond = self._join_fixture_m2o() 816 eq_( 817 joincond.remote_columns, 818 set([self.left.c.id]) 819 ) 820 821 def test_determine_local_remote_pairs_o2m(self): 822 joincond = self._join_fixture_o2m() 823 eq_( 824 joincond.local_remote_pairs, 825 [(self.left.c.id, self.right.c.lid)] 826 ) 827 828 def test_determine_synchronize_pairs_m2m(self): 829 joincond = self._join_fixture_m2m() 830 eq_( 831 joincond.synchronize_pairs, 832 [(self.m2mleft.c.id, self.m2msecondary.c.lid)] 833 ) 834 eq_( 835 joincond.secondary_synchronize_pairs, 836 [(self.m2mright.c.id, self.m2msecondary.c.rid)] 837 ) 838 839 def test_determine_local_remote_pairs_o2m_backref(self): 840 joincond = self._join_fixture_o2m() 841 joincond2 = self._join_fixture_m2o( 842 primaryjoin=joincond.primaryjoin_reverse_remote, 843 ) 844 eq_( 845 joincond2.local_remote_pairs, 846 [(self.right.c.lid, self.left.c.id)] 847 ) 848 849 def test_determine_local_remote_pairs_m2m(self): 850 joincond = self._join_fixture_m2m() 851 eq_( 852 joincond.local_remote_pairs, 853 [(self.m2mleft.c.id, self.m2msecondary.c.lid), 854 (self.m2mright.c.id, self.m2msecondary.c.rid)] 855 ) 856 857 def test_determine_local_remote_pairs_m2m_backref(self): 858 j1, j2 = self._join_fixture_m2m_backref() 859 eq_( 860 j1.local_remote_pairs, 861 [(self.m2mleft.c.id, self.m2msecondary.c.lid), 862 (self.m2mright.c.id, self.m2msecondary.c.rid)] 863 ) 864 eq_( 865 j2.local_remote_pairs, 866 [ 867 (self.m2mright.c.id, self.m2msecondary.c.rid), 868 (self.m2mleft.c.id, self.m2msecondary.c.lid), 869 ] 870 ) 871 872 def test_determine_local_columns_m2m_backref(self): 873 j1, j2 = self._join_fixture_m2m_backref() 874 eq_( 875 j1.local_columns, 876 set([self.m2mleft.c.id]) 877 ) 878 eq_( 879 j2.local_columns, 880 set([self.m2mright.c.id]) 881 ) 882 883 def test_determine_remote_columns_m2m_backref(self): 884 j1, j2 = self._join_fixture_m2m_backref() 885 eq_( 886 j1.remote_columns, 887 set([self.m2msecondary.c.lid, self.m2msecondary.c.rid]) 888 ) 889 eq_( 890 j2.remote_columns, 891 set([self.m2msecondary.c.lid, self.m2msecondary.c.rid]) 892 ) 893 894 def test_determine_remote_columns_m2o_selfref(self): 895 joincond = self._join_fixture_m2o_selfref() 896 eq_( 897 joincond.remote_columns, 898 set([self.selfref.c.id]) 899 ) 900 901 def test_determine_local_remote_cols_three_tab_viewonly(self): 902 joincond = self._join_fixture_overlapping_three_tables() 903 eq_( 904 joincond.local_remote_pairs, 905 [(self.three_tab_a.c.id, self.three_tab_b.c.aid)] 906 ) 907 eq_( 908 joincond.remote_columns, 909 set([self.three_tab_b.c.id, self.three_tab_b.c.aid]) 910 ) 911 912 def test_determine_local_remote_overlapping_composite_fks(self): 913 joincond = self._join_fixture_overlapping_composite_fks() 914 915 eq_( 916 joincond.local_remote_pairs, 917 [ 918 (self.composite_target.c.uid, 919 self.composite_multi_ref.c.uid2,), 920 (self.composite_target.c.oid, self.composite_multi_ref.c.oid,) 921 ] 922 ) 923 924 def test_determine_local_remote_pairs_purely_single_col_o2m(self): 925 joincond = self._join_fixture_purely_single_o2m() 926 eq_( 927 joincond.local_remote_pairs, 928 [(self.purely_single_col.c.path, self.purely_single_col.c.path)] 929 ) 930 931 def test_determine_local_remote_pairs_inh_selfref_w_entities(self): 932 joincond = self._join_fixture_inh_selfref_w_entity() 933 eq_( 934 joincond.local_remote_pairs, 935 [(self.sub.c.id, self.sub_w_sub_rel.c.sub_id)] 936 ) 937 eq_( 938 joincond.remote_columns, 939 set([self.base.c.flag, self.sub_w_sub_rel.c.sub_id]) 940 ) 941 942 943class DirectionTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): 944 def test_determine_direction_compound_2(self): 945 joincond = self._join_fixture_compound_expression_2( 946 support_sync=False) 947 is_( 948 joincond.direction, 949 ONETOMANY 950 ) 951 952 def test_determine_direction_o2m(self): 953 joincond = self._join_fixture_o2m() 954 is_(joincond.direction, ONETOMANY) 955 956 def test_determine_direction_o2m_selfref(self): 957 joincond = self._join_fixture_o2m_selfref() 958 is_(joincond.direction, ONETOMANY) 959 960 def test_determine_direction_m2o_selfref(self): 961 joincond = self._join_fixture_m2o_selfref() 962 is_(joincond.direction, MANYTOONE) 963 964 def test_determine_direction_o2m_composite_selfref(self): 965 joincond = self._join_fixture_o2m_composite_selfref() 966 is_(joincond.direction, ONETOMANY) 967 968 def test_determine_direction_m2o_composite_selfref(self): 969 joincond = self._join_fixture_m2o_composite_selfref() 970 is_(joincond.direction, MANYTOONE) 971 972 def test_determine_direction_m2o(self): 973 joincond = self._join_fixture_m2o() 974 is_(joincond.direction, MANYTOONE) 975 976 def test_determine_direction_purely_single_o2m(self): 977 joincond = self._join_fixture_purely_single_o2m() 978 is_(joincond.direction, ONETOMANY) 979 980 def test_determine_direction_purely_single_m2o(self): 981 joincond = self._join_fixture_purely_single_m2o() 982 is_(joincond.direction, MANYTOONE) 983 984 985class DetermineJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): 986 __dialect__ = 'default' 987 988 def test_determine_join_o2m(self): 989 joincond = self._join_fixture_o2m() 990 self.assert_compile( 991 joincond.primaryjoin, 992 "lft.id = rgt.lid" 993 ) 994 995 def test_determine_join_o2m_selfref(self): 996 joincond = self._join_fixture_o2m_selfref() 997 self.assert_compile( 998 joincond.primaryjoin, 999 "selfref.id = selfref.sid" 1000 ) 1001 1002 def test_determine_join_m2o_selfref(self): 1003 joincond = self._join_fixture_m2o_selfref() 1004 self.assert_compile( 1005 joincond.primaryjoin, 1006 "selfref.id = selfref.sid" 1007 ) 1008 1009 def test_determine_join_o2m_composite_selfref(self): 1010 joincond = self._join_fixture_o2m_composite_selfref() 1011 self.assert_compile( 1012 joincond.primaryjoin, 1013 "composite_selfref.group_id = composite_selfref.group_id " 1014 "AND composite_selfref.id = composite_selfref.parent_id" 1015 ) 1016 1017 def test_determine_join_m2o_composite_selfref(self): 1018 joincond = self._join_fixture_m2o_composite_selfref() 1019 self.assert_compile( 1020 joincond.primaryjoin, 1021 "composite_selfref.group_id = composite_selfref.group_id " 1022 "AND composite_selfref.id = composite_selfref.parent_id" 1023 ) 1024 1025 def test_determine_join_m2o(self): 1026 joincond = self._join_fixture_m2o() 1027 self.assert_compile( 1028 joincond.primaryjoin, 1029 "lft.id = rgt.lid" 1030 ) 1031 1032 def test_determine_join_ambiguous_fks_o2m(self): 1033 assert_raises_message( 1034 exc.AmbiguousForeignKeysError, 1035 "Could not determine join condition between " 1036 "parent/child tables on relationship None - " 1037 "there are multiple foreign key paths linking " 1038 "the tables. Specify the 'foreign_keys' argument, " 1039 "providing a list of those columns which " 1040 "should be counted as containing a foreign " 1041 "key reference to the parent table.", 1042 relationships.JoinCondition, 1043 self.left, 1044 self.right_multi_fk, 1045 self.left, 1046 self.right_multi_fk, 1047 ) 1048 1049 def test_determine_join_no_fks_o2m(self): 1050 self._assert_raises_no_join( 1051 relationships.JoinCondition, 1052 "None", None, 1053 self.left, 1054 self.selfref, 1055 self.left, 1056 self.selfref, 1057 ) 1058 1059 def test_determine_join_ambiguous_fks_m2m(self): 1060 1061 self._assert_raises_ambig_join( 1062 relationships.JoinCondition, 1063 "None", self.m2msecondary_ambig_fks, 1064 self.m2mleft, 1065 self.m2mright, 1066 self.m2mleft, 1067 self.m2mright, 1068 secondary=self.m2msecondary_ambig_fks 1069 ) 1070 1071 def test_determine_join_no_fks_m2m(self): 1072 self._assert_raises_no_join( 1073 relationships.JoinCondition, 1074 "None", self.m2msecondary_no_fks, 1075 self.m2mleft, 1076 self.m2mright, 1077 self.m2mleft, 1078 self.m2mright, 1079 secondary=self.m2msecondary_no_fks 1080 ) 1081 1082 def _join_fixture_fks_ambig_m2m(self): 1083 return relationships.JoinCondition( 1084 self.m2mleft, 1085 self.m2mright, 1086 self.m2mleft, 1087 self.m2mright, 1088 secondary=self.m2msecondary_ambig_fks, 1089 consider_as_foreign_keys=[ 1090 self.m2msecondary_ambig_fks.c.lid1, 1091 self.m2msecondary_ambig_fks.c.rid1] 1092 ) 1093 1094 def test_determine_join_w_fks_ambig_m2m(self): 1095 joincond = self._join_fixture_fks_ambig_m2m() 1096 self.assert_compile( 1097 joincond.primaryjoin, 1098 "m2mlft.id = m2msecondary_ambig_fks.lid1" 1099 ) 1100 self.assert_compile( 1101 joincond.secondaryjoin, 1102 "m2mrgt.id = m2msecondary_ambig_fks.rid1" 1103 ) 1104 1105 1106class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): 1107 __dialect__ = 'default' 1108 1109 def test_join_targets_o2m_selfref(self): 1110 joincond = self._join_fixture_o2m_selfref() 1111 left = select([joincond.parent_selectable]).alias('pj') 1112 pj, sj, sec, adapter, ds = joincond.join_targets( 1113 left, 1114 joincond.child_selectable, 1115 True) 1116 self.assert_compile( 1117 pj, "pj.id = selfref.sid" 1118 ) 1119 1120 right = select([joincond.child_selectable]).alias('pj') 1121 pj, sj, sec, adapter, ds = joincond.join_targets( 1122 joincond.parent_selectable, 1123 right, 1124 True) 1125 self.assert_compile( 1126 pj, "selfref.id = pj.sid" 1127 ) 1128 1129 def test_join_targets_o2m_plain(self): 1130 joincond = self._join_fixture_o2m() 1131 pj, sj, sec, adapter, ds = joincond.join_targets( 1132 joincond.parent_selectable, 1133 joincond.child_selectable, 1134 False) 1135 self.assert_compile( 1136 pj, "lft.id = rgt.lid" 1137 ) 1138 1139 def test_join_targets_o2m_left_aliased(self): 1140 joincond = self._join_fixture_o2m() 1141 left = select([joincond.parent_selectable]).alias('pj') 1142 pj, sj, sec, adapter, ds = joincond.join_targets( 1143 left, 1144 joincond.child_selectable, 1145 True) 1146 self.assert_compile( 1147 pj, "pj.id = rgt.lid" 1148 ) 1149 1150 def test_join_targets_o2m_right_aliased(self): 1151 joincond = self._join_fixture_o2m() 1152 right = select([joincond.child_selectable]).alias('pj') 1153 pj, sj, sec, adapter, ds = joincond.join_targets( 1154 joincond.parent_selectable, 1155 right, 1156 True) 1157 self.assert_compile( 1158 pj, "lft.id = pj.lid" 1159 ) 1160 1161 def test_join_targets_o2m_composite_selfref(self): 1162 joincond = self._join_fixture_o2m_composite_selfref() 1163 right = select([joincond.child_selectable]).alias('pj') 1164 pj, sj, sec, adapter, ds = joincond.join_targets( 1165 joincond.parent_selectable, 1166 right, 1167 True) 1168 self.assert_compile( 1169 pj, 1170 "pj.group_id = composite_selfref.group_id " 1171 "AND composite_selfref.id = pj.parent_id" 1172 ) 1173 1174 def test_join_targets_m2o_composite_selfref(self): 1175 joincond = self._join_fixture_m2o_composite_selfref() 1176 right = select([joincond.child_selectable]).alias('pj') 1177 pj, sj, sec, adapter, ds = joincond.join_targets( 1178 joincond.parent_selectable, 1179 right, 1180 True) 1181 self.assert_compile( 1182 pj, 1183 "pj.group_id = composite_selfref.group_id " 1184 "AND pj.id = composite_selfref.parent_id" 1185 ) 1186 1187 1188class LazyClauseTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL): 1189 __dialect__ = 'default' 1190 1191 def test_lazy_clause_o2m(self): 1192 joincond = self._join_fixture_o2m() 1193 lazywhere, bind_to_col, equated_columns = joincond.create_lazy_clause() 1194 self.assert_compile( 1195 lazywhere, 1196 ":param_1 = rgt.lid" 1197 ) 1198 1199 def test_lazy_clause_o2m_reverse(self): 1200 joincond = self._join_fixture_o2m() 1201 lazywhere, bind_to_col, equated_columns =\ 1202 joincond.create_lazy_clause(reverse_direction=True) 1203 self.assert_compile( 1204 lazywhere, 1205 "lft.id = :param_1" 1206 ) 1207 1208 def test_lazy_clause_o2m_o_side_none(self): 1209 # test for #2948. When the join is "o.id == m.oid 1210 # AND o.something == something", 1211 # we don't want 'o' brought into the lazy load for 'm' 1212 joincond = self._join_fixture_o2m_o_side_none() 1213 lazywhere, bind_to_col, equated_columns = joincond.create_lazy_clause() 1214 self.assert_compile( 1215 lazywhere, 1216 ":param_1 = rgt.lid AND :param_2 = :x_1", 1217 checkparams={'param_1': None, 'param_2': None, 'x_1': 5} 1218 ) 1219 1220 def test_lazy_clause_o2m_o_side_none_reverse(self): 1221 # continued test for #2948. 1222 joincond = self._join_fixture_o2m_o_side_none() 1223 lazywhere, bind_to_col, equated_columns = joincond.create_lazy_clause( 1224 reverse_direction=True) 1225 self.assert_compile( 1226 lazywhere, 1227 "lft.id = :param_1 AND lft.x = :x_1", 1228 checkparams={'param_1': None, 'x_1': 5} 1229 ) 1230 1231 def test_lazy_clause_remote_local_multiple_ref(self): 1232 joincond = self._join_fixture_remote_local_multiple_ref() 1233 lazywhere, bind_to_col, equated_columns = joincond.create_lazy_clause() 1234 1235 self.assert_compile( 1236 lazywhere, 1237 ":param_1 = selfref.sid OR selfref.sid = :param_1", 1238 checkparams={'param_1': None} 1239 ) 1240