Chapter 6. Expressions for Server-Side Execution

Table of Contents

Server-Side Expressions in General
Arithmetic Operations
String Concatenation
Function Calls
Pre-wrapped functions
Scalar and Aggregate Functions
Writing Your Own Function Wrappers
Relational Operations
predicates and Logical Operations
Collective Comparisons (PostgreSQL only)
Scalar Subqueries

This chapter deals with one component of queries: the part that represents a server-side expression, i.e. an expression to be evaluated by the DBMS.

The simplest of these components are either a value of some mapped type, e.g. 3.14f, or one of the mapper objects that were described in the previous chapter, e.g. points->x.

The features described in this chapter let us build up from there. E.g. there is an infix +, which lets us build 3.14f + points->x. Then we can build on what we have built, and so on.

The simplest components come in a variety of C++ types, e.g. 3.14f is of type float, points->x is of type abstract_mapper<float>; but the components we build will always have the type exprn_mapper<T>, where T is the expression's result type -- i.e. the C++ equivalent of the SQL type to which the expression actually evaluates. So 3.14f + points->x has the type exprn_mapper<float>.

It would seem that, for any mapped type T, a wide range of C++ types can represent server-side expressions with result type T:

  • There is T itself.
  • If T is a polymorphically mapped type (like float), then there is abstract_mapper<T>.
  • If T is a statically mapped type, then there is some optional_mapper, tuple_mapper or class_mapper class.
  • There is exprn_mapper<T>.

Will this make our lives difficult (or at least repetitive) when we try to write code that deals with any server-side expression with result type T?

Fortunately we can shorten the list, by folding some of the cases into others. In the statically mapped case, the mapper class is always a subclass of abstract_mapper<T>. And, for the sake of shortening the list further, quince defines exprn_mapper<T> as a subclass of abstract_mapper<T>. So the types we need to consider separately are two:

  • T
  • abstract_mapper<T>

For example, if we want to write a function that takes as an argument any server-side expression with result type uint16_t, we need just two overloads:

oscars_before(uint16_t cutoff) {
    return oscars.where(oscars->year < cutoff);

oscars_before(const abstract_mapper<uint16_t> &cutoff) {
    return oscars.where(oscars->year < cutoff);

But maybe we don't want even that much repeated code. Then we have alternatives.

exprn_mapper<T> supports implicit conversions from T and from abstract_mapper<T>. So we can take advantage of those:

oscars_before(const exprn_mapper<uint16_t> &cutoff) {
    return oscars.where(oscars->year < cutoff);

Or we can use a template:

template<typename Arg>
oscars_before(const Arg &cutoff) {
    return oscars.where(oscars->year < cutoff);

which leaves the type checking in the hands of quince's <.