"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).
conf.resultList().begin()@ an exception gets triggered and the above message appears in the logs:
As soon as I run
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 theWApplication
.
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.