zorba-coders team mailing list archive
-
zorba-coders team
-
Mailing list archive
-
Message #19295
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
Juan Zacarias has proposed merging lp:~zorba-coders/zorba/bug1123016 into lp:zorba.
Commit message:
Implmentation of two new functions for xqxq module xqxq:query-plan and xqxq:load-from-query-plan
Requested reviews:
Matthias Brantner (matthias-brantner)
Chris Hillery (ceejatec)
Related bugs:
Bug #1123016 in Zorba: "FOTS driver does not run the tests thorugh the plan serializer"
https://bugs.launchpad.net/zorba/+bug/1123016
For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/bug1123016/+merge/155082
Implmentation of two new functions for xqxq module xqxq:query-plan and xqxq:load-from-query-plan
--
https://code.launchpad.net/~zorba-coders/zorba/bug1123016/+merge/155082
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'modules/xqxq/xqxq.xq'
--- modules/xqxq/xqxq.xq 2013-03-15 18:25:44 +0000
+++ modules/xqxq/xqxq.xq 2013-03-22 23:43:21 +0000
@@ -387,6 +387,15 @@
xs:QName) as item()* external;
+declare function xqxq:query-plan($query-key as xs:anyURI)
+ as xs:base64Binary external;
+
+declare function xqxq:load-from-query-plan($plan as xs:base64Binary)
+ as xs:anyURI external;
+
+declare function xqxq:load-from-query-plan($plan as xs:base64Binary,
+ $resolver as item()?, $mapper as item()?) as xs:anyURI external;
+
(:~
: Internal helper function. Only necessary because of incomplete HOF
: support in Zorba.
=== modified file 'modules/xqxq/xqxq.xq.src/xqxq.cpp'
--- modules/xqxq/xqxq.xq.src/xqxq.cpp 2013-03-13 16:17:38 +0000
+++ modules/xqxq/xqxq.xq.src/xqxq.cpp 2013-03-22 23:43:21 +0000
@@ -85,6 +85,14 @@
{
lFunc = new VariableValueFunction(this);
}
+ else if (localName == "query-plan")
+ {
+ lFunc = new QueryPlanFunction(this);
+ }
+ else if (localName == "load-from-query-plan")
+ {
+ lFunc = new LoadFromQueryPlanFunction(this);
+ }
}
return lFunc;
@@ -325,7 +333,7 @@
/*******************************************************************************
********************************************************************************/
-void PrepareMainModuleFunction::XQXQURIMapper::mapURI(
+void XQXQURIMapper::mapURI(
String aUri,
EntityData const* aEntityData,
std::vector<String>& oUris)
@@ -376,7 +384,7 @@
/*******************************************************************************
********************************************************************************/
-Resource* PrepareMainModuleFunction::XQXQURLResolver::resolveURL(
+Resource* XQXQURLResolver::resolveURL(
const String& aUrl,
EntityData const* aEntityData)
{
@@ -964,7 +972,126 @@
}
}
+/*******************************************************************************
+
+********************************************************************************/
+zorba::ItemSequence_t QueryPlanFunction::evaluate(
+ const Arguments_t& aArgs,
+ const zorba::StaticContext* aSctx,
+ const zorba::DynamicContext* aDctx) const
+{
+ String lQueryID = XQXQFunction::getOneStringArgument(aArgs,0);
+
+ QueryMap* lQueryMap;
+ if (!(lQueryMap= dynamic_cast<QueryMap*>(aDctx->getExternalFunctionParameter("xqxqQueryMap"))))
+ {
+ throwError("NoQueryMatch", "String identifying query does not exists.");
+ }
+
+ XQuery_t lQuery = getQuery(aDctx, lQueryID);
+
+ std::stringstream* lExcPlan = new std::stringstream();
+ if (!lQuery->saveExecutionPlan(*lExcPlan, ZORBA_USE_BINARY_ARCHIVE))
+ {
+ throwError("QueryPlanError", "FAILED getting query execution plan.");
+ }
+
+ return ItemSequence_t(new SingletonItemSequence(XQXQModule::getItemFactory()->createStreamableBase64Binary(*lExcPlan, &streamReleaser)));
+}
+
+/*******************************************************************************
+
+********************************************************************************/
+zorba::ItemSequence_t LoadFromQueryPlanFunction::evaluate(
+ const Arguments_t& aArgs,
+ const zorba::StaticContext* aSctx,
+ const zorba::DynamicContext* aDctx) const
+{
+ Item lQueryPlanItem = XQXQFunction::getItemArgument(aArgs,0);
+ std::istream& lQueryPlanStream = lQueryPlanItem.getStream();
+
+ DynamicContext* lDynCtx = const_cast<DynamicContext*>(aDctx);
+ StaticContext_t lSctxChild = aSctx->createChildContext();
+
+ QueryMap* lQueryMap;
+ if (!(lQueryMap = dynamic_cast<QueryMap*>(lDynCtx->getExternalFunctionParameter("xqxqQueryMap"))))
+ {
+ lQueryMap = new QueryMap();
+ lDynCtx->addExternalFunctionParameter("xqxqQueryMap", lQueryMap);
+ }
+
+ Zorba* lZorba = Zorba::getInstance(0);
+ XQuery_t lQuery;
+
+ std::auto_ptr<XQXQURLResolver> lResolver;
+ std::auto_ptr<XQXQURIMapper> lMapper;
+ try
+ {
+ lQuery = lZorba->createQuery();
+ if ( aArgs.size() > 2)
+ {
+ QueryPlanSerializationCallback lPlanSer;
+
+ Item lMapperFunctionItem = getItemArgument(aArgs, 2);
+ if (!lMapperFunctionItem.isNull())
+ {
+ lMapper.reset(new XQXQURIMapper(lMapperFunctionItem, lSctxChild));
+ lPlanSer.add_URIMapper(lMapper.get());
+ }
+
+ Item lResolverFunctionItem = getItemArgument(aArgs, 1);
+ if (!lResolverFunctionItem.isNull())
+ {
+ lResolver.reset(new XQXQURLResolver(lResolverFunctionItem, lSctxChild));
+ lPlanSer.add_URLResolver(lResolver.get());
+ }
+
+ lQuery->loadExecutionPlan(lQueryPlanStream, &lPlanSer);
+ }
+ else
+ {
+ lQuery->loadExecutionPlan(lQueryPlanStream);
+ }
+ }
+ catch (XQueryException& xe)
+ {
+ lQuery = NULL;
+ std::ostringstream err;
+ err << "The query loaded from the query plan raised an error at"
+ << " file" << xe.source_uri() << " line" << xe.source_line()
+ << " column" << xe.source_column() << ": " << xe.what();
+ Item errQName = XQXQModule::getItemFactory()->createQName(
+ xe.diagnostic().qname().ns(),
+ xe.diagnostic().qname().localname());
+ throw USER_EXCEPTION(errQName, err.str());
+ }
+ catch (ZorbaException& ze)
+ {
+ lQuery = NULL;
+ std::ostringstream err;
+ if (ze.diagnostic() == zerr::ZCSE0013_UNABLE_TO_LOAD_QUERY)
+ err << "The query loaded from the query plan raised an error: failed to load pre-compiled query: document, collection, or module resolver required but not given.";
+ else
+ err << "The query loaded from the query plan raised an error: "<< ze.what();
+ Item errQName = XQXQModule::getItemFactory()->createQName(
+ ze.diagnostic().qname().ns(),
+ ze.diagnostic().qname().localname());
+ throw USER_EXCEPTION(errQName, err.str());
+ }
+
+ uuid lUUID;
+ uuid::create(&lUUID);
+
+ std::stringstream lStream;
+ lStream << lUUID;
+
+ String lStrUUID = lStream.str();
+
+ lQueryMap->storeQuery(lStrUUID, lQuery, lMapper.release(), lResolver.release());
+ return ItemSequence_t(new SingletonItemSequence(XQXQModule::getItemFactory()->createAnyURI(lStrUUID)));
+}
+
}/*namespace xqxq*/ }/*namespace zorba*/
#ifdef WIN32
=== modified file 'modules/xqxq/xqxq.xq.src/xqxq.h'
--- modules/xqxq/xqxq.xq.src/xqxq.h 2013-02-15 06:11:20 +0000
+++ modules/xqxq/xqxq.xq.src/xqxq.h 2013-03-22 23:43:21 +0000
@@ -7,6 +7,7 @@
#include <zorba/external_module.h>
#include <zorba/function.h>
#include <zorba/dynamic_context.h>
+#include <zorba/serialization_callback.h>
#define XQXQ_MODULE_NAMESPACE "http://www.zorba-xquery.com/modules/xqxq"
@@ -51,6 +52,52 @@
/*******************************************************************************
+
+********************************************************************************/
+class XQXQURLResolver : public URLResolver
+{
+protected:
+ Item theFunction;
+ StaticContext_t theCtx;
+
+public:
+ XQXQURLResolver(Item& aFunction, StaticContext_t& aSctx)
+ :
+ URLResolver(),
+ theFunction(aFunction),
+ theCtx(aSctx)
+ {
+ }
+
+ virtual ~XQXQURLResolver() { }
+
+ virtual Resource* resolveURL(const String& url, EntityData const* entityData);
+};
+
+
+class XQXQURIMapper : public URIMapper
+{
+protected:
+ Item theFunction;
+ StaticContext_t theCtx;
+
+public:
+ XQXQURIMapper(Item& aFunction, StaticContext_t& aSctx)
+ :
+ URIMapper(),
+ theFunction(aFunction),
+ theCtx(aSctx)
+ {
+ }
+
+ virtual ~XQXQURIMapper(){ }
+
+ virtual void mapURI(
+ const zorba::String aUri,
+ EntityData const* aEntityData,
+ std::vector<zorba::String>& oUris);
+};
+/*******************************************************************************
Bag class for objects associated with a prepared query
********************************************************************************/
class QueryData : public SmartObject
@@ -144,52 +191,6 @@
evaluate(const Arguments_t&,
const zorba::StaticContext*,
const zorba::DynamicContext*) const;
-
-protected:
-
- class XQXQURLResolver : public URLResolver
- {
- protected:
- Item theFunction;
- StaticContext_t theCtx;
-
- public:
- XQXQURLResolver(Item& aFunction, StaticContext_t& aSctx)
- :
- URLResolver(),
- theFunction(aFunction),
- theCtx(aSctx)
- {
- }
-
- virtual ~XQXQURLResolver() { }
-
- virtual Resource* resolveURL(const String& url, EntityData const* entityData);
- };
-
-
- class XQXQURIMapper : public URIMapper
- {
- protected:
- Item theFunction;
- StaticContext_t theCtx;
-
- public:
- XQXQURIMapper(Item& aFunction, StaticContext_t& aSctx)
- :
- URIMapper(),
- theFunction(aFunction),
- theCtx(aSctx)
- {
- }
-
- virtual ~XQXQURIMapper(){ }
-
- virtual void mapURI(
- const zorba::String aUri,
- EntityData const* aEntityData,
- std::vector<zorba::String>& oUris);
- };
};
@@ -561,7 +562,87 @@
const zorba::DynamicContext*) const;
};
-
+/*******************************************************************************
+
+********************************************************************************/
+class QueryPlanFunction : public XQXQFunction
+{
+public:
+ QueryPlanFunction(const XQXQModule* aModule) : XQXQFunction(aModule) {}
+
+ virtual ~QueryPlanFunction() {}
+
+ virtual zorba::String
+ getLocalName() const {return "query-plan"; }
+
+ virtual zorba::ItemSequence_t
+ evaluate(const Arguments_t&,
+ const zorba::StaticContext*,
+ const zorba::DynamicContext*) const;
+
+ virtual String getURI() const {
+ return theModule->getURI();
+ }
+
+protected:
+ const XQXQModule* theModule;
+};
+
+
+/*******************************************************************************
+
+********************************************************************************/
+class LoadFromQueryPlanFunction : public XQXQFunction
+{
+public:
+ LoadFromQueryPlanFunction(const XQXQModule* aModule) : XQXQFunction(aModule) {}
+
+ virtual ~LoadFromQueryPlanFunction() {}
+
+ virtual zorba::String
+ getLocalName() const {return "load-from-query-plan"; }
+
+ virtual zorba::ItemSequence_t
+ evaluate(const Arguments_t&,
+ const zorba::StaticContext*,
+ const zorba::DynamicContext*) const;
+
+ virtual String getURI() const {
+ return theModule->getURI();
+ }
+
+protected:
+ const XQXQModule* theModule;
+
+ class QueryPlanSerializationCallback : public zorba::SerializationCallback
+ {
+ std::vector<URIMapper*> theUriMappers;
+ std::vector<URLResolver*>theUrlResolvers;
+
+ public:
+ QueryPlanSerializationCallback()
+ {
+ }
+
+ virtual ~QueryPlanSerializationCallback() {}
+
+ void add_URIMapper(URIMapper* aMapper)
+ {
+ theUriMappers.push_back(aMapper);
+ }
+
+ void add_URLResolver(URLResolver* aResolver)
+ {
+ theUrlResolvers.push_back(aResolver);
+ }
+
+ virtual URIMapper*
+ getURIMapper(size_t i ) const { return theUriMappers.size() < i? NULL : theUriMappers[i]; }
+
+ virtual URLResolver*
+ getURLResolver(size_t i) const { return theUrlResolvers.size() < i? NULL : theUrlResolvers[i]; }
+ };
+};
}/*xqxq namespace*/}/*zorba namespace*/
=== modified file 'src/compiler/api/compilercb.cpp'
--- src/compiler/api/compilercb.cpp 2013-03-20 23:33:11 +0000
+++ src/compiler/api/compilercb.cpp 2013-03-22 23:43:21 +0000
@@ -201,6 +201,7 @@
ar & theIsEval;
ar & theIsLoadProlog;
ar & theIsUpdating;
+ ar & theIsSequential;
ar & theSctxMap;
ar & theRootSctx;
#ifdef ZORBA_WITH_DEBUGGER
=== added file 'test/rbkt/ExpQueryResults/zorba/xqxq/query-plan.xml.res'
--- test/rbkt/ExpQueryResults/zorba/xqxq/query-plan.xml.res 1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/xqxq/query-plan.xml.res 2013-03-22 23:43:21 +0000
@@ -0,0 +1,1 @@
+2
\ No newline at end of file
=== added file 'test/rbkt/ExpQueryResults/zorba/xqxq/query-plan2.xml.res'
--- test/rbkt/ExpQueryResults/zorba/xqxq/query-plan2.xml.res 1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/xqxq/query-plan2.xml.res 2013-03-22 23:43:21 +0000
@@ -0,0 +1,1 @@
+foo
\ No newline at end of file
=== added file 'test/rbkt/ExpQueryResults/zorba/xqxq/query-plan3.xml.res'
--- test/rbkt/ExpQueryResults/zorba/xqxq/query-plan3.xml.res 1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/xqxq/query-plan3.xml.res 2013-03-22 23:43:21 +0000
@@ -0,0 +1,1 @@
+foo
\ No newline at end of file
=== added file 'test/rbkt/Queries/zorba/xqxq/query-plan.xq'
--- test/rbkt/Queries/zorba/xqxq/query-plan.xq 1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/xqxq/query-plan.xq 2013-03-22 23:43:21 +0000
@@ -0,0 +1,6 @@
+import module namespace xqxq = 'http://www.zorba-xquery.com/modules/xqxq';
+
+variable $queryID := xqxq:prepare-main-module("1+1");
+variable $query-plan := xqxq:query-plan($queryID);
+variable $queryID2 := xqxq:load-from-query-plan($query-plan);
+xqxq:evaluate ($queryID2)
=== added file 'test/rbkt/Queries/zorba/xqxq/query-plan2.xq'
--- test/rbkt/Queries/zorba/xqxq/query-plan2.xq 1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/xqxq/query-plan2.xq 2013-03-22 23:43:21 +0000
@@ -0,0 +1,20 @@
+import module namespace xqxq = 'http://www.zorba-xquery.com/modules/xqxq';
+
+declare namespace resolver = 'http://www.zorba-xquery.com/modules/xqxq/url-resolver';
+declare namespace op = "http://www.zorba-xquery.com/options/features";
+declare namespace f = "http://www.zorba-xquery.com/features";
+
+declare function resolver:url-resolver($namespace as xs:string, $entity as xs:string) {
+ if($namespace = 'http://test.xq')
+ then "module namespace test = 'http://test'; declare function test:foo(){'foo'};"
+ else ()
+};
+
+variable $queryID := xqxq:prepare-main-module(
+ "import module namespace test = 'http://test'; test:foo()",
+ resolver:url-resolver#2, ());
+
+
+variable $query-plan := xqxq:query-plan($queryID);
+variable $queryID2 := xqxq:load-from-query-plan($query-plan, resolver:url-resolver#2, ());
+xqxq:evaluate ($queryID2)
=== added file 'test/rbkt/Queries/zorba/xqxq/query-plan3.xq'
--- test/rbkt/Queries/zorba/xqxq/query-plan3.xq 1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/xqxq/query-plan3.xq 2013-03-22 23:43:21 +0000
@@ -0,0 +1,28 @@
+import module namespace xqxq = 'http://www.zorba-xquery.com/modules/xqxq';
+
+declare namespace resolver = 'http://www.zorba-xquery.com/modules/xqxq/url-resolver';
+declare namespace mapper = 'http://www.zorba-xquery.com/modules/xqxq/uri-mapper';
+declare namespace op = "http://www.zorba-xquery.com/options/features";
+declare namespace f = "http://www.zorba-xquery.com/features";
+
+declare function resolver:url-resolver($namespace as xs:string, $entity as xs:string) {
+ if($namespace = 'http://foo')
+ then "module namespace test = 'http://test'; declare function test:foo(){'foo'};"
+ else ()
+};
+
+declare function mapper:uri-mapper($namespace as xs:string, $entity as xs:string)
+{
+ if($namespace = 'http://test')
+ then 'http://foo'
+ else ()
+};
+
+variable $queryID := xqxq:prepare-main-module
+(
+ "import module namespace test = 'http://test'; test:foo()",
+ resolver:url-resolver#2, mapper:uri-mapper#2
+);
+variable $query-plan := xqxq:query-plan($queryID);
+variable $queryID2 := xqxq:load-from-query-plan($query-plan, resolver:url-resolver#2, mapper:uri-mapper#2);
+xqxq:evaluate ($queryID2)
=== added file 'test/rbkt/Queries/zorba/xqxq/query-plan4.spec'
--- test/rbkt/Queries/zorba/xqxq/query-plan4.spec 1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/xqxq/query-plan4.spec 2013-03-22 23:43:21 +0000
@@ -0,0 +1,1 @@
+Error: http://www.zorba-xquery.com/errors:ZCSE0013
=== added file 'test/rbkt/Queries/zorba/xqxq/query-plan4.xq'
--- test/rbkt/Queries/zorba/xqxq/query-plan4.xq 1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/xqxq/query-plan4.xq 2013-03-22 23:43:21 +0000
@@ -0,0 +1,28 @@
+import module namespace xqxq = 'http://www.zorba-xquery.com/modules/xqxq';
+
+declare namespace resolver = 'http://www.zorba-xquery.com/modules/xqxq/url-resolver';
+declare namespace mapper = 'http://www.zorba-xquery.com/modules/xqxq/uri-mapper';
+declare namespace op = "http://www.zorba-xquery.com/options/features";
+declare namespace f = "http://www.zorba-xquery.com/features";
+
+declare function resolver:url-resolver($namespace as xs:string, $entity as xs:string) {
+ if($namespace = 'http://foo')
+ then "module namespace test = 'http://test'; declare function test:foo(){'foo'};"
+ else ()
+};
+
+declare function mapper:uri-mapper($namespace as xs:string, $entity as xs:string)
+{
+ if($namespace = 'http://test')
+ then 'http://foo'
+ else ()
+};
+
+variable $queryID := xqxq:prepare-main-module
+(
+ "import module namespace test = 'http://test'; test:foo()",
+ resolver:url-resolver#2, mapper:uri-mapper#2
+);
+variable $query-plan := xqxq:query-plan($queryID);
+variable $queryID2 := xqxq:load-from-query-plan($query-plan);
+xqxq:evaluate ($queryID2)
Follow ups
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: noreply, 2013-04-10
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-10
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Chris Hillery, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Chris Hillery, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Chris Hillery, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Chris Hillery, 2013-04-09
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Chris Hillery, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Sorin Marian Nasoi, 2013-04-09
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-27
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-27
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-27
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-27
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Matthias Brantner, 2013-03-27
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Matthias Brantner, 2013-03-27
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-26
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-26
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-26
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Zorba Build Bot, 2013-03-26
-
[Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Matthias Brantner, 2013-03-26
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Matthias Brantner, 2013-03-26
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Matthias Brantner, 2013-03-26
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Juan Zacarias, 2013-03-25
-
Re: [Merge] lp:~zorba-coders/zorba/bug1123016 into lp:zorba
From: Matthias Brantner, 2013-03-23