AW: [odb-users] Problem with object loading view

Marcel Nehring mne at qosmotec.com
Tue Feb 16 05:50:43 EST 2016


Hi Boris,

thanks for your answer.

I am not sure if you misunderstood me or I misunderstood you, but I don't think that there is a recursive use of the view. At least loading the view does not trigger another load of the same view while the first is not yet finished.

However, I dug a little bit deeper into this and figured out that the problem only occurs if a polymorphic class is involved.

Since I now understand the problem better, I was finally able to construct a small example to reproduce the problem.
What happens is the following:

* I manually load all instances of class Derived from the database. Derived has a polymorphic base class.
* Loading an instance of Derived triggers loading of a class Foo due to object relationships.
* Loading of a Foo triggers loading of a Bar, let's call it A.
* Bars are collected in a BarCollection and they know of the collection they are a member of, so loading a Bar triggers loading of a BarCollection.
* Loading of a BarCollection triggers loading of all contained Bars, including a Bar B.
* When Bar B is fully loaded the post load callback fires.
* Inside this callback I manually try to load a Derived, since loading of the Derived object from the first step is not yet finished it is not present in ODB's session cache. (If it were, it would work fine)
* ODB tries to load the polymorphic object from the database, causing the assertion to fire. I guess the cached statement is still locked because we are still in the middle of loading the Derived instance from the first step.

The code that reproduces this problem together with the data for the database that is needed to reproduce it is attached below.

As I said, I am not sure if this is the problem that you already mentioned in you last message.

Regards,
Marcel

// crash.h
#pragma once

/*
INSERT INTO BASE (ID, TYPEID)
VALUES (1, 'Derived');

INSERT INTO BASE (ID, TYPEID)
VALUES (2, 'Derived');

INSERT INTO DERIVED (ID)
VALUES (1);

INSERT INTO DERIVED (ID)
VALUES (2);

INSERT INTO BARCOLLECTION (ID)
VALUES (1);

INSERT INTO BAR (ID, COLLECTION)
VALUES (1, 1);

INSERT INTO BAR (ID, COLLECTION)
VALUES (2, 1);

INSERT INTO FOO (ID, BAR, DERIVED)
VALUES (1, 1, 1);

INSERT INTO FOO (ID, BAR, DERIVED)
VALUES (2, 2, 2);
*/

#include <memory>
#include <vector>

#include <odb/core.hxx>
#include <odb/callback.hxx>

class Foo;
class Bar;

#pragma db object polymorphic pointer(std::shared_ptr) session
class Base
{
public:
    virtual void foo() = 0;
#pragma db id auto
    int m_id;
};

#pragma db object pointer(std::shared_ptr) session
class Derived : public Base
{
public:
    void foo() { /* no-op */ };
#pragma db inverse(m_derived)
    std::shared_ptr<Foo> m_foo;
};

#pragma db object pointer(std::shared_ptr) session
class Foo
{
public:
#pragma db id auto
    int m_id;
    std::shared_ptr<Bar> m_bar;
    std::shared_ptr<Derived> m_derived;
};

#pragma db object pointer(std::shared_ptr) session
class BarCollection
{
public:

#pragma db id auto
    int m_id;
#pragma db inverse(m_collection)
    std::vector<std::weak_ptr<Bar>> m_bars;
};

#pragma db view object(Bar) object(Foo inner) object(Derived inner) session
struct MyView
{
    std::shared_ptr<Derived> derived;
};

#pragma db object callback(loadView) pointer(std::shared_ptr) session
class Bar
{
public:
    void loadView(odb::callback_event event, odb::database& db);

#pragma db id auto
    int m_id;
    std::shared_ptr<BarCollection> m_collection;
};


//crash.cpp
#include "crash.h"
#include "crash-odb.h"

#include "odb/oracle/database.hxx"
#include "odb/session.hxx"

#include <iostream>

void Bar::loadView(odb::callback_event event, odb::database& db)
{
    if (event == odb::callback_event::post_load)
    {
        auto resultSet(db.query<MyView>(odb::query<MyView>::Derived::id == m_id));
        for (auto iter(resultSet.begin()); iter != resultSet.end(); ++iter)
        {
            auto relatedDerived = iter->derived;
        }
    }
}

int main(int argc, char ** argv)
{
    std::shared_ptr<odb::database> db(new odb::oracle::database("odb",
                                                                "secret",
                                                                "XE",
                                                                "localhost",
                                                                1521));

    odb::session s;

    odb::transaction t(db->begin());

    odb::result<Derived> results(db->query<Derived>());
    for (auto iter(results.begin()); iter != results.end(); ++iter)
    {
        auto derived = iter.load();
        std::cout << "Derived with ID " << derived->m_id << " loaded" << std::endl;
    }

    t.commit();
}



More information about the odb-users mailing list