Commits

Kirill Simonov committed 045fef0

Updated function bind error messages.

Comments (0)

Files changed (9)

src/htsql/tr/bind.py

         for binding in bindings:
             domain = coerce(binding.domain)
             if domain is None:
-                raise BindError("output column must be a scalar value",
+                raise BindError("output column must be scalar",
                                 binding.mark)
             element = CastBinding(binding, domain, binding.syntax)
             elements.append(element)
         for element in elements:
             domain = coerce(element.domain)
             if domain is None:
-                raise BindError("quotient column must be a scalar value",
+                raise BindError("quotient column must be scalar",
                                 element.mark)
             kernel = CastBinding(element, domain, element.syntax)
             kernels.append(kernel)
         for origin_image, target_image in zip(origin_images, target_images):
             domain = coerce(origin_image.domain, target_image.domain)
             if domain is None:
-                raise BindError("cannot convert origin and target columns"
+                raise BindError("cannot coerce origin and target columns"
                                 " to a common type", self.syntax.mark)
             origin_image = CastBinding(origin_image, domain,
                                        origin_image.syntax)

src/htsql/tr/fn/bind.py

                        IntegerDomain, DecimalDomain, FloatDomain,
                        DateDomain, TimeDomain, DateTimeDomain, EnumDomain)
 from ..syntax import (NumberSyntax, StringSyntax, IdentifierSyntax,
-                      SpecifierSyntax, ApplicationSyntax, GroupSyntax)
+                      SpecifierSyntax, ApplicationSyntax, OperatorSyntax,
+                      GroupSyntax)
 from ..binding import (LiteralBinding, SortBinding, SieveBinding,
                        FormulaBinding, CastBinding, WrappingBinding,
                        TitleBinding, DirectionBinding, QuotientBinding,
                        CommandBinding, SegmentBinding, QueryBinding, Binding,
                        BindingRecipe, ComplementRecipe, KernelRecipe,
                        SubstitutionRecipe, ClosedRecipe)
-from ..bind import BindByName, BindByRecipe, BindingState
+from ..bind import BindByName, BindingState
 from ..error import BindError
 from ..coerce import coerce
 from ..lookup import direct, expand, guess_name, lookup_command
     def match(self):
         assert self.signature is not None
         arguments = {}
-        if isinstance(self.syntax, ApplicationSyntax):
-            operands = self.syntax.arguments[:]
+        if self.arguments is None:
+            operands = []
         else:
-            operands = []
+            operands = self.arguments[:]
+        min_args = len([slot for slot in self.signature.slots
+                             if slot.is_mandatory])
+        max_args = len(self.signature.slots)
+        if self.signature.slots and not self.signature.slots[-1].is_singular:
+            max_args = None
+        if len(operands) < min_args or (max_args is not None and
+                                        len(operands) > max_args):
+            if min_args == max_args == 1:
+                message = "1 argument"
+            elif min_args == max_args:
+                message = "%s arguments" % min_args
+            elif max_args == 1:
+                message = "%s to %s argument" % (min_args, max_args)
+            elif max_args is not None:
+                message = "%s to %s arguments" % (min_args, max_args)
+            else:
+                message = "%s or more arguments" % min_args
+            raise BindError("function '%s' expects %s; got %s"
+                            % (self.name.encode('utf-8'),
+                               message, len(operands)),
+                            self.syntax.mark)
+
         for index, slot in enumerate(self.signature.slots):
             name = slot.name
             value = None
             if not operands:
-                if slot.is_mandatory:
-                    raise BindError("missing argument %s" % name,
-                                    self.syntax.mark)
+                assert not slot.is_mandatory
                 if not slot.is_singular:
                     value = []
             elif slot.is_singular:
                 else:
                     value = [operands.pop(0)]
             arguments[name] = value
-        if operands:
-            raise BindError("unexpected argument", operands[0].mark)
+        assert not operands
         return arguments
 
     def bind(self):
                     recipies = expand(bound_value, is_hard=False)
                     if slot.is_mandatory and (recipies is not None and
                                               not recipies):
-                        raise BindError("missing argument %s" % name,
+                        raise BindError("at least one element is expected",
                                         value.mark)
                     if recipies is None:
                         bound_value = [bound_value]
                     else:
                         bound_value = []
                         for syntax, recipe in recipies:
-                            bind = BindByRecipe(recipe, syntax, self.state)
-                            bound_value.append(bind())
+                            bound_value.append(self.state.use(recipe, syntax))
                 else:
                     bound_value = []
             bound_arguments[name] = bound_value
                 domains.extend(item.domain for item in value)
         domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            if len(domains) > 1:
+                raise BindError("cannot coerce values of types (%s)"
+                                " to a common type"
+                                % (", ".join("'%s'" % domain.family
+                                             for domain in domains)),
+                                self.syntax.mark)
+            else:
+                raise BindError("a scalar value is expected",
+                                self.syntax.mark)
         cast_arguments = {}
         for slot in self.signature.slots:
             name = slot.name
         self.arguments = binding.arguments
 
     def __call__(self):
-        raise BindError("incompatible arguments", self.binding.mark)
+        if isinstance(self.binding.syntax, OperatorSyntax):
+            name = "operator '%s'" % self.binding.syntax.symbol.encode('utf-8')
+        elif isinstance(self.binding.syntax, ApplicationSyntax):
+            name = "function '%s'" % self.binding.syntax.name.encode('utf-8')
+        else:
+            name = "'%s'" % self.binding.syntax
+        key_signature, domain_vector = self.dispatch_key
+        if len(domain_vector) > 1:
+            types = "types"
+            values = "values"
+        else:
+            types = "type"
+            values = "a value"
+        families = ", ".join("'%s'" % domain_class.family
+                             for domain_class in domain_vector)
+        if len(domain_vector) > 1:
+            families = "(%s)" % families
+        valid_types = []
+        for component in self.interface.implementations():
+            if component.input_signature is None:
+                continue
+            if not issubclass(key_signature, component.input_signature):
+                continue
+            for domain_vector in component.input_domains:
+                if any(issubclass(domain_class, UntypedDomain)
+                       for domain_class in domain_vector):
+                    continue
+                valid_families = ", ".join("'%s'" % domain_class.family
+                                           for domain_class in domain_vector)
+                if len(domain_vector) > 1:
+                    valid_families = "(%s)" % valid_families
+                if valid_families not in valid_types:
+                    valid_types.append(valid_families)
+        hint = None
+        if valid_types:
+            hint = "valid %s: %s" % (types, ", ".join(valid_types))
+        raise BindError("%s cannot be applied to %s of %s %s"
+                        % (name, values, types, families),
+                        self.binding.mark, hint=hint)
 
 
 def correlates(signature, *domain_vectors):
     def expand(self, op):
         op = self.state.bind(op)
         if not isinstance(op, SegmentBinding):
-            raise BindError("a segment is required", op.mark)
+            raise BindError("function '%s' expects a segment argument"
+                            % self.name.encode('utf-8'), op.mark)
         binding = QueryBinding(self.state.root, op, op.syntax)
         command = RetrieveCmd(binding)
         return CommandBinding(self.state.scope, command, self.syntax)
         producer = lookup_command(op)
         if producer is None:
             if not isinstance(op, SegmentBinding):
-                raise BindError("a segment is required", op.mark)
+                raise BindError("function '%s' expects a segment argument"
+                                % self.name.encode('utf-8'), op.mark)
             binding = QueryBinding(self.state.root, op, op.syntax)
             producer = DefaultCmd(binding)
         command = self.command(producer)
         seed = self.state.bind(op)
         recipes = expand(seed, is_hard=False)
         if recipes is None:
-            raise BindError("a selector is required", op.mark)
+            raise BindError("function '%s' expects an argument with a selector"
+                            % self.name.encode('utf-8'), op.mark)
         kernels = []
         for syntax, recipe in recipes:
             element = self.state.use(recipe, syntax, scope=seed)
             element = RescopingBinding(element, seed, element.syntax)
             domain = coerce(element.domain)
             if domain is None:
-                raise BindError("invalid element type", element.mark)
+                raise BindError("quotient column must be scalar", element.mark)
             element = CastBinding(element, domain, element.syntax)
             kernels.append(element)
         quotient = QuotientBinding(self.state.scope, seed, kernels,
 
     def expand(self, base, title):
         if not isinstance(title, (StringSyntax, IdentifierSyntax)):
-            raise BindError("expected a string literal or an identifier",
+            raise BindError("function '%s' expects a string literal"
+                            " or an identifier" % self.name.encode('utf-8'),
                             title.mark)
         base = self.state.bind(base)
         return TitleBinding(base, title.value, self.syntax)
                         syntax = GroupSyntax(syntax, syntax.mark)
                     syntax = SpecifierSyntax(element.syntax, syntax,
                                              syntax.mark)
-                    bind = BindByRecipe(recipe, syntax, self.state)
-                    elements.append(bind())
+                    elements.append(self.state.use(recipe, syntax))
             else:
                 elements.append(element)
         order = []
                         syntax = GroupSyntax(syntax, syntax.mark)
                     syntax = SpecifierSyntax(element.syntax, syntax,
                                              syntax.mark)
-                    bind = BindByRecipe(recipe, syntax, self.state)
-                    elements.append(bind())
+                    elements.append(self.state.use(recipe, syntax))
             else:
                 elements.append(element)
         return ForkBinding(self.state.scope, elements, self.syntax)
             if not (value >= 0):
                 raise ValueError
         except ValueError:
-            raise BindError("expected a non-negative integer", argument.mark)
+            raise BindError("function '%s' expects a non-negative integer"
+                            % self.name.encode('utf-8'), argument.mark)
         return value
 
     def expand(self, limit, offset=None):
             if recipies is None:
                 domain = coerce(binding.domain)
                 if domain is None:
-                    raise BindError("incompatible expression type",
+                    raise BindError("function '%s' expects a scalar"
+                                    " expression" % self.name.encode('utf-8'),
                                     binding.mark)
                 binding = CastBinding(binding, domain, binding.syntax)
                 bindings.append(binding)
             else:
                 for syntax, recipe in recipies:
-                    bind = BindByRecipe(recipe, syntax, self.state)
-                    binding = bind()
+                    binding = self.state.use(recipe, syntax)
                     bindings.append(binding)
         return SortBinding(self.state.scope, bindings, None, None, self.syntax)
 
         for op in ops:
             assignment = self.state.bind(op, scope=binding)
             if not isinstance(assignment, AssignmentBinding):
-                raise BindError("an assignment expected", op.mark)
+                raise BindError("function '%s' expects an assignment"
+                                " expression" % self.name.encode('utf-8'),
+                                op.mark)
             name, is_reference = assignment.terms[0]
             arity = None
             if is_reference:
         for op in rops:
             assignment = self.state.bind(op, scope=binding)
             if not isinstance(assignment, AssignmentBinding):
-                raise BindError("an assignment expected", op.mark)
+                raise BindError("function '%s' expects an assignment"
+                                " expression" % self.name.encode('utf-8'),
+                                op.mark)
             name, is_reference = assignment.terms[0]
             arity = None
             if is_reference:
     polarity = None
 
     def correlate(self, lop, rops):
-        domain = coerce(lop.domain, *(rop.domain for rop in rops))
+        domains = [lop.domain] + [rop.domain for rop in rops]
+        domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            raise BindError("cannot coerce values of types (%s)"
+                            " to a common type"
+                            % (", ".join("'%s'" % domain.family
+                                         for domain in domains)),
+                            self.syntax.mark)
         lop = CastBinding(lop, domain, lop.syntax)
         rops = [CastBinding(rop, domain, rop.syntax) for rop in rops]
         if len(rops) == 1:
     polarity = None
 
     def correlate(self, lop, rop):
-        domain = coerce(lop.domain, rop.domain)
+        domains = [lop.domain, rop.domain]
+        domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            raise BindError("cannot coerce values of types (%s)"
+                            " to a common type"
+                            % (", ".join("'%s'" % domain.family
+                                         for domain in domains)),
+                            self.syntax.mark)
         lop = CastBinding(lop, domain, lop.syntax)
         rop = CastBinding(rop, domain, rop.syntax)
         return FormulaBinding(self.state.scope,
     relation = None
 
     def correlate(self, lop, rop):
-        domain = coerce(lop.domain, rop.domain)
+        domains = [lop.domain, rop.domain]
+        domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            raise BindError("cannot coerce values of types (%s)"
+                            " to a common type"
+                            % (", ".join("'%s'" % domain.family
+                                         for domain in domains)),
+                            self.syntax.mark)
         lop = CastBinding(lop, domain, lop.syntax)
         rop = CastBinding(rop, domain, rop.syntax)
         comparable = Comparable(domain)
         if not comparable():
-            raise BindError("uncomparable arguments", self.syntax.mark)
+            raise BindError("values of type '%s' are not comparable"
+                            % domain.family, self.syntax.mark)
         return FormulaBinding(self.state.scope,
                               self.signature(self.relation),
                               coerce(BooleanDomain()),
     def match(self):
         operands = list(reversed(self.syntax.arguments))
         if len(operands) < 2:
-            raise BindError("not enough arguments", self.syntax.mark)
+            raise BindError("function '%s' expects 2 or more arguments;"
+                            " got %s" % (self.name.encode('utf-8'),
+                                         len(operands)), self.syntax.mark)
         predicates = []
         consequents = []
         alternative = None
             domains.append(alternative.domain)
         domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            if len(domains) > 1:
+                raise BindError("cannot coerce values of types (%s)"
+                                " to a common type"
+                                % (", ".join("'%s'" % domain.family
+                                             for domain in domains)),
+                                self.syntax.mark)
+            else:
+                raise BindError("a scalar value is expected",
+                                consequents[0].mark
+                                if consequents else alternative.mark)
         consequents = [CastBinding(consequent, domain, consequent.syntax)
                        for consequent in consequents]
         if alternative is not None:
     def match(self):
         operands = list(reversed(self.syntax.arguments))
         if len(operands) < 3:
-            raise BindError("not enough arguments", self.syntax.mark)
+            raise BindError("function '%s' expects 3 or more arguments;"
+                            " got %s" % (self.name.encode('utf-8'),
+                                         len(operands)), self.syntax.mark)
         variable = None
         variants = []
         consequents = []
         domains = [variable.domain] + [variant.domain for variant in variants]
         domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            raise BindError("cannot coerce values of types (%s)"
+                            " to a common type"
+                            % (", ".join("'%s'" % domain.family
+                                         for domain in domains)),
+                            self.syntax.mark)
         variable = CastBinding(variable, domain, variable.syntax)
         variants = [CastBinding(variant, domain, variant.syntax)
                     for variant in variants]
             domains.append(alternative.domain)
         domain = coerce(*domains)
         if domain is None:
-            raise BindError("incompatible arguments", self.syntax.mark)
+            if len(domains) > 1:
+                raise BindError("cannot coerce values of types (%s)"
+                                " to a common type"
+                                % (", ".join("'%s'" % domain.family
+                                             for domain in domains)),
+                                self.syntax.mark)
+            else:
+                raise BindError("a scalar value is expected",
+                                consequents[0].mark
+                                if consequents else alternative.mark)
         consequents = [CastBinding(consequent, domain, consequent.syntax)
                        for consequent in consequents]
         if alternative is not None:
         plural_base = None
         if recipies is not None:
             if len(recipies) != 1:
-                raise BindError("one operand is expected", op.mark)
+                raise BindError("function '%s' expects 1 argument; got %s"
+                                % (self.name.encode('utf-8'), len(recipies)),
+                                op.mark)
             plural_base = op
             syntax, recipe = recipies[0]
-            bind = BindByRecipe(recipe, syntax, self.state)
-            op = bind()
+            op = self.state.use(recipe, syntax)
         op = CastBinding(op, coerce(BooleanDomain()), op.syntax)
         return FormulaBinding(self.state.scope,
                               QuantifySig(self.polarity), op.domain,
         plural_base = None
         if recipies is not None:
             if len(recipies) != 1:
-                raise BindError("one operand is expected", op.mark)
+                raise BindError("function '%s' expects 1 argument; got %s"
+                                % (self.name.encode('utf-8'), len(recipies)),
+                                op.mark)
             plural_base = op
             syntax, recipe = recipies[0]
-            bind = BindByRecipe(recipe, syntax, self.state)
-            op = bind()
+            op = self.state.use(recipe, syntax)
         op = CastBinding(op, coerce(BooleanDomain()), op.syntax)
         op = FormulaBinding(self.state.scope,
                             CountSig(), coerce(IntegerDomain()),
         plural_base = None
         if recipies is not None:
             if len(recipies) != 1:
-                raise BindError("one operand is expected", op.mark)
+                raise BindError("function '%s' expects 1 argument; got %s"
+                                % (self.name.encode('utf-8'), len(recipies)),
+                                op.mark)
             plural_base = op
             syntax, recipe = recipies[0]
-            bind = BindByRecipe(recipe, syntax, self.state)
-            op = bind()
+            op = self.state.use(recipe, syntax)
         binding = FormulaBinding(self.state.scope,
                                  self.signature(), self.codomain, self.syntax,
                                  op=op)
         plural_base = None
         if recipies is not None:
             if len(recipies) != 1:
-                raise BindError("one operand is expected", op.mark)
+                raise BindError("function '%s' expects 1 argument; got %s"
+                                % (self.name.encode('utf-8'), len(recipies)),
+                                op.mark)
             plural_base = op
             syntax, recipe = recipies[0]
-            bind = BindByRecipe(recipe, syntax, self.state)
-            op = bind()
+            op = self.state.use(recipe, syntax)
         binding = FormulaBinding(self.state.scope,
                                  self.signature(self.polarity), self.codomain,
                                  self.syntax, op=op)

src/htsql_tweak/inet/domain.py

 
 class INetDomain(Domain):
 
+    family = 'inet'
+
     def parse(self, data):
         assert isinstance(data, maybe(unicode))
         if data is None:

test/input/error.yaml

   - uri: /program{code, count(student_by_year(2010))}
     expect: 400
 
+- title: Function Bind Errors
+  tests:
+  # invalid number of arguments
+  - uri: /count()
+    expect: 400
+  - uri: /today(1)
+    expect: 400
+  - uri: /slice()
+    expect: 400
+  - uri: /slice('HTSQL',1,3,5)
+    expect: 400
+  - uri: /school.sort()
+    expect: 400
+  - uri: /'SQL'={}
+    expect: 400
+  - uri: /if(school)
+    expect: 400
+  - uri: /switch(school,department)
+    expect: 400
+  # invalid coersion
+  - uri: /department{code, school :if_null 'none'}
+    expect: 400
+  - uri: /department{code, is_null(school)}
+    expect: 400
+  - uri: /school?code=today()
+    expect: 400
+  - uri: /school?code==today()
+    expect: 400
+  - uri: /school?code={'art',5,today()}
+    expect: 400
+  - uri: /school?code<today()
+    expect: 400
+  - uri: /department{code, if(school,school.code,today())}
+    expect: 400
+  - uri: /department{code, if(school.code,school)}
+    expect: 400
+  - uri: /department{code, switch(school.code,today(),now())}
+    expect: 400
+  - uri: /department{code, switch(school.code,'art',today(),now())}
+    expect: 400
+  - uri: /department{code, switch(school.code,'art',school)}
+    expect: 400
+  # invalid comparison
+  - uri: /student?is_active<true()
+    expect: 400
+  # invalid correlation
+  - uri: /trunc(school)
+    expect: 400
+  - uri: /trunc(school,department)
+    expect: 400
+  - uri: /-school
+    expect: 400
+  - uri: /school*department
+    expect: 400
+  # invalid special arguments
+  - uri: /retrieve(school)
+    expect: 400
+  - uri: /html(school)
+    expect: 400
+  - uri: /distinct(department)
+    expect: 400
+  - uri: /distinct(department{school})
+    expect: 400
+  - uri: /school.limit(today())
+    expect: 400
+  - uri: /school.limit(1,-1)
+    expect: 400
+  - uri: /department.sort(school)
+    expect: 400
+  - uri: /define(today()){now()}
+    expect: 400
+  - uri: /today() :where now()
+    expect: 400
+  # invalid aggregate expansion
+  - uri: /exists(school{code,name})
+    expect: 400
+  - uri: /count(school{code,name})
+    expect: 400
+  - uri: /avg(school{code,name})
+    expect: 400
+  - uri: /max(school{code,name})
+    expect: 400
 
+

test/output/mssql.yaml

           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('boolean', 'integer') to a common type:
                 /{true()=1}
                   ^^^^^^^^
         - uri: /{'cinq'!=4.9}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{date('2010-04-15')==1991}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{date('2010-04-15')=datetime('2010-04-15')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'datetime') to a common type:
                 /{date('2010-04-15')=datetime('2010-04-15')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{datetime('2010-04-15 20:13')!==time('02:01')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{datetime('2010-04-15 20:13')!==time('02:01')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{1<10, 7.0<=7.0, 'omega'>'alpha'}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: uncomparable arguments:
+            bind error: values of type 'boolean' are not comparable:
                 /{false()<true()}
                   ^^^^^^^^^^^^^^
         - uri: /{'cinq'>4.9}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{date('2010-04-15')>=1991}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{datetime('2010-04-15 20:13')>time('02:01')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{datetime('2010-04-15 20:13')>time('02:01')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{if_null('Victor', 'William'), if_null(null(), 'William'), if_null('Victor',
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{if_null(date('2010-04-15'),1991)}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{null_if(datetime('2010-04-15 20:13'),time('02:01'))}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{null_if(datetime('2010-04-15 20:13'),time('02:01'))}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{if(true(), 'then'), if(false(), 'then'), if(null(), 'then')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('integer', 'date', 'date') to a common type:
                 /{switch(1, date('2010-04-15'), 1, date('1991-08-20'), 2)}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{switch(1, 1, 'George', 2, false())}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '+' cannot be applied to values of types ('untyped', 'integer'):
                 /{'cinq'+7}
                   ^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('string', 'string'))
         - uri: /{2.125-date('2010-04-15')}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '-' cannot be applied to values of types ('decimal', 'date'):
                 /{2.125-date('2010-04-15')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('date', 'date'))
         - uri: /{true()/271828e-5}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '/' cannot be applied to values of types ('boolean', 'float'):
                 /{true()/271828e-5}
                   ^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'))
         - uri: /{7*2147483647}
           status: 409 Conflict
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'round' cannot be applied to values of types ('float', 'integer'):
                 /{round(271828e-5,2)}
                   ^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
         - uri: /{trunc(271828e-5,2)}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'trunc' cannot be applied to values of types ('float', 'integer'):
                 /{trunc(271828e-5,2)}
                   ^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
       - id: string-functions-and-operators
         tests:
         - uri: /{string(null()), string('OMGWTFBBQ')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'length' cannot be applied to a value of type 'date':
                 /{length(date(2010-04-15))}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid type: 'string')
         - uri: /{'OMG'+'WTF'+'BBQ'}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '+' cannot be applied to values of types ('string', 'integer'):
                 /{string('LOL')+7}
                   ^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('string', 'string'))
         - uri: /{'OMGWTFBBQ'~'wtf', 'OMGWTFBBQ'!~'LOL'}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'min' cannot be applied to a value of type 'boolean':
                 /{min(student.is_active), max(student.is_active)}
                   ^^^^^^^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float', 'string', 'date', 'time', 'datetime')
         - uri: /{sum(course{credits}?department_code='be'), count(course{credits}?department_code='be'),
             avg(course{credits}?department_code='be')}
           status: 200 OK
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'sum' cannot be applied to a value of type 'datetime':
                 /{sum(student.dob)}
                   ^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
         - uri: /{avg(student.name)}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'avg' cannot be applied to a value of type 'string':
                 /{avg(student.name)}
                   ^^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
       - id: table-functions-and-operators
         tests:
         - uri: /school.program
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: missing argument op:
+            bind error: function 'retrieve' expects 1 argument; got 0:
                 /retrieve()
                  ^^^^^^^^^^
         - uri: /retrieve(school)
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: a segment is required:
+            bind error: function 'retrieve' expects a segment argument:
                 /retrieve(school)
                           ^^^^^^
         - uri: /school/:html/:retrieve
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: a segment is required:
+            bind error: function 'retrieve' expects a segment argument:
                 /school/:html/:retrieve
                 ^^^^^^^^^^^^^
         - uri: /school/:html/:json
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: missing argument op:
+            bind error: function 'meta' expects 1 argument; got 0:
                 /meta()
                  ^^^^^^
         - uri: /meta(/column,/link)
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: unexpected argument:
+            bind error: function 'meta' expects 1 argument; got 2:
                 /meta(/column,/link)
-                              ^^^^^
+                 ^^^^^^^^^^^^^^^^^^^
         - uri: /meta(/schema)
           status: 400 Bad Request
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: output column must be a scalar value:
+            bind error: output column must be scalar:
                 /department{school, code}
                             ^^^^^^
         - uri: /school{code, num_dept():=count(department)}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: quotient column must be a scalar value:
+            bind error: quotient column must be scalar:
                 /department^school
                             ^^^^^^
         - uri: /department^(num_course:=count(course))
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: quotient column must be a scalar value:
+            bind error: quotient column must be scalar:
                 /department^(num_course:=count(course))
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /program.{school_code,code}->school{code}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: cannot convert origin and target columns to a common type:
+            bind error: cannot coerce origin and target columns to a common type:
                 /program.{code}->course{no}
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /define((school).num_prog:=count(program)).true()
             bind error: unrecognized function 'student_by_year':
                 /program{code, count(student_by_year(2010))}
                                      ^^^^^^^^^^^^^^^^^^^^^
+      - id: function-bind-errors
+        tests:
+        - uri: /count()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'count' expects 1 argument; got 0:
+                /count()
+                 ^^^^^^^
+        - uri: /today(1)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'today' expects 0 arguments; got 1:
+                /today(1)
+                 ^^^^^^^^
+        - uri: /slice()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'slice' expects 1 to 3 arguments; got 0:
+                /slice()
+                 ^^^^^^^
+        - uri: /slice('HTSQL',1,3,5)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'slice' expects 1 to 3 arguments; got 4:
+                /slice('HTSQL',1,3,5)
+                 ^^^^^^^^^^^^^^^^^^^^
+        - uri: /school.sort()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'sort' expects 1 or more arguments; got 0:
+                /school.sort()
+                        ^^^^^^
+        - uri: /'SQL'={}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: at least one element is expected:
+                /'SQL'={}
+                       ^^
+        - uri: /if(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'if' expects 2 or more arguments; got 1:
+                /if(school)
+                 ^^^^^^^^^^
+        - uri: /switch(school,department)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'switch' expects 3 or more arguments; got 2:
+                /switch(school,department)
+                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, school :if_null 'none'}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('tuple', 'untyped') to a common type:
+                /department{code, school :if_null 'none'}
+                                  ^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, is_null(school)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: a scalar value is expected:
+                /department{code, is_null(school)}
+                                  ^^^^^^^^^^^^^^^
+        - uri: /school?code=today()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /school?code=today()
+                        ^^^^^^^^^^^^
+        - uri: /school?code==today()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /school?code==today()
+                        ^^^^^^^^^^^^^
+        - uri: /school?code={'art',5,today()}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'untyped', 'integer', 'date') to a common type:
+                /school?code={'art',5,today()}
+                        ^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /school?code<today()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /school?code<today()
+                        ^^^^^^^^^^^^
+        - uri: /department{code, if(school,school.code,today())}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /department{code, if(school,school.code,today())}
+                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, if(school.code,school)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: a scalar value is expected:
+                /department{code, if(school.code,school)}
+                                                 ^^^^^^
+        - uri: /department{code, switch(school.code,today(),now())}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /department{code, switch(school.code,today(),now())}
+                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, switch(school.code,'art',today(),now())}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('date', 'datetime') to a common type:
+                /department{code, switch(school.code,'art',today(),now())}
+                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, switch(school.code,'art',school)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: a scalar value is expected:
+                /department{code, switch(school.code,'art',school)}
+                                                           ^^^^^^
+        - uri: /student?is_active<true()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: values of type 'boolean' are not comparable:
+                /student?is_active<true()
+                         ^^^^^^^^^^^^^^^^
+        - uri: /trunc(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'trunc' cannot be applied to a value of type 'tuple':
+                /trunc(school)
+                 ^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
+        - uri: /trunc(school,department)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'trunc' cannot be applied to values of types ('tuple', 'tuple'):
+                /trunc(school,department)
+                 ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
+        - uri: /-school
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: operator '-' cannot be applied to a value of type 'tuple':
+                /-school
+                 ^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
+        - uri: /school*department
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: operator '*' cannot be applied to values of types ('tuple', 'tuple'):
+                /school*department
+                 ^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'))
+        - uri: /retrieve(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'retrieve' expects a segment argument:
+                /retrieve(school)
+                          ^^^^^^
+        - uri: /html(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'html' expects a segment argument:
+                /html(school)
+                      ^^^^^^
+        - uri: /distinct(department)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'distinct' expects an argument with a selector:
+                /distinct(department)
+                          ^^^^^^^^^^
+        - uri: /distinct(department{school})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: quotient column must be scalar:
+                /distinct(department{school})
+                                     ^^^^^^
+        - uri: /school.limit(today())
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'limit' expects a non-negative integer:
+                /school.limit(today())
+                              ^^^^^^^
+        - uri: /school.limit(1,-1)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'limit' expects a non-negative integer:
+                /school.limit(1,-1)
+                                ^^
+        - uri: /department.sort(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'sort' expects a scalar expression:
+                /department.sort(school)
+                                 ^^^^^^
+        - uri: /define(today()){now()}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'define' expects an assignment expression:
+                /define(today()){now()}
+                        ^^^^^^^
+        - uri: /today() :where now()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'where' expects an assignment expression:
+                /today() :where now()
+                                ^^^^^
+        - uri: /exists(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'exists' expects 1 argument; got 2:
+                /exists(school{code,name})
+                        ^^^^^^^^^^^^^^^^^
+        - uri: /count(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'count' expects 1 argument; got 2:
+                /count(school{code,name})
+                       ^^^^^^^^^^^^^^^^^
+        - uri: /avg(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'avg' expects 1 argument; got 2:
+                /avg(school{code,name})
+                     ^^^^^^^^^^^^^^^^^
+        - uri: /max(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'max' expects 1 argument; got 2:
+                /max(school{code,name})
+                     ^^^^^^^^^^^^^^^^^

test/output/mysql.yaml

           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('boolean', 'integer') to a common type:
                 /{true()=1}
                   ^^^^^^^^
         - uri: /{'cinq'!=4.9}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{date('2010-04-15')==1991}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{date('2010-04-15')=datetime('2010-04-15')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'datetime') to a common type:
                 /{date('2010-04-15')=datetime('2010-04-15')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{datetime('2010-04-15 20:13')!==time('02:01')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{datetime('2010-04-15 20:13')!==time('02:01')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{1<10, 7.0<=7.0, 'omega'>'alpha'}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: uncomparable arguments:
+            bind error: values of type 'boolean' are not comparable:
                 /{false()<true()}
                   ^^^^^^^^^^^^^^
         - uri: /{'cinq'>4.9}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{date('2010-04-15')>=1991}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{datetime('2010-04-15 20:13')>time('02:01')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{datetime('2010-04-15 20:13')>time('02:01')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{if_null('Victor', 'William'), if_null(null(), 'William'), if_null('Victor',
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{if_null(date('2010-04-15'),1991)}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{null_if(datetime('2010-04-15 20:13'),time('02:01'))}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{null_if(datetime('2010-04-15 20:13'),time('02:01'))}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{if(true(), 'then'), if(false(), 'then'), if(null(), 'then')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('integer', 'date', 'date') to a common type:
                 /{switch(1, date('2010-04-15'), 1, date('1991-08-20'), 2)}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{switch(1, 1, 'George', 2, false())}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '+' cannot be applied to values of types ('untyped', 'integer'):
                 /{'cinq'+7}
                   ^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('string', 'string'))
         - uri: /{2.125-date('2010-04-15')}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '-' cannot be applied to values of types ('decimal', 'date'):
                 /{2.125-date('2010-04-15')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('date', 'date'))
         - uri: /{true()/271828e-5}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '/' cannot be applied to values of types ('boolean', 'float'):
                 /{true()/271828e-5}
                   ^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'))
         - uri: /{7*2147483647, 9223372036854775807+1}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'round' cannot be applied to values of types ('float', 'integer'):
                 /{round(271828e-5,2)}
                   ^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
         - uri: /{trunc(271828e-5,2)}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'trunc' cannot be applied to values of types ('float', 'integer'):
                 /{trunc(271828e-5,2)}
                   ^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
       - id: string-functions-and-operators
         tests:
         - uri: /{string(null()), string('OMGWTFBBQ')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'length' cannot be applied to a value of type 'date':
                 /{length(date(2010-04-15))}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid type: 'string')
         - uri: /{'OMG'+'WTF'+'BBQ'}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '+' cannot be applied to values of types ('string', 'integer'):
                 /{string('LOL')+7}
                   ^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('string', 'string'))
         - uri: /{'OMGWTFBBQ'~'wtf', 'OMGWTFBBQ'!~'LOL'}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'min' cannot be applied to a value of type 'boolean':
                 /{min(student.is_active), max(student.is_active)}
                   ^^^^^^^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float', 'string', 'date', 'time', 'datetime')
         - uri: /{sum(course{credits}?department_code='be'), count(course{credits}?department_code='be'),
             avg(course{credits}?department_code='be')}
           status: 200 OK
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'sum' cannot be applied to a value of type 'date':
                 /{sum(student.dob)}
                   ^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
         - uri: /{avg(student.name)}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'avg' cannot be applied to a value of type 'string':
                 /{avg(student.name)}
                   ^^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
       - id: table-functions-and-operators
         tests:
         - uri: /school.program
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: missing argument op:
+            bind error: function 'retrieve' expects 1 argument; got 0:
                 /retrieve()
                  ^^^^^^^^^^
         - uri: /retrieve(school)
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: a segment is required:
+            bind error: function 'retrieve' expects a segment argument:
                 /retrieve(school)
                           ^^^^^^
         - uri: /school/:html/:retrieve
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: a segment is required:
+            bind error: function 'retrieve' expects a segment argument:
                 /school/:html/:retrieve
                 ^^^^^^^^^^^^^
         - uri: /school/:html/:json
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: missing argument op:
+            bind error: function 'meta' expects 1 argument; got 0:
                 /meta()
                  ^^^^^^
         - uri: /meta(/column,/link)
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: unexpected argument:
+            bind error: function 'meta' expects 1 argument; got 2:
                 /meta(/column,/link)
-                              ^^^^^
+                 ^^^^^^^^^^^^^^^^^^^
         - uri: /meta(/schema)
           status: 400 Bad Request
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: output column must be a scalar value:
+            bind error: output column must be scalar:
                 /department{school, code}
                             ^^^^^^
         - uri: /school{code, num_dept():=count(department)}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: quotient column must be a scalar value:
+            bind error: quotient column must be scalar:
                 /department^school
                             ^^^^^^
         - uri: /department^(num_course:=count(course))
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: quotient column must be a scalar value:
+            bind error: quotient column must be scalar:
                 /department^(num_course:=count(course))
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /program.{school_code,code}->school{code}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: cannot convert origin and target columns to a common type:
+            bind error: cannot coerce origin and target columns to a common type:
                 /program.{code}->course{no}
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /define((school).num_prog:=count(program)).true()
             bind error: unrecognized function 'student_by_year':
                 /program{code, count(student_by_year(2010))}
                                      ^^^^^^^^^^^^^^^^^^^^^
+      - id: function-bind-errors
+        tests:
+        - uri: /count()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'count' expects 1 argument; got 0:
+                /count()
+                 ^^^^^^^
+        - uri: /today(1)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'today' expects 0 arguments; got 1:
+                /today(1)
+                 ^^^^^^^^
+        - uri: /slice()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'slice' expects 1 to 3 arguments; got 0:
+                /slice()
+                 ^^^^^^^
+        - uri: /slice('HTSQL',1,3,5)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'slice' expects 1 to 3 arguments; got 4:
+                /slice('HTSQL',1,3,5)
+                 ^^^^^^^^^^^^^^^^^^^^
+        - uri: /school.sort()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'sort' expects 1 or more arguments; got 0:
+                /school.sort()
+                        ^^^^^^
+        - uri: /'SQL'={}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: at least one element is expected:
+                /'SQL'={}
+                       ^^
+        - uri: /if(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'if' expects 2 or more arguments; got 1:
+                /if(school)
+                 ^^^^^^^^^^
+        - uri: /switch(school,department)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'switch' expects 3 or more arguments; got 2:
+                /switch(school,department)
+                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, school :if_null 'none'}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('tuple', 'untyped') to a common type:
+                /department{code, school :if_null 'none'}
+                                  ^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, is_null(school)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: a scalar value is expected:
+                /department{code, is_null(school)}
+                                  ^^^^^^^^^^^^^^^
+        - uri: /school?code=today()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /school?code=today()
+                        ^^^^^^^^^^^^
+        - uri: /school?code==today()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /school?code==today()
+                        ^^^^^^^^^^^^^
+        - uri: /school?code={'art',5,today()}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'untyped', 'integer', 'date') to a common type:
+                /school?code={'art',5,today()}
+                        ^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /school?code<today()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /school?code<today()
+                        ^^^^^^^^^^^^
+        - uri: /department{code, if(school,school.code,today())}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /department{code, if(school,school.code,today())}
+                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, if(school.code,school)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: a scalar value is expected:
+                /department{code, if(school.code,school)}
+                                                 ^^^^^^
+        - uri: /department{code, switch(school.code,today(),now())}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('string', 'date') to a common type:
+                /department{code, switch(school.code,today(),now())}
+                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, switch(school.code,'art',today(),now())}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: cannot coerce values of types ('date', 'datetime') to a common type:
+                /department{code, switch(school.code,'art',today(),now())}
+                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+        - uri: /department{code, switch(school.code,'art',school)}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: a scalar value is expected:
+                /department{code, switch(school.code,'art',school)}
+                                                           ^^^^^^
+        - uri: /student?is_active<true()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: values of type 'boolean' are not comparable:
+                /student?is_active<true()
+                         ^^^^^^^^^^^^^^^^
+        - uri: /trunc(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'trunc' cannot be applied to a value of type 'tuple':
+                /trunc(school)
+                 ^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
+        - uri: /trunc(school,department)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'trunc' cannot be applied to values of types ('tuple', 'tuple'):
+                /trunc(school,department)
+                 ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
+        - uri: /-school
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: operator '-' cannot be applied to a value of type 'tuple':
+                /-school
+                 ^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
+        - uri: /school*department
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: operator '*' cannot be applied to values of types ('tuple', 'tuple'):
+                /school*department
+                 ^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'))
+        - uri: /retrieve(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'retrieve' expects a segment argument:
+                /retrieve(school)
+                          ^^^^^^
+        - uri: /html(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'html' expects a segment argument:
+                /html(school)
+                      ^^^^^^
+        - uri: /distinct(department)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'distinct' expects an argument with a selector:
+                /distinct(department)
+                          ^^^^^^^^^^
+        - uri: /distinct(department{school})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: quotient column must be scalar:
+                /distinct(department{school})
+                                     ^^^^^^
+        - uri: /school.limit(today())
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'limit' expects a non-negative integer:
+                /school.limit(today())
+                              ^^^^^^^
+        - uri: /school.limit(1,-1)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'limit' expects a non-negative integer:
+                /school.limit(1,-1)
+                                ^^
+        - uri: /department.sort(school)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'sort' expects a scalar expression:
+                /department.sort(school)
+                                 ^^^^^^
+        - uri: /define(today()){now()}
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'define' expects an assignment expression:
+                /define(today()){now()}
+                        ^^^^^^^
+        - uri: /today() :where now()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'where' expects an assignment expression:
+                /today() :where now()
+                                ^^^^^
+        - uri: /exists(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'exists' expects 1 argument; got 2:
+                /exists(school{code,name})
+                        ^^^^^^^^^^^^^^^^^
+        - uri: /count(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'count' expects 1 argument; got 2:
+                /count(school{code,name})
+                       ^^^^^^^^^^^^^^^^^
+        - uri: /avg(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'avg' expects 1 argument; got 2:
+                /avg(school{code,name})
+                     ^^^^^^^^^^^^^^^^^
+        - uri: /max(school{code,name})
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'max' expects 1 argument; got 2:
+                /max(school{code,name})
+                     ^^^^^^^^^^^^^^^^^

test/output/oracle.yaml

           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('boolean', 'integer') to a common type:
                 /{true()=1}
                   ^^^^^^^^
         - uri: /{'cinq'!=4.9}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{date('2010-04-15')==1991}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{date('2010-04-15')=datetime('2010-04-15')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'datetime') to a common type:
                 /{date('2010-04-15')=datetime('2010-04-15')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{datetime('2010-04-15 20:13')!==time('02:01')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{datetime('2010-04-15 20:13')!==time('02:01')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{1<10, 7.0<=7.0, 'omega'>'alpha'}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: uncomparable arguments:
+            bind error: values of type 'boolean' are not comparable:
                 /{false()<true()}
                   ^^^^^^^^^^^^^^
         - uri: /{'cinq'>4.9}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{date('2010-04-15')>=1991}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{datetime('2010-04-15 20:13')>time('02:01')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{datetime('2010-04-15 20:13')>time('02:01')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{if_null('Victor', 'William'), if_null(null(), 'William'), if_null('Victor',
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('date', 'integer') to a common type:
                 /{if_null(date('2010-04-15'),1991)}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{null_if(datetime('2010-04-15 20:13'),time('02:01'))}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('datetime', 'time') to a common type:
                 /{null_if(datetime('2010-04-15 20:13'),time('02:01'))}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{if(true(), 'then'), if(false(), 'then'), if(null(), 'then')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: cannot coerce values of types ('integer', 'date', 'date') to a common type:
                 /{switch(1, date('2010-04-15'), 1, date('1991-08-20'), 2)}
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /{switch(1, 1, 'George', 2, false())}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '+' cannot be applied to values of types ('untyped', 'integer'):
                 /{'cinq'+7}
                   ^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('string', 'string'))
         - uri: /{2.125-date('2010-04-15')}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '-' cannot be applied to values of types ('decimal', 'date'):
                 /{2.125-date('2010-04-15')}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('date', 'date'))
         - uri: /{true()/271828e-5}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '/' cannot be applied to values of types ('boolean', 'float'):
                 /{true()/271828e-5}
                   ^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'))
         - uri: /{7*2147483647, 9223372036854775807+1}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'round' cannot be applied to values of types ('float', 'integer'):
                 /{round(271828e-5,2)}
                   ^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
         - uri: /{trunc(271828e-5,2)}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'trunc' cannot be applied to values of types ('float', 'integer'):
                 /{trunc(271828e-5,2)}
                   ^^^^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('decimal', 'integer'))
       - id: string-functions-and-operators
         tests:
         - uri: /{string(null()), string('OMGWTFBBQ')}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'length' cannot be applied to a value of type 'date':
                 /{length(date(2010-04-15))}
                   ^^^^^^^^^^^^^^^^^^^^^^^^
+            (valid type: 'string')
         - uri: /{'OMG'+'WTF'+'BBQ'}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: operator '+' cannot be applied to values of types ('string', 'integer'):
                 /{string('LOL')+7}
                   ^^^^^^^^^^^^^^^
+            (valid types: ('integer', 'integer'), ('integer', 'decimal'), ('decimal', 'integer'), ('decimal', 'decimal'), ('integer', 'float'), ('decimal', 'float'), ('float', 'integer'), ('float', 'decimal'), ('float', 'float'), ('date', 'integer'), ('datetime', 'integer'), ('datetime', 'decimal'), ('datetime', 'float'), ('string', 'string'))
         - uri: /{'OMGWTFBBQ'~'wtf', 'OMGWTFBBQ'!~'LOL'}
           status: 200 OK
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'min' cannot be applied to a value of type 'boolean':
                 /{min(student.is_active), max(student.is_active)}
                   ^^^^^^^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float', 'string', 'date', 'time', 'datetime')
         - uri: /{sum(course{credits}?department_code='be'), count(course{credits}?department_code='be'),
             avg(course{credits}?department_code='be')}
           status: 200 OK
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'sum' cannot be applied to a value of type 'datetime':
                 /{sum(student.dob)}
                   ^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
         - uri: /{avg(student.name)}
           status: 400 Bad Request
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: incompatible arguments:
+            bind error: function 'avg' cannot be applied to a value of type 'string':
                 /{avg(student.name)}
                   ^^^^^^^^^^^^^^^^^
+            (valid type: 'integer', 'decimal', 'float')
       - id: table-functions-and-operators
         tests:
         - uri: /school.program
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: missing argument op:
+            bind error: function 'retrieve' expects 1 argument; got 0:
                 /retrieve()
                  ^^^^^^^^^^
         - uri: /retrieve(school)
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: a segment is required:
+            bind error: function 'retrieve' expects a segment argument:
                 /retrieve(school)
                           ^^^^^^
         - uri: /school/:html/:retrieve
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: a segment is required:
+            bind error: function 'retrieve' expects a segment argument:
                 /school/:html/:retrieve
                 ^^^^^^^^^^^^^
         - uri: /school/:html/:json
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: missing argument op:
+            bind error: function 'meta' expects 1 argument; got 0:
                 /meta()
                  ^^^^^^
         - uri: /meta(/column,/link)
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: unexpected argument:
+            bind error: function 'meta' expects 1 argument; got 2:
                 /meta(/column,/link)
-                              ^^^^^
+                 ^^^^^^^^^^^^^^^^^^^
         - uri: /meta(/schema)
           status: 400 Bad Request
           headers:
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: output column must be a scalar value:
+            bind error: output column must be scalar:
                 /department{school, code}
                             ^^^^^^
         - uri: /school{code, num_dept():=count(department)}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: quotient column must be a scalar value:
+            bind error: quotient column must be scalar:
                 /department^school
                             ^^^^^^
         - uri: /department^(num_course:=count(course))
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: quotient column must be a scalar value:
+            bind error: quotient column must be scalar:
                 /department^(num_course:=count(course))
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /program.{school_code,code}->school{code}
           headers:
           - [Content-Type, text/plain; charset=UTF-8]
           body: |
-            bind error: cannot convert origin and target columns to a common type:
+            bind error: cannot coerce origin and target columns to a common type:
                 /program.{code}->course{no}
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
         - uri: /define((school).num_prog:=count(program)).true()
             bind error: unrecognized function 'student_by_year':
                 /program{code, count(student_by_year(2010))}
                                      ^^^^^^^^^^^^^^^^^^^^^
+      - id: function-bind-errors
+        tests:
+        - uri: /count()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'count' expects 1 argument; got 0:
+                /count()
+                 ^^^^^^^
+        - uri: /today(1)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'today' expects 0 arguments; got 1:
+                /today(1)
+                 ^^^^^^^^
+        - uri: /slice()
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'slice' expects 1 to 3 arguments; got 0:
+                /slice()
+                 ^^^^^^^
+        - uri: /slice('HTSQL',1,3,5)
+          status: 400 Bad Request
+          headers:
+          - [Content-Type, text/plain; charset=UTF-8]
+          body: |
+            bind error: function 'slice' expects 1 to 3 arguments; got 4:
+                /slice('HTSQL',1,3,5)
+                 ^^^^^^^^^^^^^^^^^^^^
+        - uri: /school.sort()
+          status: 400 Bad Request
+          headers: