Project

General

Profile

"Wt: fatal error: Session::query(): too many aliases for result"

Added by Vincenzo Romano over 5 years ago

This is my application factory (the callback used with Wt::WServer::addEntryPoint() ):

Wt::WApplication *applicationInit( const Wt::WEnvironment& env ) {
  Wt::WApplication *app = new Wt::WApplication( env );
  const char* dbconn = getenv( "DBCONNINFO" );
  if( dbconn == 0 || dbconn[0] == 0 )
    dbconn = "dbname=tmp1";
  dbBackEnd.connect( std::string( dbconn ) );
  dbSession.setConnection( dbBackEnd );
  dbSession.mapClass<ConfigEntry>( "session_configuration()" ); // PG table function!
  dbSession.query<void>( "select * from session_init()" ); // procedure call, no result
  auto conf = dbSession.query< Wt::Dbo::ptr<ConfigEntry> >( "select k,v from session_configuration()" );
  if( conf.resultList().size() != 0 ) { // EXCEPTION HERE!
    configuration.clear();
    for( auto i = conf.resultList().begin(); i != conf.resultList().end(); i++  ) {
      configuration.add( (*i)->k,(*i)->v );
    }
  }
  return app;
}

It successfully compiles with no warning at all (I compile with @ -Wall).
As soon as I run
conf.resultList().begin()@ an exception gets triggered and the above message appears in the logs:

Wt: fatal error: Session::query(): too many aliases for result

What does it mean?


Replies (14)

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

The results doesn't (obviously) change by replacing the auto type with the actual type.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Roel Standaert over 5 years ago

That may not be the most informative message, indeed.

I think the issue is that you are selecting k,v, but the result is a Dbo::ptr<ConfigEntry>, which to Dbo is only one thing.

I'm still not sure if that's actually going to work, though. If you're having trouble getting it to work I would suggest creating a view.

Regards,

Roel

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

I have also this fragment in the very same source file:

struct ConfigEntry {
  std::string k;
  std::string v;
  template<class Action> void persist( Action& a ) {
    Wt::Dbo::field( a,k,"k");
    Wt::Dbo::field( a,v,"v");
  }
};

(sorry for having missed it in the first post).

If there's no way to reliable read from a Set Returning Function, I will go the VIEW way.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Roel Standaert over 5 years ago

You'll probably also need to disable the id and the version field, which are implicitly defined by default, see the Dbo tutorial on that: https://www.webtoolkit.eu/wt/wt3/doc/tutorial/dbo.html#_changing_or_disabling_the_surrogate_primary_key_id_field

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

I have added this code:

namespace Wt {
  namespace Dbo {
    template<>
    struct dbo_traits<ConfigEntry> : public dbo_default_traits {
      static const char *surrogateIdField() {
        return "k";
      }
      static const char *versionField() {
        return 0;
      }
    };
  }
}

But I am still catching a Wt::Dbo::Exception saying: "Session::query(): too many aliases for result".

The exception code is empty.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Roel Standaert over 5 years ago

What I wanted to say is that you need to change the query too, to say something like:

"select * from session_configuration()", or "select c from session_configuration() c".

As for the surrogateIdField() change: if your key field is a member of ConfigEntry, you'll probably want to specify it as a natural primary key: https://www.webtoolkit.eu/wt/wt3/doc/tutorial/dbo.html#_specifying_a_natural_primary_key

Regards,

Roel

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

I have updated the code a little bit and have moved to a VIEW.

struct ConfigEntry {
  std::string k;
  std::string v;
  template<class Action> void persist( Action& a ) {
    Wt::Dbo::field( a,k,"k");
    Wt::Dbo::field( a,v,"v");
    Wt::Dbo::id( a,k,"k");
  }
};

namespace Wt {
  namespace Dbo {
    template<>
    struct dbo_traits<ConfigEntry> : public dbo_default_traits {
      typedef std::string IdType;
      static IdType invalidId() {
        return std::string();
      }
      static const char *surrogateIdField() {
        return 0;
      }
      static const char *versionField() {
        return 0;
      }
    };
  }
}

Wt::WApplication *applicationInit( const Wt::WEnvironment& env ) {
  Wt::WApplication *app = new Wt::WApplication( env );
  const char* dbconn = getenv( "DBCONNINFO" );
  if( dbconn == 0 || dbconn[0] == 0 )
    dbconn = "dbname=tmp1";
  dbBackEnd.connect( std::string( dbconn ) );
  dbSession.setConnection( dbBackEnd );
  dbSession.mapClass<ConfigEntry>( "v_session_configuration" );
  Wt::Dbo::Transaction transaction( dbSession );
  dbSession.query<void>( "select * from session_init()" );
  auto conf = dbSession.find<ConfigEntry>();
  if( conf.resultList().size() != 0 ) {
    configuration.clear();
    for( auto i = conf.resultList().begin(); i != conf.resultList().end(); ++i  ) {
      configuration.add( (*i)->k,(*i)->v );
    }
  }
  catch( Wt::WException& exc ) {
    DEBUG( "Wt Exception caught: '%s'",exc.what() );
  }
  catch( Wt::Dbo::Exception& exc ) {
    DEBUG( "Dbo Exception caught: '%s' code:'%s'",exc.what(),exc.code().c_str() );
  }
  catch( ... ) {
    DEBUG( "Exception ignored" );
  }
  return app;
}

Now I can retrieve only the very first item from the view (they are two).

At the second iteration I catch an exception saying:

Postgres: nextRow(): statement already finished

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Roel Standaert over 5 years ago

That looks a bit strange to me:

Wt::Dbo::field( a,k,"k");
Wt::Dbo::field( a,v,"v");
Wt::Dbo::id( a,k,"k");

You don't need that extra Wt::Dbo::field(a,k,"k"). id implies field.

Also, you're calling resultList() multiple times: it's not a simple getter. You should save the result of conf.resultList() and iterate over that.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

Thanks. It worked by replacing auto conf = dbSession.find<ConfigEntry>() with auto conf = dbSession.find<ConfigEntry>().resultList().

I have tried to use the DirectBinding strategy to see if it could work.

It didn't either.

From the documentation I read:

Returns a result list.

This returns a collection which is backed by the underlying query. The query is not actually run until this collection is traversed or its size is asked.

When using a DynamicBinding bind strategy, after a result has been fetched, the query can no longer be used.

I'd make it clearer that the resulting collection<> needs to be copied. Maybe this can be enforced.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

The resultList() method, moreover, is labelled as const.

Nonetheless it (somehow) modifies the Query on which it's being called.

If I knew really more about Wt (and C) I'd say it's misleading.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

If I refresh the page, I get:

"Wt: fatal error: Cannot map tables after schema was initialized."

Which of course makes sense.

Disconnecting the backend isn't enough and it seems I can neither check whether the class is already mapped nor "unmap" it.

Any hint?

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Vincenzo Romano over 5 years ago

I have "solved" the issue by moving the Wt::Dbo::Session object as local to the function.

But then the documentation says:

A session will typically be a long-lived object in your application.

What'd be a best practice with this type of objects?

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Roel Standaert over 5 years ago

Generally, every WApplication will have their own instance of Wt::Dbo::Session. That works well for most applications.

RE: "Wt: fatal error: Session::query(): too many aliases for result" - Added by Roel Standaert over 5 years ago

Basically, the two things to consider are:

  • Sessions are not thread safe, so usage should be synchronized. When used within a WApplication, you are using the application's update lock to take care of that.
  • Sessions should only be initialized once. When you use one per WApplication, you can simply initialize the Dbo session together with the rest of the WApplication.

If you have long running queries and you don't want to block the UI, you might want to have a Wt::Dbo::Session on another thread, though.

    (1-14/14)