#900 ✓resolved
MarkMT

Stack exception accessing a many-to-one association

Reported by MarkMT | June 12th, 2009 @ 06:38 PM | in 0.10.0

A problem occurs when four conditions coincide -

(i) Given three models A, B and C, many-to-one relationships exist between A and C and between B and C
(ii) The 'belongs_to :b' statement in the definition of the child model C specifies the :classname for the parent model B.
(iii) The 'belongs_to :a' statement in C precedes the one for B. (iv) The parent model A is loaded after the child model C.

In this case, given an object, c, of class C, accessing the association c.a causes an infinite loop leading to a stack exception.

Detailed explanation here -

http://groups.google.com/group/datamapper/browse_thread/thread/503e...

Comments and changes to this ticket

  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) June 16th, 2009 @ 03:37 AM

    • Assigned user set to “Dan Kubb (dkubb)”
    • State changed from “new” to “accepted”
    • Tag changed from association, many-to-one to association, many-to-one
    • Milestone set to 0.10.0
  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) June 23rd, 2009 @ 01:37 AM

    • State changed from “accepted” to “hold”

    @Mark: Can you try to see if this problem persists with 0.10 RC1? The relationship code was completely rewritten, so there's a good chance this is no longer an issue at all.

    If it is still a problem, can you attach a stand-alone script to this ticket that allows me to reproduce the problem at my end?

  • Martin Gamsjaeger (snusnu)

    Martin Gamsjaeger (snusnu) July 9th, 2009 @ 02:09 PM

    • State changed from “hold” to “resolved”

    Mark,

    I extracted a little standalone script (with a few modifications to make it 0.10 compliant) from the mailing list thread, ran it against current next branches as of today, and I don't get any errors. I marked this as resolved for 0.10, but if my script doesn't quite cover your usecase and/or you can still reproduce the error with next branches, feel free to comment and we can reopen it.

    require "rubygems"
    require "dm-core"
    require "spec"
    
    DataMapper::Logger.new(STDOUT, :debug)
    DataMapper.setup(:default, "sqlite3::memory:")
    
    class User
      include DataMapper::Resource
      property :id, Serial
      has n, :recaps
    end
    
    class Recap
      include DataMapper::Resource
      property :id, Serial
      belongs_to :author, User
      belongs_to :regatta
    end
    
    class Regatta
      include DataMapper::Resource
      property :id, Serial
      has 1, :recap
    end
    
    describe "This setup" do
    
      before(:all) do
        DataMapper.auto_migrate!
      end
    
      it "should not recurse infinitely" do
    
        user    = User.create
        regatta = Regatta.create
        recap   = Recap.create :regatta => regatta, :author => user
    
        r = Recap.get(1)
        r.regatta.should == regatta
    
      end
    end
    
    # mungo:snippets snusnu$ spec -cfs markmt.rb 
    # 
    # This setup
    #  ~ (0.000166) SELECT sqlite_version(*)
    #  ~ (0.000108) DROP TABLE IF EXISTS "users"
    #  ~ (0.000018) DROP TABLE IF EXISTS "recaps"
    #  ~ (0.000026) DROP TABLE IF EXISTS "regattas"
    #  ~ (0.000023) PRAGMA table_info("users")
    #  ~ (0.000361) CREATE TABLE "users" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT)
    #  ~ (0.000017) PRAGMA table_info("recaps")
    #  ~ (0.000121) CREATE TABLE "recaps" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "author_id" INTEGER NOT NULL, "regatta_id" INTEGER NOT NULL)
    #  ~ (0.000131) CREATE INDEX "index_recaps_regatta" ON "recaps" ("regatta_id")
    #  ~ (0.000101) CREATE INDEX "index_recaps_author" ON "recaps" ("author_id")
    #  ~ (0.000009) PRAGMA table_info("regattas")
    #  ~ (0.000111) CREATE TABLE "regattas" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT)
    #  ~ (0.000045) INSERT INTO "users" DEFAULT VALUES
    #  ~ (0.000033) INSERT INTO "regattas" DEFAULT VALUES
    #  ~ (0.000054) INSERT INTO "recaps" ("author_id", "regatta_id") VALUES (1, 1)
    #  ~ (0.000028) SELECT "id", "author_id", "regatta_id" FROM "recaps" WHERE "id" = 1
    #  ~ (0.000038) SELECT "id" FROM "regattas" WHERE "id" = 1 ORDER BY "id" LIMIT 1
    # - should not recurse infinitely
    # 
    # Finished in 0.012402 seconds
    # 
    # 1 example, 0 failures
    
  • MarkMT

    MarkMT July 13th, 2009 @ 11:19 PM

    Martin,

    Thanks for looking into this... I haven't had a chance to try this with the next branch, but just looking at your script I think you've missed one condition that I noted in my google groups posting - in class Recap, the declaration 'belongs_to :regatta' has to precede 'belongs_to :author, User'. Yes it seems weird that that would matter, but this was one of the four conditions necessary for the failure I observed. Can you try that? Everything else in your script looks fine.

    Mark.

  • Martin Gamsjaeger (snusnu)

    Martin Gamsjaeger (snusnu) July 14th, 2009 @ 07:21 AM

    Mark,

    I just tried the snippet with the change you mentioned and it still behaves correctly! I think we can leave this resolved.

  • MarkMT

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Pages