# Basic Constructs

The basic building blocks of the OSQL query language are described here.

## The Read-Eval-Print-Loopâ€‹

OSQL statements can be evaluated immediately by SA Engine through its
OSQL *Read-Eval-Print-Loop, REPL*. When using the SA Engine Console
REPL in Unix/Windows shells, OSQL statements must be terminated by a
semicolon (;). The `;`

is not needed when evaluating a single
statement to the REPL in your browser or in SA Studio. You
can enter more than one statement to the REPL by separating them with
a `;`

, for example:

`"Hello" + " world";`

1+2+3;

sin(4) + sqrt(9) * 2;

rand(100)+1;

## Constantsâ€‹

*Constants* can be integers, reals, strings, time stamps, booleans, or null. The value of a constant is the constant itself, for example:

`123;`

1.2;

2.3E2;

-2.4e-21;

'a string';

|2023-09-21T09:45:44.086Z|;

true;

false;

null;

Examples of *strings*:

`"A string";`

'A string';

'A string with "';

"A string with \" and '"

The enclosing string separators (`'`

or `"`

) for a string constant
must be the same. If the string separator is `"`

then `\`

is the
*escape character* inside the string, replacing the succeeding
character. For example the string `'ab"\'`

can also be written as
`"ab\"\\"`

, and the string `a'"b`

must be written as `"a'\"b"`

.

*Boolean constants* represent logical values. The constant `false`

is
equivalent to `null`

casted to type *Boolean*. The only legal boolean
value that can be stored in the database is `true`

and a boolean value
is regarded as false if it is not in the database (so called close
world assumption).

## Identifiersâ€‹

*Identifiers* represent names of OSQL variables, functions, arrays,
etc. Examples:

` MySalary`

sin

Integer

x

x1234

x1234_b

Notice that OSQL identifiers are NOT case sensitive; i.e. they are
always internally capitalized. By contrast OSQL reserved keywords are
always written with *lower case* letters.

## Commentsâ€‹

A *comment* enclosed with `/*`

and `*/`

can be placed anywhere in an
OSQL statement outside identifiers, constants, strings, or
variables. You can also use the `// comment`

notation for single line
comments.

## Variablesâ€‹

*Variables* are of two kinds: *local variables* and *session
variables*.

*Local variables* are identifiers for data values inside OSQL queries
and functions. Variables are *not* case sensitive and internally capitalized.

For example, the following query uses a local variable named `I`

;

`select i`

from Number i

where I = 3

Local variables must
be declared in `from`

clauses of
**queries** or function
signatures (see **Defining
Functions**).

*Session variables* prefixed with `:`

hold only *temporary* results
during interactive sessions. Session variables *cannot* be
referenced in function bodies and they are *not* stored in the
database. Their lifespan is the current session only. Their
purpose is to hold temporary values in scripts and database
interactions.

Examples:

` :my_session_variable`

:MySessionVariable2

Session variables can be assigned by the variable assignment statement
*set*, for example:

`set :x3 = 2.3; // set session variable :x3`

set :i = 2 + sqrt(:x3); // set session variable :i

set :v = [1,2,3]; // set session variavle :v

:x3; // get value of :x3

:i; // get value of :i

:V; // get value of :v

You can inspect the type of the value to which a variable is assigned
by the function `typesig(Object)`

. For example:

`set :x3 = 2.3; `

set :v = [1,2,3];

typesig(:x3);

typesig(:v)

## Expressionsâ€‹

Expressions are formulas expressed with the OSQL syntax that can be evaluated by the REPL to produce a value. Complex expressions can be built up in terms of other expression. Expressions are basic building blocks in all kinds of OSQL statements.

For example:

`1.23;`

1+2+3;

[1,2,3]

To run this code block you must be logged in and your studio instance must be started.

Entering simple expressions followed by a semicolon is the simplest form of OSQL queries.

## Collectionsâ€‹

*Collections* represent sets of objects. OSQL supports five kinds of
collections: bags, vectors, arrays, streams, and key-value
associations (records):

- A
*bag*is a set where duplicates are allowed. - A
*vector*is an ordered sequence of objects of any kind. - A
*record*is an associative array of key-value pairs. - An
*array*is a multi-dimensional collection of numbers. - A
*stream*is a possibly infinite sequence of objects that is continuously extended with new*elements*at the end.

Collections are constructed by collection *constructor* expressions.
Examples:

`bag(1,2,3);`

[1,2,3];

[[1,2],[3,4]];

{"id":1,"name":"Kalle","age":32};

array([1,2,3])

## Bagsâ€‹

A common kind of collection is *bags*, which are unordered sets of
objects with duplicates allowed. The value of a query is usually a
bag. When a query to an SA Engine REPL returns a bag as result the
elements of the bag are printed on separate lines.

Bags can be explicitly created using the `bag()`

constructor function,
for example:

` bag(1,2,2,3)`

The value of **set
queries** are bags, for
example:

`select i `

from Integer i

where i in bag(2,8,7)

## Vectorsâ€‹

*Vectors* are sequences of objects of any kind. Square brackets `[]`

enclose vector elements, For example, after evaluating the statement
`set :v=[1,2,3]`

the value of the session variable `:v`

is `[1,2,3]`

.

Vector element $v_i$ can be accessed with the notation `v[i]`

, where the
indexing `i`

is from 1 and up. For example:

`:v=[1,2,3];`

:v[3]

Vectors are represented as objects of type `Vector`

.

Section **Vector
queries** documents
vector functions and queries.

## Arraysâ€‹

*Multi-dimensional arrays* represent collections of numbers. 1D arrays
represent numerical vectors, 2D arrays represent numberical matricies,
and higher dimensionality arrays represent tensors.

Arrays are represented as objects of type `Array`

. The difference
between objects of type `Array`

and type `Vector`

is that vectors
represent 1D sequences of objects of any kind, while 1D arrays
very efficiently represent numerical vectors.

Arrays create created with the function array():

`array([1,2,3]); // a 1D array of integers`

array([1.0,2.0,3.0]); // a 1D array of reals

array([[1,2],[3,4]]); // a 2x2 2D array of integers

array([[1.0,2],[3,4]]); // a 2x2 2D array of reals

Section **Arrays and tensors**
documents array functions and queries.

## Recordsâ€‹

*Records* represent dynamic associations between keys and values. A
record is a dynamic and associative array. Other commonly used terms
for associative arrays are JSON objects, property lists, key-value pairs,
dictionaries, or hash links. OSQL uses generalized JSON notation to
construct records. For example the following expression assigns `:r`

to a record where the key (property) 'Greeting' field has the value
'Hello, I am Tore' and the key 'Email' has the value
'Tore.Andersson@it.uu.se':

` set :r= {'Greeting':'Hello, I am Tore','Email':'Tore.Andersson@it.uu.se'}`

A field `f`

of a record bound to a variable `r`

can be access with the notation `r[f]`

, for example:
`:r['Greeting' ]`

evaluates to `Hello, I am Tore`

.

The type of records is called `Record`

. The type `Json`

represents
JSON data. It is above both types `Record`

and `Vector`

in the type
hierarchy, since JSON representations include both JSON objects and
vectors.

## Streamsâ€‹

*Streams* represent possibly infinite and growing sequences of
elements, such as measurements produced by a sensor. Streams can also
be generated (simulated) by functions. For example, the function `heartbeat(p)`

returns an infinite stream of number elements produced every p seconds:

`heartbeat(0.5) `

Streams are documented in section **Streams**.