Commit 13fc8ab6 authored by Laurent Wouters's avatar Laurent Wouters

[rql] Store selected variables for RQL select queries in ResultSet (#17218476)

By storing the name of the selected variables for RQL select queries in the
ResultSet (within the "variables" attribute), the information can be passed
down to specific protocols, e.g. rqlio that may wish to pass is down further
to clients.
In turn, clients can then choose to present the results of RQL select queries
as symbolic bindings using the names used in the query's projection, instead of
ordinal arrays.
parent 665f66e57fc6
......@@ -42,7 +42,7 @@ class ResultSet(object):
:param rql: the original RQL query string
"""
def __init__(self, results, rql, args=None, description=None):
def __init__(self, results, rql, args=None, description=None, variables=None):
self.rows = results
self.rowcount = results and len(results) or 0
# original query and arguments
......@@ -54,6 +54,7 @@ class ResultSet(object):
self.description = []
else:
self.description = description
self.variables = variables if variables is not None else []
# set to (limit, offset) when a result set is limited using the
# .limit method
self.limited = None
......
......@@ -25,7 +25,8 @@ from itertools import repeat
from rql import RQLSyntaxError, CoercionError
from rql.stmts import Union
from rql.nodes import ETYPE_PYOBJ_MAP, etype_from_pyobj, Relation, Exists, Not
from rql.nodes import ETYPE_PYOBJ_MAP, etype_from_pyobj, Relation, Exists, Not,\
VariableRef, Constant
from yams import BASE_TYPES
from cubicweb import ValidationError, Unauthorized, UnknownEid, QueryError
......@@ -579,6 +580,7 @@ class QuerierHelper(object):
# build a description for the results if necessary
descr = ()
variables = None
if build_descr:
if rqlst.TYPE == 'select':
# sample selection
......@@ -588,6 +590,8 @@ class QuerierHelper(object):
solution = rqlst.children[0].solutions[0]
description = _make_description(selected, args, solution)
descr = RepeatList(len(results), tuple(description))
variables = [self._get_projected_name(projected, rqlst.children[0].stinfo)
for projected in selected]
else:
# hard, delegate the work :o)
descr = manual_build_descr(cnx, rqlst, args, results)
......@@ -605,12 +609,24 @@ class QuerierHelper(object):
emit_to_debug_channel("rql", query_debug_informations)
# return a result set object
return ResultSet(results, rql, args, descr)
return ResultSet(results, rql, args, descr, variables)
# these are overridden by set_log_methods below
# only defining here to prevent pylint from complaining
info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
@staticmethod
def _get_projected_name(projected, stinfo):
if isinstance(projected, VariableRef):
return projected.name
elif isinstance(projected, Constant):
if stinfo['rewritten'] is None:
return str(projected)
for name, value in stinfo['rewritten'].items():
if [projected] == value:
return name
return str(projected)
class RQLCache(object):
......
......@@ -1626,7 +1626,6 @@ Any P1,B,E WHERE P1 identity P2 WITH
'X in_state S, S name SN')
self.assertEqual(rset.rows, [[peid]])
def test_nonregr_sql_cache(self):
# different SQL generated when 'name' is None or not (IS NULL).
self.assertFalse(self.qexecute('Any X WHERE X is CWEType, X name %(name)s',
......@@ -1634,6 +1633,24 @@ Any P1,B,E WHERE P1 identity P2 WITH
self.assertTrue(self.qexecute('Any X WHERE X is CWEType, X name %(name)s',
{'name': 'CWEType'}))
def test_variables_in_rset_for_select(self):
rset = self.qexecute('Any X WHERE X is CWUser, X eid %(x)s', {'x': self.ueid})
self.assertEqual(rset.variables, ['X'])
def test_only_selected_variables_in_rset(self):
rset = self.qexecute('Any X,Y WHERE X is Personne, Y is Personne, '
'X nom XD, Y nom XD, X eid Z, Y eid > Z')
# Z is not selected
self.assertEqual(rset.variables, ['X', 'Y'])
def test_no_variables_in_rset(self):
rset = self.qexecute('Any COUNT(X) WHERE X is CWUser')
self.assertEqual(rset.variables, ['COUNT(X)'])
def test_mixed_projection_in_rset(self):
rset = self.qexecute('Any X,COUNT(X) WHERE X is CWUser')
self.assertEqual(rset.variables, ['X', 'COUNT(X)'])
class NonRegressionTC(CubicWebTC):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment