Table of Contents
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
:
T
itself.
T
is a polymorphically mapped type (like
float
), then there is
abstract_mapper<
T
>
.
T
is a statically mapped type, then there
is some optional_mapper
,
tuple_mapper
or class_mapper
class.
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:
query<oscar> oscars_before(uint16_t cutoff) { return oscars.where(oscars->year < cutoff); } query<oscar> 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:
query<oscar> oscars_before(const exprn_mapper<uint16_t> &cutoff) { return oscars.where(oscars->year < cutoff); }
Or we can use a template:
template<typename Arg> query<oscar> oscars_before(const Arg &cutoff) { return oscars.where(oscars->year < cutoff); }
which leaves the type checking in the hands of quince's <
.