Commits

Kirill Simonov  committed 30f4e1b

Adding the `.limit()` method.

At the moment, it does not work correctly since `LIMIT` is applied
before `ORDER BY`.

  • Participants
  • Parent commits ad03491

Comments (0)

Files changed (6)

File src/htsql/request.py

                 rows = cursor.fetchall()
                 connection.close()
             except DBError, exc:
-                raise EngineError(str(exc), plan.mark)
+                raise EngineError("error while executing %r: %s"
+                                  % (plan.sql, exc), plan.mark)
             records = []
             select = plan.frame.segment.select
             normalizers = []

File src/htsql/tr/assembler.py

                             self.space, baseline, routes, self.space.mark)
 
 
+    def inject(self, term):
+        assert self.space in term.routes
+        return term
+
+
 class AssembleColumn(AssembleUnit):
 
     adapts(ColumnUnit, Assembler)

File src/htsql/tr/binding.py

 from ..entity import CatalogEntity, TableEntity, ColumnEntity, Join
 from ..domain import Domain, VoidDomain, BooleanDomain, TupleDomain
 from .syntax import Syntax
-from ..util import maybe, listof, Node
+from ..util import maybe, listof, tupleof, Node
 
 
 class Binding(Node):
         self.binding = binding
 
 
+class OrderedBinding(Binding):
+
+    def __init__(self, parent, order, limit, offset, syntax):
+        assert isinstance(order, listof(tupleof(Binding, int)))
+        assert isinstance(limit, maybe(int))
+        assert isinstance(offset, maybe(int))
+        super(OrderedBinding, self).__init__(parent, parent.domain, syntax)
+        self.order = order
+        self.limit = limit
+        self.offset = offset
+
+
 class TupleBinding(Binding):
 
     def __init__(self, binding):

File src/htsql/tr/encoder.py

 from .binding import (Binding, RootBinding, QueryBinding, SegmentBinding,
                       TableBinding, FreeTableBinding, JoinedTableBinding,
                       ColumnBinding, LiteralBinding, SieveBinding,
-                      EqualityBinding, InequalityBinding,
+                      OrderedBinding, EqualityBinding, InequalityBinding,
                       ConjunctionBinding, DisjunctionBinding,
                       NegationBinding, CastBinding, TupleBinding)
 from .code import (ScalarSpace, FreeTableSpace, JoinedTableSpace,
         return ScreenSpace(space, filter, self.binding.mark)
 
 
+class EncodeOrdered(Encode):
+
+    adapts(OrderedBinding, Encoder)
+
+    def relate(self):
+        space = self.encoder.relate(self.binding.parent)
+        order = [self.encoder.encode(binding)
+                 for binding in self.binding.order]
+        limit = self.binding.limit
+        offset = self.binding.offset
+        return OrderedSpace(space, order, limit, offset, self.binding.mark)
+
+
 class EncodeTuple(Encode):
 
     adapts(TupleBinding, Encoder)

File src/htsql/tr/fn/function.py

 from ...error import InvalidArgumentError
 from ...domain import (Domain, UntypedDomain, BooleanDomain, StringDomain,
                        IntegerDomain, DecimalDomain, FloatDomain, DateDomain)
-from ..binding import (LiteralBinding, FunctionBinding,
+from ..binding import (LiteralBinding, OrderedBinding, FunctionBinding,
                        EqualityBinding, InequalityBinding,
                        ConjunctionBinding, DisjunctionBinding, NegationBinding)
 from ..encoder import Encoder, Encode
     adapts_none()
 
     def bind_arguments(self, arguments, parent, mark):
-        arguments = [parent] + [list(self.binder.bind(argument, parent))
-                                for argument in arguments]
+        arguments = [[parent]] + [list(self.binder.bind(argument, parent))
+                                  for argument in arguments]
         return self.check_arguments(arguments, mark)
 
 
+class LimitMethod(ProperMethod):
+
+    adapts(named['limit'])
+
+    parameters = [
+            Parameter('this'),
+            Parameter('limit', IntegerDomain),
+            Parameter('offset', IntegerDomain, is_mandatory=False),
+    ]
+
+    def correlate(self, this, limit, offset, syntax, parent):
+        if not (isinstance(limit, LiteralBinding) and
+                (limit.value is None or limit.value >= 0)):
+            raise InvalidArgumentError("expected a non-negative integer",
+                                       limit.mark)
+        if not (offset is None or
+                (isinstance(offset, LiteralBinding) and
+                 (offset.value is None or offset.value >= 0))):
+            raise InvalidArgumentError("expected a non-negative integer",
+                                       offset.mark)
+        limit = limit.value
+        if offset is not None:
+            offset = offset.value
+        yield OrderedBinding(parent, [], limit, offset, syntax)
+
+
 class NullFunction(ProperFunction):
 
     adapts(named['null'])

File src/htsql/tr/lookup.py

 from ..adapter import Adapter, adapts, find_adapters
 from .binding import (Binding, RootBinding, QueryBinding, SegmentBinding,
                       TableBinding, FreeTableBinding, JoinedTableBinding,
-                      ColumnBinding, LiteralBinding, SieveBinding)
+                      ColumnBinding, LiteralBinding, SieveBinding,
+                      OrderedBinding)
 from .syntax import Syntax, IdentifierSyntax
 from ..error import InvalidArgumentError
 from ..entity import DirectJoin, ReverseJoin
     adapts(SieveBinding)
 
 
+class LookupOrdered(ProxyMixin, Lookup):
+
+    adapts(OrderedBinding)
+
+
 lookup_adapters = find_adapters()