Commit 441b1d9f authored by François Ferry's avatar François Ferry
Browse files

feat(order): add support for order by NULLS LAST and NULLS FIRST

related: cubicweb#301
parent 0032f636379a
Pipeline #54081 passed with stages
in 1 minute and 2 seconds
......@@ -1073,35 +1073,46 @@ class SortTerm(Node):
def __init__(self,
variable: Any,
asc: int = 1,
nulls_sort: int = 0,
copy: Optional[Any] = None) -> None:
Node.__init__(self)
self.asc = asc
self.nulls_sort = nulls_sort
if copy is None:
self.append(variable)
def initargs(self, stmt: Optional["rql.stmts.Statement"]) -> Tuple[None, int, bool]:
def initargs(self, stmt: Optional["rql.stmts.Statement"]) -> Tuple[None, int, int, bool]:
"""return list of arguments to give to __init__ to clone this node"""
return (None, self.asc, True)
return (None, self.asc, self.nulls_sort, True)
def is_equivalent(self, other: Any) -> bool:
if not Node.is_equivalent(self, other):
return False
return self.asc == other.asc
return self.asc == other.asc and self.nulls_sort == other.nulls_sort
def as_string(self, kwargs: Optional[Dict] = None) -> str:
if self.asc:
return '%s' % self.term
return '%s DESC' % self.term
return f'{self.term}{self._nulls_sort_as_string}'
return f'{self.term} DESC{self._nulls_sort_as_string}'
def __repr__(self) -> str:
if self.asc:
return '%r ASC' % self.term
return '%r DESC' % self.term
return f'{self.term} ASC{self._nulls_sort_as_string}'
return f'{self.term} DESC{self._nulls_sort_as_string}'
@property
def term(self):
return self.children[0]
@property
def _nulls_sort_as_string(self):
if self.nulls_sort == 0:
return ""
elif self.nulls_sort == 1:
return " NULLSFIRST"
elif self.nulls_sort == 2:
return " NULLSLAST"
###############################################################################
......
......@@ -68,6 +68,8 @@ parser Hercule:
token ORDERBY: r'(?i)ORDERBY'
token SORT_ASC: r'(?i)ASC'
token SORT_DESC: r'(?i)DESC'
token SORT_NULLSFIRST: r'(?i)NULLSFIRST'
token SORT_NULLSLAST: r'(?i)NULLSLAST'
token LIMIT: r'(?i)LIMIT'
token OFFSET: r'(?i)OFFSET'
token DATE: r'(?i)TODAY'
......@@ -180,7 +182,7 @@ rule subquery<<S>>: variables<<S>> {{ node = SubQuery() ; no
BEING r"\(" union<<Union()>> r"\)" {{ node.set_query(union); return node }}
rule sort_term<<S>>: expr_add<<S>> sort_meth {{ return SortTerm(expr_add, sort_meth) }}
rule sort_term<<S>>: expr_add<<S>> sort_meth sort_meth_nulls {{ return SortTerm(expr_add, sort_meth, sort_meth_nulls) }}
rule sort_meth: SORT_DESC {{ return 0 }}
......@@ -190,6 +192,13 @@ rule sort_meth: SORT_DESC {{ return 0 }}
| {{ return 1 # default to SORT_ASC }}
rule sort_meth_nulls: SORT_NULLSFIRST {{ return 1 }}
| SORT_NULLSLAST {{ return 2 }}
| {{ return 0 # default }}
#// Limit and offset ############################################################
rule limit_offset<<R>> : limit<<R>> offset<<R>> {{ return limit or offset }}
......
This diff is collapsed.
......@@ -465,6 +465,48 @@ class NodesTest(TestCase):
self.assertIsInstance(sort[0], nodes.SortTerm)
self.assertEqual(sort[0].term.name, 'N')
self.assertEqual(sort[0].asc, 0)
self.assertEqual(sort[0].nulls_sort, 0)
self.assertEqual(select.distinct, False)
def test_select_ord_nullsfirst(self):
tree = self._parse("Any X ORDERBY N DESC NULLSFIRST WHERE X is Person, X name N")
select = tree.children[0]
self.assertEqual(len(select.children), 3)
self.assertIsInstance(select.where, nodes.And)
sort = select.orderby
self.assertIsInstance(sort[0], nodes.SortTerm)
self.assertEqual(sort[0].term.name, 'N')
self.assertEqual(sort[0].asc, 0)
self.assertEqual(sort[0].nulls_sort, 1)
self.assertEqual(select.distinct, False)
def test_select_ord_nullslast(self):
tree = self._parse("Any X ORDERBY N DESC NULLSLAST WHERE X is Person, X name N")
select = tree.children[0]
self.assertEqual(len(select.children), 3)
self.assertIsInstance(select.where, nodes.And)
sort = select.orderby
self.assertIsInstance(sort[0], nodes.SortTerm)
self.assertEqual(sort[0].term.name, 'N')
self.assertEqual(sort[0].asc, 0)
self.assertEqual(sort[0].nulls_sort, 2)
self.assertEqual(select.distinct, False)
def test_select_ord_multi_nullslast(self):
tree = self._parse(
"Any X ORDERBY N DESC NULLSLAST,X NULLSFIRST WHERE X is Person, X name N")
select = tree.children[0]
self.assertEqual(len(select.children), 4)
self.assertIsInstance(select.where, nodes.And)
sort = select.orderby
self.assertIsInstance(sort[0], nodes.SortTerm)
self.assertEqual(sort[0].term.name, 'N')
self.assertEqual(sort[0].asc, 0)
self.assertEqual(sort[0].nulls_sort, 2)
self.assertIsInstance(sort[1], nodes.SortTerm)
self.assertEqual(sort[1].term.name, 'X')
self.assertEqual(sort[1].asc, 1)
self.assertEqual(sort[1].nulls_sort, 1)
self.assertEqual(select.distinct, False)
def test_select_group_ord_asc(self):
......
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