Commits

Kirill Simonov  committed 9112151

Added `length()` method and `~` operator.

  • Participants
  • Parent commits 5c0dcc2

Comments (0)

Files changed (4)

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

         return self.format.switch_fn(token, items, values)
 
 
+class LengthMethod(ProperMethod):
+
+    adapts(named['length'])
+
+    parameters = [
+            Parameter('this')
+    ]
+
+    def correlate(self, this, syntax, parent):
+        Implementation = Length.realize(this.domain)
+        length = Implementation(this, self.binder, syntax, parent)
+        yield length()
+
+
+class Length(Adapter):
+
+    adapts(Domain)
+
+    def __init__(self, this, binder, syntax, parent):
+        self.this = this
+        self.binder = binder
+        self.syntax = syntax
+        self.parent = parent
+
+    def __call__(self):
+        raise InvalidArgumentError("unexpected type", self.syntax.mark)
+
+
+class TextLength(Length):
+
+    adapts_none()
+
+    def __call__(self):
+        this = self.binder.cast(self.this, StringDomain())
+        return TextLengthBinding(self.parent, IntegerDomain(), self.syntax,
+                                 this=this)
+
+
+class TextLengthOfString(TextLength):
+
+    adapts(StringDomain)
+
+
+class TextLengthOfUntyped(TextLength):
+
+    adapts(UntypedDomain)
+
+
+TextLengthBinding = GenericBinding.factory(LengthMethod)
+TextLengthExpression = GenericExpression.factory(LengthMethod)
+TextLengthPhrase = GenericPhrase.factory(LengthMethod)
+
+
+EncodeTextLength = GenericEncode.factory(LengthMethod,
+        TextLengthBinding, TextLengthExpression)
+EvaluateTextLength = GenericEvaluate.factory(LengthMethod,
+        TextLengthExpression, TextLengthPhrase)
+SerializeTextLength = GenericSerialize.factory(LengthMethod,
+        TextLengthPhrase, "CHARACTER_LENGTH(%(this)s)")
+
+
+class ContainsOperator(ProperFunction):
+
+    adapts(named['~'])
+
+    parameters = [
+            Parameter('left'),
+            Parameter('right'),
+    ]
+
+    def correlate(self, left, right, syntax, parent):
+        Implementation = Contains.realize(left.domain, right.domain)
+        length = Implementation(left, right, self.binder, syntax, parent)
+        yield length()
+
+
+class Contains(Adapter):
+
+    adapts(Domain, Domain)
+
+    def __init__(self, left, right, binder, syntax, parent):
+        self.left = left
+        self.right = right
+        self.binder = binder
+        self.syntax = syntax
+        self.parent = parent
+
+    def __call__(self):
+        raise InvalidArgumentError("unexpected types", self.syntax.mark)
+
+
+class ContainsStrings(Contains):
+
+    adapts_none()
+
+    def __call__(self):
+        left = self.binder.cast(self.left, StringDomain(),
+                                parent=self.parent)
+        right = self.binder.cast(self.right, StringDomain(),
+                                 parent=self.parent)
+        return ContainsBinding(self.parent, BooleanDomain(), self.syntax,
+                               left=left, right=right)
+
+
+class ContainsStringInString(ContainsStrings):
+
+    adapts(StringDomain, StringDomain)
+
+
+class ContainsUntypedInString(ContainsStrings):
+
+    adapts(StringDomain, UntypedDomain)
+
+
+class ContainsStringInUntyped(ContainsStrings):
+
+    adapts(UntypedDomain, StringDomain)
+
+
+class ContainsUntypedInUntyped(ContainsStrings):
+
+    adapts(UntypedDomain, UntypedDomain)
+
+
+ContainsBinding = GenericBinding.factory(ContainsOperator)
+ContainsExpression = GenericExpression.factory(ContainsOperator)
+ContainsPhrase = GenericPhrase.factory(ContainsOperator)
+
+
+EncodeContains = GenericEncode.factory(ContainsOperator,
+        ContainsBinding, ContainsExpression)
+EvaluateContains = GenericEvaluate.factory(ContainsOperator,
+        ContainsExpression, ContainsPhrase)
+SerializeContains = GenericSerialize.factory(ContainsOperator,
+        ContainsPhrase, "(POSITION(LOWER(%(right)s) IN LOWER(%(left)s)) > 0)")
+
+
 class FormatFunctions(Format):
 
     weights(0)

File src/htsql_sqlite/tr/serializer.py

 
 from htsql.adapter import adapts, find_adapters
 from htsql.tr.frame import LeafFrame
-from htsql.tr.serializer import Serializer, SerializeLeaf
+from htsql.tr.serializer import Serializer, Format, SerializeLeaf
+
+
+class SQLiteFormat(Format):
+
+    def true(self):
+        return "1"
+
+    def false(self):
+        return "0"
 
 
 class SQLiteSerializeLeaf(SerializeLeaf):

File test/input/pgsql.yaml

         tests:
         # String cast (from untyped, string, integer, decimal, float).
         - uri: /{string('X'),string(string('X')), string(1), string(1.0), string(1e0)}
+        # The Length method.
+        - uri: /{'HTSQL'.length(),''.length(),null().length()}
+        # The Contains operator.
+        - uri: /{'HTSQL'~'sql','HTSQL'~'HTTP','HTSQL'~'',
+                 'HTSQL'~null(),null()~'HTSQL',null()~null()}
 
   # Simple (non-aggregate) filters.
   - title: Simple filters

File test/output/pgsql.yaml

              ----
              /{string('X'),string(string('X')),string(1),string(1.0),string(1e0)}
              SELECT 'X', 'X', CAST(1 AS TEXT), CAST(1.0 AS TEXT), CAST(1.0::float8 AS TEXT)
+        - uri: /{'HTSQL'.length(),''.length(),null().length()}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | /{'HTSQL'.length(),''.length(),null().length()}  |
+            -+--------------------------------------------------+-
+             | 'HTSQL'.length() | ''.length() | null().length() |
+            -+------------------+-------------+-----------------+-
+             |                5 |           0 |                 |
+                                                          (1 row)
+
+             ----
+             /{'HTSQL'.length(),''.length(),null().length()}
+             SELECT CHARACTER_LENGTH('HTSQL'), CHARACTER_LENGTH(''), CHARACTER_LENGTH(NULL)
+        - uri: /{'HTSQL'~'sql','HTSQL'~'HTTP','HTSQL'~'', 'HTSQL'~null(),null()~'HTSQL',null()~null()}
+          status: 200 OK
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |2
+             | /{'HTSQL'~'sql','HTSQL'~'HTTP','HTSQL'~'','HTSQL'~null(),null()~'HTSQL',null()~null()}        |
+            -+-----------------------------------------------------------------------------------------------+-
+             | 'HTSQL'~'sql' | 'HTSQL'~'HTTP' | 'HTSQL'~'' | 'HTSQL'~null() | null()~'HTSQL' | null()~null() |
+            -+---------------+----------------+------------+----------------+----------------+---------------+-
+             | true          | false          | true       |                |                |               |
+                                                                                                       (1 row)
+
+             ----
+             /{'HTSQL'~'sql','HTSQL'~'HTTP','HTSQL'~'','HTSQL'~null(),null()~'HTSQL',null()~null()}
+             SELECT (POSITION(LOWER('sql') IN LOWER('HTSQL')) > 0), (POSITION(LOWER('HTTP') IN LOWER('HTSQL')) > 0), (POSITION(LOWER('') IN LOWER('HTSQL')) > 0), (POSITION(LOWER(NULL) IN LOWER('HTSQL')) > 0), (POSITION(LOWER('HTSQL') IN LOWER(NULL)) > 0), (POSITION(LOWER(NULL) IN LOWER(NULL)) > 0)
   - id: simple-filters
     tests:
     - uri: /school?code='ns'