code: def to_expression(self, lhs, rhs, op):
      
      if op == "raw":
        # TODO: This is not documented
        return lhs
      elif op == "between":
        return "{0} between {1} and {2}".format(lhs, *rhs)
      elif op == "in":
        return "{0} in {1}".format(lhs, self.to_tuple(rhs))
      elif op.startswith("not(") and op.endswith(")"):
        return "not ({0} {1} {2})".format(lhs, op[4:-1].strip(), rhs)
      else:
        return "{0} {1} {2}".format(lhs, op.strip(), rhs)
docs: Builds a binary sql expression.
  
      At its most basic, returns 'lhs op rhs' such as '5 + 3'. However, it also specially handles the
      'in' and 'between' operators. For each of these operators it is expected that rhs will be
      iterable.
  
      If the comparison operator is of the form 'not(op)' where op is the operator, it will result in
      not (lhs op rhs).
  
      This allows for doing the full range of null checks on composite types. For composite types,
      'is null' only returns true when all fields are null, and 'is not null' returns true only when
      all fields are not null. So, for a composite type with some null fields, 'is null' and
      'is not null' will both return false, making it difficult to get all rows that have composite
      columns with some value in them. The solution to this is to use not (composite is null) which
      is true when all composite fields, or only some composite fields are not null.