***************************
  Functions and Operators
***************************

.. contents:: Table of Contents
   :depth: 1
   :local:

This document describes built-in functions and operators.


Logical Functions and Operators
===============================

+----------------------+---------------------------+---------------------------+----------------------+
| Function             | Description               | Example Input             | Output               |
+======================+===========================+===========================+======================+
| `boolean(x)`         | cast *x* to Boolean       | ``boolean('true')``       | ``true``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``boolean('false')``      | ``false``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | |boolean-from-string-in|  | ``true``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``boolean(string(''))``   | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `true()`             | logical *TRUE* value      | ``true()``                |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `false()`            | logical *FALSE* value     | ``false()``               |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `p & q`              | logical *AND* operator    | ``true()&true()``         | ``true``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``true()&false()``        | ``false``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``false()&false()``       | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `p | q`              | logical *OR* operator     | ``true()|true()``         | ``true``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``true()|false()``        | ``true``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``false()|false()``       | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `\!p`                | logical *NOT* operator    | ``!true()``               | ``false``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``!false()``              | ``true``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `null(x)`            | *NULL* value              | ``null()``                |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `is_null(x)`         | *x* is null               | ``is_null(null())``       | ``true``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `if_null(x,y)`       | *x* if *x* is not null;   | ``if_null(1,0)``          | ``1``                |
|                      | *y* otherwise             +---------------------------+----------------------+
|                      |                           | ``if_null(null(),0)``     | ``0``                |
+----------------------+---------------------------+---------------------------+----------------------+
| `null_if(x,y)`       | *x* if *x* is not equal   | ``null_if(1,0)``          | ``1``                |
|                      | to *y*; null otherwise    +---------------------------+----------------------+
|                      |                           | ``null_if(0,0)``          | ``null``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `x = y`              | *x* is equal to *y*       | ``'HTSQL'='QUEL'``        | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `x != y`             | *x* is not equal to *y*   | ``'HTSQL'!='QUEL'``       | ``true``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `x == y`             | *x* is equal to *y*;      | ``'HTSQL'=='QUEL'``       | ``false``            |
|                      | treats nulls as regular   +---------------------------+----------------------+
|                      | values                    | ``2==null()``             | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `x !== y`            | *x* is not equal to *y*;  | ``'HTSQL'!=='QUEL'``      | ``true``             |
|                      | treats nulls as regular   +---------------------------+----------------------+
|                      | values                    | ``2!==null()``            | ``true``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `x = {a,b,c,...}`    | *x* is among *a*, *b*,    | ``5={2,3,5,7}'``          | ``true``             |
|                      | *c*, ...                  |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `x != {a,b,c,...}`   | *x* is not among *a*,     | ``5!={2,3,5,7}'``         | ``false``            |
|                      | *b*, *c*, ...             |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `x < y`              | *x* is less than *y*      | ``1<10``                  | ``true``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``'omega'<'alpha'``       | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `x <= y`             | *x* is less than or equal | ``1<=10``                 | ``true``             |
|                      | to *y*                    +---------------------------+----------------------+
|                      |                           | ``'omega'<='alpha'``      | ``false``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `x > y`              | *x* is greater than *y*   | ``1>10``                  | ``false``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``'omega'>'alpha'``       | ``true``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `x >= y`             | *x* is greater than or    | ``1>=10``                 | ``false``            |
|                      | equal to *y*              +---------------------------+----------------------+
|                      |                           | ``'omega'>='alpha'``      | ``true``             |
+----------------------+---------------------------+---------------------------+----------------------+
| |if-fn|              | first *ck* such that *pk* | |if-true-in|              | ``'up'``             |
+----------------------+ is *TRUE*; *o* or null    +---------------------------+----------------------+
| |if-else-fn|         | otherwise                 | |if-false-in|             | ``'down'``           |
+----------------------+---------------------------+---------------------------+----------------------+
| |switch-fn|          | first *ck* such that *x*  | |switch-1-in|             | ``'up'``             |
+----------------------+ is equal to *yk*; *o* or  +---------------------------+----------------------+
| |switch-else-fn|     | null otherwise            | |switch-0-in|             | ``'down'``           |
+----------------------+---------------------------+---------------------------+----------------------+

.. |boolean-from-string-in| replace:: ``boolean(string('HTSQL'))``
.. |if-fn| replace:: `if(p1,c1,...,pn,cn)`
.. |if-else-fn| replace:: `if(p1,c1,...,pn,cn,o)`
.. |if-true-in| replace:: ``if(true(),'up','down')``
.. |if-false-in| replace:: ``if(false(),'up','down')``
.. |switch-fn| replace:: `switch(x,y1,c1,...,yn,cn)`
.. |switch-else-fn| replace:: `switch(x,y1,c1,...,yn,cn,o)`
.. |switch-1-in| replace:: ``switch(1,1,'up',0,'down')``
.. |switch-0-in| replace:: ``switch(0,1,'up',0,'down')``

.. index:: boolean()

Boolean Cast
------------

`boolean(x)`
    Convert `x` to Boolean.

The result of the conversion depends on the type of the argument:

`untyped`
    The literal ``'false'`` is converted to *FALSE*, the literal
    ``'true'`` is converted to *TRUE*, any other literals generate an
    error.
`boolean`
    The value is unchanged.
`string`
    *NULL* and an empty string are converted to *FALSE*, other values
    are converted to *TRUE*.
other data types
    `null` values are converted to *FALSE*, other values are converted
    to *TRUE*.

.. htsql:: /{boolean('false'), boolean('true')}

.. htsql:: /{boolean(null()), boolean(false()), boolean(true())}

.. htsql:: /{boolean(string(null())), boolean(string('')),
             boolean(string('HTSQL'))}

.. htsql:: /{boolean(integer(null())), boolean(0.0),
             boolean(date('2010-04-15'))}

.. index:: true(), false()

Logical Values
--------------

`true()`
    Logical *TRUE* value.

`false()`
    Logical *FALSE* value.

.. htsql:: /{true(), false()}

.. index:: |, &, !

Logical Operators
-----------------

`p | q`
    Logical *OR* operator.

`p & q`
    Logical *AND* operator.

`\! p`
    Logical *NOT* operator.

Arguments of a logical operators that are not of a Boolean type
automatically converted to Boolean (see `boolean()` function).

.. htsql:: /{true()|true(), true()|false(),
             false()|true(), false()|false()}

.. htsql:: /{true()&true(), true()&false(),
             false()&true(), false()&false()}

.. htsql:: /{!true(), !false()}

.. htsql::

   /{true()&null(), false()&null(), null()&null(),
     true()|null(), false()|null(), null()|null(),
     !null()}

.. htsql:: /school?exists(program)&exists(department)|!campus
   :cut: 3

.. index:: null(), is_null(), if_null(), null_if()

NULL Checking
-------------

`null()`
    Untyped *NULL* value.
`is_null(x)`
    *TRUE* if `x` is *NULL*, *FALSE* otherwise.
`if_null(x,y)`
    `x` if `x` is not *NULL*, `y` otherwise.
`null_if(x,y)`
    `x` if `x` is not equal to `y`, *NULL* otherwise.

The arguments of `if_null()` and `null_if()` should be of the same type;
if not, the arguments are coerced to the most general type.

.. htsql:: /{null()}

.. htsql:: /{is_null(null()), is_null(0)}

.. htsql:: /{if_null('SQL','HTSQL'), if_null(null(),'HTSQL')}

.. htsql:: /{null_if('HTSQL','SQL'), null_if('SQL','SQL')}

.. htsql:: /course{title, credits}?is_null(credits)

.. htsql:: /course{title, credits}?(credits :if_null 0)=0

.. htsql:: /course{title, credits}?!(credits :null_if 0)

.. index:: =, !=, ==, !==

Equality Operators
------------------

`x = y`
    *TRUE* if `x` is equal to `y`, *FALSE* otherwise.  Returns *NULL* if
    any of the operands is *NULL*.
`x != y`
    *TRUE* if `x` is not equal to `y`, *FALSE* otherwise.  Returns
    *NULL* if any of the operands is *NULL*.
`x == y`
    *TRUE* if `x` is equal to `y`, *FALSE* otherwise.  Treats *NULL* as
    a regular value.
`x !== y`
    *TRUE* if `x` is not equal to `y`, *FALSE* otherwise.  Treats *NULL*
    as a regular value.
`x = {a,b,c,...}`
    *TRUE* if `x` is equal to *some* value among `a,b,c,...`, *FALSE*
    otherwise.
`x != {a,b,c,...}`
    *TRUE* if `x` is not equal to *all* values among `a,b,c,...`,
    *FALSE* otherwise.

The form `x = {a,b,c,...}` is a short-cut syntax for `x=a|x=b|x=c|...`.
Similarly, the form `x != {a,b,c,...}` is a short-cut syntax for
`x!=a|x!=b|x!=c|...`.

The operands of equality operators are expected to be of the same time.
If the types of the operands are different, the operands are coerced to
the most general type; it is an error if the operand types are not
compatible to each other.

.. htsql:: /{1=1.0, 'HTSQL'!='SQUARE'}

.. htsql:: /{0!=null(), null()=null(), 0!==null(), null()==null()}

.. htsql:: /'HTSQL'!={'ISBL','SQUARE','QUEL'}

.. htsql:: /school?campus='old'
   :cut: 3

.. htsql:: /school?campus!={'north','south'}
   :cut: 3

.. htsql:: /school{code, campus=='old', campus=='north', campus=='south'}
   :cut: 3

.. index:: <, <=, >, >=

Comparison Operators
--------------------

`x < y`
    *TRUE* if `x` is less than `y`, *FALSE* otherwise.
`x <= y`
    *TRUE* if `x` is less than or equal to `y`, *FALSE* otherwise.
`x > y`
    *TRUE* if `x` is greater than `y`, *FALSE* otherwise.
`x >= y`
    *TRUE* if `x` is greater than or equal to `y`, *FALSE* otherwise.

The result is *NULL* if any of the operands is *NULL*.

An operand of a comparison operator must be of a string, numeric,
enumeration, or date/time type.  Both operands are expected to be of
the same type; if not, the operands are coerced to the most general
type.

.. htsql:: /{23<=17.5, 'HTSQL'<'SQUARE',
             date('2010-04-15')>=date('1991-08-20')}

.. htsql:: /school?count(department)>=4
   :cut: 3

.. index:: if(), switch()

Branching Functions
-------------------

`if(p1,c1,p2,c2,...,pn,cn[,o])`
    This function takes *N* logical expressions `p1,p2,...,pN`
    interleaved with *N* values `c1,c2,...,cN`, followed by an optional
    value `o`.  The function returns the value `ck` corresponding to the
    first predicate `pk` evaluated to *TRUE*.  If none of the predicates
    are evaluated to *TRUE*, the value of `o` is returned, or *NULL* if
    `o` is not specified.
`switch(x,y1,c1,y2,c2,...,yn,cn[,o])`
    This function takes a control expression `x` followed by *N* variant
    values `y1,y2,...,yN` interleaved with *N* resulting values
    `c1,c2,...,cN`, and concluded with an optional default value `o`.
    The function returns the value `ck` corresponding to the first
    variant `yk` equal to `x`.  If none of the variants are equal to the
    control value, `o` is returned, or *NULL* if `o` is not specified.

These functions expect all the resulting values `c1,c2,...,cN` as well
as the default value `o` to be of the same type.  If the value types
are different, all values are coerced to the most general type.  Same
is true for the control expression `x` and variant values `y1,y2,...,yN`
of the function `switch()`.

.. htsql::
   :cut: 3

   /course{title, if(credits>=5, 'hard',
                     credits>=3, 'medium',
                                 'easy') :as level}
          ?department.code='astro'

.. htsql::
   :cut: 3

   /student{name, switch(gender, 'm', 1,
                                 'f', -1) :as sex_code}
           ?program.code='gedu'


Numeric Functions
=================

+----------------------+---------------------------+---------------------------+----------------------+
| Function             | Description               | Example Input             | Output               |
+======================+===========================+===========================+======================+
| `integer(x)`         | cast *x* to integer       | ``integer('60')``         | ``60``               |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``integer(17.25)``        | ``17``               |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``integer(string('60'))`` | ``60``               |
+----------------------+---------------------------+---------------------------+----------------------+
| `decimal(x)`         | cast *x* to decimal       | ``decimal('17.25')``      | ``17.25``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``decimal(223607e-5)``    | ``2.23607``          |
|                      |                           +---------------------------+----------------------+
|                      |                           | |decimal-from-string-in|  | ``17.25``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `float(x)`           | cast *x* to float         | ``float('223607e-5')``    | ``223607e-5``        |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``float(60)``             | ``6e1``              |
|                      |                           +---------------------------+----------------------+
|                      |                           | |float-from-string-in|    | ``223607e-5``        |
+----------------------+---------------------------+---------------------------+----------------------+
| `+ x`                | *x*                       | ``+60``                   |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `- x`                | negate *x*                | ``-7``                    |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `x + y`              | add *x* to *y*            | ``13+7``                  | ``20``               |
+----------------------+---------------------------+---------------------------+----------------------+
| `x - y`              | subtract *y* from *x*     | ``13-7``                  | ``6``                |
+----------------------+---------------------------+---------------------------+----------------------+
| `x * y`              | multiply *x* by *y*       | ``13*7``                  | ``91``               |
+----------------------+---------------------------+---------------------------+----------------------+
| `x / y`              | divide *x* by *y*         | ``13/7``                  | ``1.85714285714286`` |
+----------------------+---------------------------+---------------------------+----------------------+
| `round(x)`           | round *x* to the nearest  | ``round(17.25)``          | ``17``               |
|                      | integer                   |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `round(x,n)`         | round *x* to *n* decimal  | ``round(17.25,1)``        | ``17.3``             |
|                      | places                    |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `trunc(x)`           | round *x* to an integer,  | ``trunc(17.25)``          | ``17``               |
|                      | towards zero              |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `trunc(x,n)`         | round *x* to *n* decimal  | ``trunc(17.25,1)``        | ``17.2``             |
|                      | places, towards zero      |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+

.. |decimal-from-string-in| replace:: ``decimal(string('17.25'))``
.. |float-from-string-in| replace:: ``float(string('223607e-5'))``

.. index:: integer(), decimal(), float()

Numeric Cast
------------

`integer(x)`
    Convert `x` to integer.
`decimal(x)`
    Convert `x` to decimal.
`float(x)`
    Convert `x` to float.

The argument of a conversion function can be of one of the following
types:

*untyped*
    An untyped literal must be a valid number.  The `integer()` function
    accepts only integer literals, `decimal()` and `float()` accepts
    untyped literals written in integer, decimal or scientific notation.
*numeric*
    Numeric cast functions convert numbers between different storage
    forms.  Behavior on range overflow and rounding rules are
    backend-dependent.
*string*
    A string value must contain a valid number.  The set of allowed
    input values depends on the backend.

.. htsql:: /{integer(2.125), decimal('271828e-5'), float(string(60))}

.. index:: +, -, *, /

.. **

Arithmetic Expressions
----------------------

`+ x`
    Return `x`.
`- x`
    Negate `x`.
`x + y`
    Add `x` to `y`.
`x - y`
    Subtract `y` from `x`.
`x * y`
    Multiply `x` by `y`.
`x / y`
    Divide `x` by `y`.

Arithmetic operators expect operands of a numeric type.  If the operands
are of different types, they are coerced to the most general type, in
the order: *integer*, *decimal*, *float*.  For instance, adding an
integer value to a decimal value converts the integer operand to
decimal; multiplying a decimal value to a float value converts the
decimal operand to float.

In general, the type of the result coincides with the type of the
operands.  The only exception is the division operator: when applied to
integer operands, division produces a decimal value.

The behavior of arithmetic expressions on range overflow or division by
zero is backend-dependent: different backends may raise an error, return
a *NULL* value or generate an incorrect result.

Note that some arithmetic operators are also defined for *string*
and *date* values; they are described in respective sections.

.. htsql:: /{(2+4)*7, -(98-140), 21/5}

.. index:: round(), trunc()

Rounding Functions
------------------

`round(x)`
    Round `x` to the nearest integer value.
`round(x,n)`
    Round `x` to `n` decimal places.
`trunc(x)`
    Round `x` to an integer, towards zero.
`trunc(x,n)`
    Round `x` to `n` decimal places, towards zero.

If called with one argument, the functions accept values of *decimal* or
*float* types and return a value of the same type.

When called with two arguments, the functions expects a *decimal* argument
and produces a *decimal* value.  The second argument should be an integer;
negative values are permitted.

.. htsql:: /{round(3272.78125),
             round(3272.78125,2),
             round(3272.78125,-2)}

.. htsql:: /{trunc(3272.78125),
             trunc(3272.78125,2),
             trunc(3272.78125,-2)}

.. htsql:: /school{code, avg(department.count(course)) :round 2}
   :cut: 3

.. htsql::
   :cut: 3

   /department^avg_credits {avg_credits, count(department)}
    :where(department.avg_credits := avg(course.credits) :trunc(1))


String Functions
================

By convention, string functions take a string as its first parameter.
When an untyped literal, such as ``'value'`` is used and a string is
expected, it is automatically cast.  Hence, for convenience, we write
string typed values using single quotes in the output column.

+----------------------+---------------------------+---------------------------+----------------------+
| Function             | Description               | Example Input             | Output               |
+======================+===========================+===========================+======================+
| `string(x)`          | cast *x* to string        | ``string('Hello')``       | ``'Hello'``          |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``string(1.0)``           | ``'1.0'``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | |string-from-date-in|     | ``'2010-04-15'``     |
+----------------------+---------------------------+---------------------------+----------------------+
| `length(s)`          | number of characters      | ``length('HTSQL')``       | ``5``                |
|                      | in *s*                    |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `s + t`              | concatenate *s* and *t*   | ``'HT' + 'SQL'``          | ``'HTSQL'``          |
+----------------------+---------------------------+---------------------------+----------------------+
| `s ~ t`              | *s* contains *t*;         | ``'HTSQL' ~ 'sql'``       | ``true``             |
|                      | case-insensitive          |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `s !~ t`             | *s* does not contain      | ``'HTSQL' !~ 'sql'``      | ``false``            |
|                      | *t*; case-insensitive     |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `head(s)`            | first character of *s*    | ``head('HTSQL')``         | ``'H'``              |
+----------------------+---------------------------+---------------------------+----------------------+
| `head(s,n)`          | first *n* characters      | ``head('HTSQL',2)``       | ``'HT'``             |
|                      | of *s*                    +---------------------------+----------------------+
|                      |                           | ``head('HTSQL',-3)``      | ``'HT'``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `tail(s)`            | last character of *s*     | ``tail('HTSQL')``         | ``'L'``              |
+----------------------+---------------------------+---------------------------+----------------------+
| `tail(s,n)`          | last *n* characters       | ``tail('HTSQL',3)``       | ``'SQL'``            |
|                      | of *s*                    +---------------------------+----------------------+
|                      |                           | ``tail('HTSQL',-2)``      | ``'SQL'``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `slice(s,i,j)`       | *i*-th to *j*-th          | ``slice('HTSQL',1,4)``    | ``'TSQ'``            |
|                      | characters of *s*; null   +---------------------------+----------------------+
|                      | or missing index means    | ``slice('HTSQL',-4,-1)``  | ``'TSQ'``            |
|                      | the beginning or the end  +---------------------------+----------------------+
|                      | of the string             | |slice-start-in|          | ``'HT'``             |
|                      |                           +---------------------------+----------------------+
|                      |                           | |slice-end-in|            | ``'SQL'``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `at(s,k)`            | *k*-th character of *s*   | ``at('HTSQL',2)``         | ``'S'``              |
+----------------------+---------------------------+---------------------------+----------------------+
| `at(s,k,n)`          | *n* characters of *s*     | ``at('HTSQL',1,3)``       | ``'TSQ'``            |
|                      | starting with *k*-th      +---------------------------+----------------------+
|                      | character                 | ``at('HTSQL,-4,3)``       | ``'TSQ'``            |
|                      |                           +---------------------------+----------------------+
|                      |                           | ``at('HTSQL,4,-3)``       | ``'TSQ'``            |
+----------------------+---------------------------+---------------------------+----------------------+
| `upper(s)`           | upper case of *s*         | ``upper('htsql')``        | ``'HTSQL'``          |
+----------------------+---------------------------+---------------------------+----------------------+
| `lower(s)`           | lower case of *s*         | ``lower('HTSQL')``        | ``'htsql'``          |
+----------------------+---------------------------+---------------------------+----------------------+
| `trim(s)`            | strip leading and         | ``trim('  HTSQL  ')``     | ``'HTSQL'``          |
|                      | trailing spaces from *s*  |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `ltrim(s)`           | strip leading spaces      | ``ltrim('  HTSQL  ')``    | ``'HTSQL  '``        |
|                      | from *s*                  |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `rtrim(s)`           | strips trailing spaces    | ``rtrim('  HTSQL  ')``    | ``'  HTSQL'``        |
|                      | from *s*                  |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `replace(s,t,r)`     | replace all occurences    | |replace-in|              | ``'HTRAF'``          |
|                      | of *t* in *s* with *r*    |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+

.. |string-from-date-in| replace:: ``string(date('2010-04-15'))``
.. |string-from-dt-in| replace:: ``string(datetime('2010-04-15 20:13'))``
.. |string-from-dt-out| replace:: ``'2010-04-15 20:13'``
.. |slice-start-in| replace:: ``slice('HTSQL',null(),2)``
.. |slice-end-in| replace:: ``slice('HTSQL',2,null())``
.. |replace-in| replace:: ``replace('HTSQL','SQL','RAF')``

.. index:: string()

String Cast
-----------

`string(x)`
    Convert `x` to a string.

HTSQL permits any value to be converted to a string; the conversion
respects the format for literals of the original type.

.. htsql:: /{string('HTSQL'), string(true()), string(2.125),
             string(datetime('2010-04-15 20:13'))}

.. htsql::
   :cut: 3

   /department{'Department of '+name+' offers '
               +string(count(course))+' courses' :as text}
              ?exists(course)

.. index:: length()

String Length
-------------

`length(s)`
    Number of characters in `s`.

The exact meaning of a string length depends on the backend and the
underlying SQL type.  The function returns ``0`` if the argument is
*NULL*.

.. htsql:: /{length('HTSQL'), length(''), length(null())}

.. index:: +

Concatenation
-------------

`s + t`
    Concatenate `s` and `t`.

The concatenation operator treats a *NULL* operand as an empty string.

.. htsql:: /{'HT'+'SQL', null()+'SQL'}

.. htsql:: /course{department_code+'.'+string(no) :as code, title}
   :cut: 3

.. index:: ~, !~

Substring Search
----------------

`s ~ t`
    *TRUE* if `t` is a substring of `s`, *FALSE* otherwise.
`s !~ t`
    *TRUE* if `t` is a substring of `s`, *FALSE* otherwise.

The search functions are case-insensitive; exact rules for
case-insensitivity depend on the backend.

.. htsql:: /{'HTSQL'~'sql', 'sql'!~'HTSQL'}

.. htsql:: /school?code~'art'

.. index:: head(), tail(), slice(), at()

Substring Extraction
--------------------

`head(s)`
    The first character of `s`.
`head(s,n)`
    The first `n` characters of `s`.
`tail(s)`
    The last character of `s`.
`tail(s,n)`
    The last `n` characters of `s`.
`slice(s,i,j)`
    The `i`-th to `j`-th (exclusive) characters of `s`.
`at(s,k)`
    The `k`-th character of `s`.
`at(s,k,n)`
    `n` characters of `s` starting from the `k`-th.

In HTSQL, characters of a string are indexed from `0`.

Extraction functions permit negative or *NULL* indexes.  `head()`
(`tail()`), when given a negative `n`, produces all but the last (first)
`-n` characters of `s`; if `n` is *NULL*, it is assumed to be ``1``.

For `slice()`, a negative index `i` or `j` indicates to count
`(-i-1)`-th (`(-j-1)`-th) character from the end of `s`.  *NULL* value
for `i` or `j` indicates the beginning (the end) of the string.

For `at()`, a negative `n` produces `-n` characters of `s`
ending at the `k`-th character; if `n` is *NULL*, it is assumed to
be ``1``.

.. htsql:: /{'HTSQL' :head, 'HTSQL' :head(2), 'HTSQL' :head(-3)}

.. htsql:: /{'HTSQL' :tail, 'HTSQL': tail(3), 'HTSQL': tail(-2)}

.. htsql:: /{'HTSQL' :slice(1,-1), 'HTSQL' :slice(1,null()),
             'HTSQL' :slice(null(),-1)}

.. htsql:: /{'HTSQL' :at(2), 'HTSQL' :at(1,3), 'HTSQL': at(-1,-3)}

.. index:: upper(), lower()

Case Conversion
---------------

`upper(s)`
    Convert `s` to upper case.
`lower(s)`
    Convert `s` to lower case.

The conversion semantics is backend-dependent.

.. htsql:: /{'htsql' :upper, 'HTSQL' :lower}

.. index:: trim(), ltrim(), rtrim()

String Trimming
---------------

`trim(s)`
    Strip leading and trailing spaces from `s`.
`ltrim(s)`
    Strip leading spaces from `s`.
`rtrim(s)`
    Strip trailing spaces from `s`.

.. htsql::

   /{'  HTSQL  ' :trim :replace(' ','!'),
     '  HTSQL  ' :ltrim :replace(' ','!'),
     '  HTSQL  ' :rtrim :replace(' ','!')}

.. index:: replace()

Search and Replace
------------------

`replace(s,t,r)`
    Replace all occurences of substring `t` in `s` with `r`.

Case-sensitivity of the search depends on the backend; *NULL* values for
`t` and `r` are interpreted as an empty string.

.. htsql::

   /{'HTTP' :replace('TP','SQL'),
     'HTTP' :replace(null(), 'SQL'),
     'HTTP' :replace('TP', null())}


Date/Time Functions
===================

+----------------------+---------------------------+---------------------------+----------------------+
| Function             | Description               | Example Input             | Output               |
+======================+===========================+===========================+======================+
| `date(x)`            | cast *x* to date          | ``date('2010-04-15')``    |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `time(x)`            | cast *x* to time          | ``time('20:13')``         |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `datetime(x)`        | cast *x* to datetime      | |dt-from-untyped-in|      |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `date(yyyy,mm,dd)`   | date *yyyy-mm-dd*         | ``date(2010,4,15)``       | |date-out|           |
+----------------------+---------------------------+---------------------------+----------------------+
| |dt-cr-fn|           | datetime *yyyy-mm-dd*     | |dt-cr-in|                | |dt-out|             |
|                      | *HH:MM:SS*                |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `datetime(d,t)`      | datetime from date and    | |dt-dt-in|                | |dt-out|             |
|                      | time                      |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `today()`            | current date              | ``today()``               |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `now()`              | current date and time     | ``now()``                 |                      |
+----------------------+---------------------------+---------------------------+----------------------+
| `date(dt)`           | date of *dt*              | |date-from-dt-in|         | |date-out|           |
+----------------------+---------------------------+---------------------------+----------------------+
| `time(dt)`           | time of *dt*              | |time-from-dt-in|         | |time-out|           |
+----------------------+---------------------------+---------------------------+----------------------+
| `year(d)`            | year of *d*               | |year-in|                 | ``2010``             |
+----------------------+---------------------------+---------------------------+----------------------+
| `month(d)`           | month of *d*              | |month-in|                | ``4``                |
+----------------------+---------------------------+---------------------------+----------------------+
| `day(d)`             | day of *d*                | |day-in|                  | ``15``               |
+----------------------+---------------------------+---------------------------+----------------------+
| `hour(t)`            | hours of *t*              | ``hour(time('20:13'))``   | ``20``               |
+----------------------+---------------------------+---------------------------+----------------------+
| `minute(t)`          | minutes of *t*            | ``minute(time('20:13'))`` | ``13``               |
+----------------------+---------------------------+---------------------------+----------------------+
| `second(t)`          | seconds of *t*            | ``second(time('20:13'))`` | ``0.0``              |
+----------------------+---------------------------+---------------------------+----------------------+
| `d + n`              | increment *d* by *n* days | |date-inc-in|             | |date-out|           |
+----------------------+---------------------------+---------------------------+----------------------+
| `d - n`              | decrement *d* by *n* days | |date-dec-in|             | |date-out|           |
+----------------------+---------------------------+---------------------------+----------------------+
| `d1 - d2`            | number of days between    | |date-diff-in|            | ``13626``            |
|                      | *d1* and *d2*             |                           |                      |
+----------------------+---------------------------+---------------------------+----------------------+

.. |date-out| replace:: ``date('2010-04-15')``
.. |time-out| replace:: ``time('20:13')``
.. |dt-from-untyped-in| replace:: ``datetime('2010-04-15T20:13')``
.. |dt-out| replace:: ``datetime('2010-04-15T20:13')``
.. |dt-from-string-in| replace:: ``datetime( string('2010-04-15T20:13') )``
.. |dt-cr-fn| replace:: `datetime(yyyy,mm,dd [,HH,MM,SS])`
.. |dt-cr-in| replace:: ``datetime(2010,4,15,20,13)``
.. |dt-dt-in| replace:: ``datetime( date('2010-04-15'), time('20:13') )``
.. |date-from-dt-in| replace:: ``date( datetime('2010-04-15T20:13') )``
.. |time-from-dt-in| replace:: ``time( datetime('2010-04-15T20:13') )``
.. |year-in| replace:: ``year(date('2010-04-15'))``
.. |month-in| replace:: ``month(date('2010-04-15'))``
.. |day-in| replace:: ``day(date('2010-04-15'))``
.. |date-inc-in| replace:: ``date('1991-08-20')+6813``
.. |date-dec-in| replace:: ``date('2028-12-09')-6813``
.. |date-diff-in| replace:: ``date('2028-12-09') - date('1991-08-20')``

.. index:: date(), time(), datetime()

Date/Time Cast
--------------

`date(x)`
    Convert `x` to a *date* value.
`time(x)`
    Convert `x` to a *time* value.
`datetime(x)`
    Convert `x` to a *datetime* value.

Conversion functions accept untyped literals and string expressions.
An untyped literal must obey the literal format of the respective target
type.  Conversion from a string value is backend-specific.

.. htsql:: /{date('2010-04-15'), time('20:13'),
             datetime('2010-04-15 20:13')}

.. htsql:: /student?dob<date('1982-06-01')
   :cut: 3

.. index:: date(), datetime()

Date/Time Construction
----------------------

`date(yyyy,mm,dd)`
    Construct a date from the given year, month and day values.
`datetime(yyyy,mm,dd[,HH,MM,SS])`
    Construct a datetime from the given year, month, day, hour, minute
    and second values.
`datetime(d,t)`
    Construct a datetime from the given date and time.

Construction functions accept and normalize component values outside the
regular range.

.. htsql::

   /{date(2010,4,15), datetime(2010,4,15,20,13),
     datetime(date('2010-04-15'),time('20:13'))}

.. htsql:: /{date(2010,4,15), date(2010,3,46), date(2011,-8,15)}

.. index:: date(), time(), year(), month(), day(), hour(), minute(),
           second()

Component Extraction
--------------------

`date(dt)`
    Date of a *datetime* value.
`time(dt)`
    Time of a *datetime* value.
`year(d)`
    Year of a *date* or a *datetime* value.
`month(d)`
    Month of a *date* or a *datetime* value.
`day(d)`
    Day of a *date* or a *datetime* value.
`hour(t)`
    Hours of a *time* or a *datetime* value.
`minute(t)`
    Minutes of a *time* or a *datetime* value.
`second(t)`
    Seconds of a *time* or a *datetime* value.

The extracted values are integers except for `second()`, where the
extracted value is a float number.

.. htsql::

   /{date($dt), time($dt),
     year($d), month($d), day($d),
     hour($t), minute($t), second($t)}
    :where ($d := date('2010-04-15'),
            $t := time('20:13'),
            $dt := datetime($d,$t))

.. index:: +, -

Date/Time Arithmetics
---------------------

`d + n`
    Increment a *date* or a *datetime* value by `n` days.
`d - n`
    Decrement a *date* or a *datetime* value by `n` days.
`d1 - d2`
    Number of days between two *date* values.

.. htsql:: /{date('1991-08-20')+6813,
             datetime('1991-08-20 02:01')+6813.75833333333}

.. htsql:: /{date('2028-12-09')-6813,
             datetime('2028-12-10 14:25')-6813.75833333333}

.. htsql:: /date('2028-12-09')-date('1991-08-20')

.. htsql:: /student{name, (start_date-dob)/365 :round(1) :as age}
   :cut: 3


Aggregate Functions
===================

+----------------------+---------------------------+---------------------------+
| Function             | Description               | Example Input             |
+======================+===========================+===========================+
| `exists(ps)`         | *TRUE* if *ps* contains   | |exists-in|               |
|                      | at least one *TRUE*       |                           |
|                      | value; *FALSE* otherwise  |                           |
+----------------------+---------------------------+---------------------------+
| `every(ps)`          | *TRUE* if *ps* contains   | |every-in|                |
|                      | only *TRUE* values;       |                           |
|                      | *FALSE* otherwise         |                           |
+----------------------+---------------------------+---------------------------+
| `count(ps)`          | number of *TRUE* values   | |count-in|                |
|                      | in *ps*                   |                           |
+----------------------+---------------------------+---------------------------+
| `min(xs)`            | smallest element in *xs*  | ``min(course.credits)``   |
+----------------------+---------------------------+---------------------------+
| `max(xs)`            | largest element in *xs*   | ``max(course.credits)``   |
+----------------------+---------------------------+---------------------------+
| `sum(xs)`            | sum of elements in *xs*   | ``sum(course.credits)``   |
+----------------------+---------------------------+---------------------------+
| `avg(xs)`            | average value of elements | ``avg(course.credits)``   |
|                      | in *xs*                   |                           |
+----------------------+---------------------------+---------------------------+

.. |exists-in| replace:: ``exists(course.credits>5)``
.. |every-in| replace:: ``every(course.credits>5)``
.. |count-in| replace:: ``count(course.credits>5)``

Aggregate functions accept a plural argument, which, when evaluated,
produces a flow of values, and generates a single *aggregating* value
from it.

.. index:: exists(), every(), count()

Boolean Aggregates
------------------

`exists(xs)`
    Produce *TRUE* if `xs` contains at least one *TRUE* value, *FALSE*
    otherwise.  The aggregate returns *FALSE* on an empty flow.
`every(xs)`
    Produce *FALSE* if `xs` contains only *TRUE* values, *FALSE*
    otherwise.  The aggregate returns *TRUE* on an empty flow.
`count(xs)`
    The number of *TRUE* values in `xs`; ``0`` if `xs` is empty.

Boolean aggregates expect a Boolean argument; a non-Boolean argument
is converted to Boolean first (see function `boolean()`).

.. htsql:: /course?department.code='astro'
   :cut: 3

.. htsql::

   /{exists(astro_course.credits>=5),
     every(astro_course.credits>=5),
     count(astro_course.credits>=5)}
    :where astro_course := course?department.code='astro'

.. htsql:: /course?department.code='pia'

.. htsql::

   /{exists(pia_course.credits>=5),
     every(pia_course.credits>=5),
     count(pia_course.credits>=5)}
    :where pia_course := course?department.code='pia'

.. index:: min(), max()

Extrema
-------

`min(xs)`
    The smallest value in `xs`.
`max(xs)`
    The largest value in `xs`.

The functions accept numeric, string, enumeration and date/time
arguments.  *NULL* values in the flow are ignored; if the flow is
empty,  *NULL* is returned.

.. htsql::

   /{min(astro_course.credits), max(astro_course.credits)}
    :where astro_course := course?department.code='astro'

.. htsql::

   /{min(pia_course.credits), max(pia_course.credits)}
    :where pia_course := course?department.code='pia'

.. index:: sum(), avg()

Sum and Average
---------------

`sum(xs)`
    The sum of values in `xs`; returns ``0`` if `xs` is empty.
`avg(xs)`
    The average of values in `xs`.

The functions accept a numeric argument.  `sum()` returns a
result of the same type as the argument, `avg()` returns
a *decimal* result for an *integer* or a *decimal* argument,
and *float* result for a *float* argument.

.. htsql::

   /{sum(astro_course.credits), avg(astro_course.credits)}
    :where astro_course := course?department.code='astro'

.. htsql::

   /{sum(pia_course.credits), avg(pia_course.credits)}
    :where pia_course := course?department.code='pia'


Flow Operations
===============

+----------------------+---------------------------+---------------------------+
| Function             | Description               | Example Input             |
+======================+===========================+===========================+
| `flow ? p`           | records from *flow*       | ``school?code='edu'``     |
+----------------------+ satisfying condition *p*  +---------------------------+
| `filter(p)`          |                           | |filter-out|              |  
+----------------------+---------------------------+---------------------------+
| `flow ^ x`           | unique values of *x* as   | ``school^campus``         |
+----------------------+ it runs over *flow*       +---------------------------+
| `distinct(flow{x})`  |                           | |distinct-out|            |
+----------------------+---------------------------+---------------------------+
| `flow {x,...}`       | select output columns     | ``school{code,name}``     |
+----------------------+ *x*, ... for *flow*       +---------------------------+
| `select(x,...)`      |                           | |select-out|              |
+----------------------+---------------------------+---------------------------+
| `sort(x,...)`        | reorder records in *flow* | ``course.sort(credits-)`` |
|                      | by *x*, ...               |                           |
+----------------------+---------------------------+---------------------------+
| `limit(n)`           | first *n* records from    | ``course.limit(10)``      |
|                      | *flow*                    |                           |
+----------------------+---------------------------+---------------------------+
| `limit(n,k)`         | *n* records from *flow*   | ``course.limit(10,20)``   |
|                      | starting from *k*-th      |                           |
+----------------------+---------------------------+---------------------------+
| `x -> xs`            | traverse an ad-hoc link   | |link-in|                 |
+----------------------+---------------------------+---------------------------+
| `fork([x])`          | traverse a                | ``course.fork(credits)``  |
|                      | self-referential link     |                           |
+----------------------+---------------------------+---------------------------+

.. |filter-out| replace:: ``school.filter(code='edu')``
.. |distinct-out| replace:: ``distinct(school{campus})``
.. |select-out| replace:: ``school.select(code,name)``
.. |link-in| replace:: ``school.(campus -> school)``

.. index:: ?, filter()

Sieving
-------

`flow ? p`
    Emit records from `flow` that satisfy condition `p`.
`filter(p)`
    Emit records from the input flow that satisfy condition `p`.

The condition is expected to be of Boolean type.  If the argument `p`
is not Boolean, it is implicitly converted to Boolean (see `boolean()`).

.. htsql:: /school?campus='south'

.. htsql:: /school.filter(campus='south')

.. index:: ^, distinct()

Projection
----------

`flow ^ x`
    Emit all unique values of `x` as it ranges over `flow`.  *NULL*
    values are ignored.
`flow ^ {x,...}`
    Emit all unique values of the expressions `x,...`.  *NULL* values
    are ignored.
`distinct(flow{x,...})`
    Emit all unique values of the output columns of `flow{x,...}`.
    *NULL* values are ignored.

The projection operation `flow ^ x` creates a new naming scope, which
may contain the following names:

`flow`
    If `flow` is an identifier, then it is used to denote the plural
    link associating each value of `x` with respective records from the
    original flow.  It is called the complement link of the projection.
    The symbol `^` is an alias for a complement link and could be used
    when `flow` is not an identifier and so cannot be used as a name.
`x`
    If `x` is an identifier, then it refers to the value of `x`.
    It is called the kernel of the projection.  When `x` is not an
    identifier, but an arbitrary expression, one may assign it a name
    using in-place selector assignment syntax.

.. htsql:: /school{code, name, campus, count(department)}
   :cut: 3

.. htsql:: /school^campus {campus, count(school)}

.. htsql:: /school^campus {*, count(^)}

.. **

.. htsql:: /distinct(school{campus}) {campus, count(school)}

.. htsql::
   :cut: 3

   /school^{num_dept := count(department)}
    {num_dept, count(school)}

.. htsql::
   :cut: 3

   /school^{campus :if_null '', count(department)}
    {*, count(school)}

.. **

.. index:: {}, select()

Selection
---------

`{x,...}`
    Define output columns in the input flow.
`flow{x,...}`
    Define output columns in the given flow.
`select(x,...)`
    Define output columns in the input flow.

The selector expression admits two forms of short-cut syntax:

*in-place assignment*
    If an element of a selector is an assignment expression,
    the name defined by the assignment is added to the current scope.
    Only unqualified attribute and reference assignments are allowed.
*sorting decorators*
    If an element of a selector contains a sort order indicators,
    the expression is used to reorder elements in the input flow.

.. htsql:: /{count(school), count(program), count(department)}

.. htsql:: /select(count(school), count(program),
                   count(department))

.. htsql:: /school{code, count(program)}
   :cut: 3

.. htsql:: /school.select(code, count(program))
   :cut: 3

.. htsql:: /school{code, count(program)-}
   :cut: 3

.. htsql:: /school{code, num_prog := count(program)}?num_prog<4
   :cut: 3

.. htsql::
   :cut: 3

   /department{code, $avg_credits := avg(course.credits),
               count(course?credits>$avg_credits)}


Scope Operations
================

+----------------------+---------------------------+---------------------------+
| Function             | Description               | Example Input             |
+======================+===========================+===========================+
| `define(x:=...)`     | add names to the current  | |define-in|               |
|                      | scope                     |                           |
+----------------------+---------------------------+---------------------------+
| `where(expr,x:=...)` | evaluate an expression    | |where-in|                |
|                      | with extra names in the   |                           |
|                      | current scope             |                           |
+----------------------+---------------------------+---------------------------+
| `@ x`                | evaluate an expression in |                           |
|                      | the initial scope         |                           |
+----------------------+---------------------------+---------------------------+
| `root()`             | root scope                |                           |
+----------------------+---------------------------+---------------------------+
| `this()`             | current scope             |                           |
+----------------------+---------------------------+---------------------------+

.. |define-in| replace:: ``define(num_prog:=count(program))``
.. |where-in| replace:: ``count(course?credits>$c) :where $c:=avg(course.credits)``

.. index:: define(), where()

Calculated Attributes
---------------------

`define(x:=...)`
    Add a calculated attribute to the current scope.
`where(expr,x:=...)`
    Evaluate an expression in a current scope with a calculated
    attribute.

These functions add calculated attributes and references to the current
scope.

.. index:: @, root(), this()

Scopes
------

`@ x`
    Reset to the initial scope.
`root()`
    The root scope.
`this()`
    The current scope.


Decorators
==========

+----------------------+---------------------------+---------------------------+
| Function             | Description               | Example Input             |
+======================+===========================+===========================+
| `as(x,title)`        | set the column title      | |as-in|                   |
+----------------------+---------------------------+---------------------------+
| `x +`                | indicate ascending order  | ``credits+``              |
+----------------------+---------------------------+---------------------------+
| `x -`                | indicate descending order | ``credits-``              |
+----------------------+---------------------------+---------------------------+

.. |as-in| replace:: ``count(program) :as '# of programs'``

.. index:: as()

Title
-----

`as(x,title)`
    Specifies the title of the output column.

The title could be either an identifier or a quoted literal.  This
function should be used only when specifying output columns using a
selection operator.

.. htsql:: /school{code :as ID, count(program) :as '# of Programs'}
   :cut: 3

.. index:: +, -

Direction Decorators
--------------------

`x +`
    Specifies ascending direction, *NULL* first.
`x -`
    Specifies descending direction, *NULL* last.

This decorators should be used only on arguments of `sort()` or in a
selection operator.

.. htsql:: /school.sort(campus+)
   :cut: 3


.. index:: html(), txt(), csv(), tsv(), raw(), xml(), sql()

Formatters
==========

+---------------+---------------------------------------+
| Function      | Description                           |
+===============+=======================================+
| `/:html`      | HTML presentation output              |
+---------------+---------------------------------------+
| `/:txt`       | plain text presentation output        |
+---------------+---------------------------------------+
| `/:csv`       | CSV (comma-separated values) output   |
+---------------+---------------------------------------+
| `/:tsv`       | TSV (tab-separated values) output     |
+---------------+---------------------------------------+
| `/:raw`       | JSON-serialized generic output        |
+---------------+---------------------------------------+
| `/:json`      | JSON-serialized object output         |
+---------------+---------------------------------------+
| `/:xml`       | XML-serialized object output          |
+---------------+---------------------------------------+
| `/:sql`       | prints corresponding SQL queries      |
+---------------+---------------------------------------+

These functions specify the format of the output data. 

Presentation Output
-------------------

We support two presentation outputs, ``/:txt`` output format is
supported for text clients like our command line shell, and the
``/:html`` provides single-page static output.  The ``/:html``
format has a customizable template that can be used to provide
headers and footers.

.. htsql:: /department{school,*}/:html
   :plain:
   :hide:

.. htsql:: /department{school,*}/:txt
   :plain:
   :cut: 12

Object Output
-------------

The ``/:xml`` and ``/:json`` formatters produce a customary "object"
structure which lack any header information.  These object formats are
perfect for post-processing tools that already know about the query
output structure, such as a Javascript program or XSLT stylesheet.

.. htsql:: /department{school,*}.limit(3)/:json
   :plain:

.. htsql:: /department{school,*}.limit(3)/:xml
   :plain:


Tabular Output
--------------

Tabular formatters (``/:csv``, ``/:tsv``) are intended for use in data
processing tools.  These formats include only column headers and the
output data on subsequent rows.

.. htsql:: /department{school,*}/:csv
   :plain:
   :cut: 3

.. htsql:: /department{school,*}/:tsv
   :plain:
   :cut: 3

Generic Output
--------------

Our primary default output, ``/:raw``, is meant for generic tools that
must handle arbitrary queries.  This format reflects our native product
structure and has two corresponding sections:  ``meta`` details type
information and ``data`` has the corresponding output data.  


.. htsql:: /department{school,*}.limit(3)/:raw
   :plain:

Query Debug
-----------

The special ``/:sql`` designator isn't actually a formatter at all,
since it shortcuts the whole query execution part of the engine and
produces the SQL queries used as an output.  It's useful for
understanding what's going on under the hood.

.. htsql:: /department{school,*}/:sql
   :plain:


.. vim: set spell spelllang=en textwidth=72:
