# 4.1 Description

Part of: [4 Cursor](4-cursor.md)

HDFql provides cursors that offer several ways to traverse result sets according to specific needs. The following list enumerates these ways or functionalities (please refer to their links for further information):

- First (moves cursor to the first position within the result set – [hdfql_cursor_first](5-2-18-hdfql-cursor-first.md))
- Last (moves cursor to the last position within the result set – [hdfql_cursor_last](5-2-20-hdfql-cursor-last.md))
- Next (moves cursor to the next position within the result set – [hdfql_cursor_next](5-2-22-hdfql-cursor-next.md))
- Previous (moves cursor to the previous position within the result set – [hdfql_cursor_previous](5-2-24-hdfql-cursor-previous.md))
- Absolute (moves cursor to an absolute position within the result set – [hdfql_cursor_absolute](5-2-26-hdfql-cursor-absolute.md))
- Relative (moves cursor to a relative position within the result set – [hdfql_cursor_relative](5-2-28-hdfql-cursor-relative.md))

Besides their traversal functionalities, a particular feature of cursors in HDFql is that they store result sets returned by [DATA QUERY LANGUAGE (DQL)](6-6-data-query-language-dql.md) and [DATA INTROSPECTION LANGUAGE (DIL)](6-7-data-introspection-language-dil.md) operations. To retrieve values from result sets, the functions starting with “hdfql_cursor_get” can be used. These and the remaining functions offered by cursors can be found in [Table 5.9](5-2-functions.md#table-5-9) (each of these begins with the prefix “hdfql_cursor”).

When a certain operation is executed, HDFql stores the result set returned by this operation in its default cursor. This cursor is available to the programmer and is automatically created and initialized upon loading the HDFql library by a program. If additional (i.e. user-defined) cursors are needed, they can be created as follows (in C):

```c
// create a cursor named "my_cursor" 
HDFQL_CURSOR my_cursor; 
```

As a side note, additional cursors are created in C++, Java, Python, C#, Fortran, R, and MATLAB as follows:

```cpp
// create a cursor named "myCursor" in C++ 
HDFql::Cursor myCursor;
```

```java
// create a cursor named "myCursor" in Java 
HDFqlCursor myCursor = new HDFqlCursor();
```

```python
# create a cursor named "my_cursor" in Python 
my_cursor = HDFql.Cursor()
```

```csharp
// create a cursor named "myCursor" in C# 
HDFqlCursor myCursor = new HDFqlCursor();
```

```fortran
! create a cursor named "my_cursor" in Fortran 
TYPE(HDFQL_CURSOR) :: my_cursor
```

```r
# create a cursor named "my_cursor" in R 
my_cursor <- hdfql_cursor()
```

```matlab
% create a cursor named "my_cursor" in MATLAB 
myCursor = HDFqlCursor();
```

Before an additional cursor is used to store and eventually traverse a result set, it must be properly initialized (refer to the function [hdfql_cursor_initialize](5-2-6-hdfql-cursor-initialize.md) for further information). The initialization of a cursor is only required in C and is performed once, while in C++, Java, Python, C#, Fortran, R, and MATLAB, such initialization is redundant (i.e. not required) as it is done automatically (by HDFql) when creating a cursor. Initializing a cursor can be done as follows (in C):

```c
// initialize a cursor named "my_cursor" 
hdfql_cursor_initialize(&my_cursor);
```

Complementarily, when a cursor is no longer needed and is about to be freed/finalized, it must be properly cleared (refer to the function [hdfql_cursor_clear](5-2-9-hdfql-cursor-clear.md) for further information). The clearing of a cursor is only required in C and is performed once before freeing the pointer representing the cursor (otherwise, a memory leak may occur), while in C++, Java, Python, C#, Fortran, R, and MATLAB, such a step is redundant (i.e. not required) as it is done automatically (by HDFql) when finalizing the object representing the cursor. Clearing a cursor can be done as follows (in C):

```c
// clear a cursor named "my_cursor" 
hdfql_cursor_clear(&my_cursor);
```

To switch between different cursors (to be used for separate needs), the function [hdfql_cursor_use](5-2-7-hdfql-cursor-use.md) may be employed (in C):

```c
// use a cursor named "my_cursor" 
hdfql_cursor_use(&my_cursor);
```

The following C code snippet illustrates the usage of the HDFql default cursor and a user-defined cursor, as well as some typical operations performed on/by these.

```c
// create a cursor named "my_cursor" 
HDFQL_CURSOR my_cursor; 

// create an HDF5 dataset named "my_dataset0" of data type int with an initial value of 8 
hdfql_execute("CREATE DATASET my_dataset0 AS INT VALUES(8)"); 

// create an HDF5 dataset named "my_dataset1" of data type float with initial values of 3.2, 5.3, 7.4, and 9.5
hdfql_execute("CREATE DATASET my_dataset1 AS FLOAT(4) VALUES(3.2, 5.3, 7.4, 9.5)");

// select (i.e. read) data from dataset "my_dataset0" and populate HDFql default cursor with it
hdfql_execute("SELECT FROM my_dataset0"); 

// initialize cursor "my_cursor" 
hdfql_cursor_initialize(&my_cursor); 

// use cursor "my_cursor" 
hdfql_cursor_use(&my_cursor); 

// select (i.e. read) data from dataset "my_dataset1" and populate cursor "my_cursor" with it
hdfql_execute("SELECT FROM my_dataset1"); 

// use HDFql default cursor 
hdfql_cursor_use(NULL); 

// display number of elements in HDFql default cursor 
printf("Number of elements in HDFql default cursor is %d\n", hdfql_cursor_get_count(NULL)); 

// move HDFql default cursor to the next position within the result set 
hdfql_cursor_next(NULL); 

// display content of HDFql default cursor 
printf("Current element of HDFql default cursor is %d\n", *hdfql_cursor_get_int(NULL)); 

// display number of elements in cursor "my_cursor" 
printf("Number of elements in cursor \"my_cursor\" is %d\n", hdfql_cursor_get_count(&my_cursor)); 

// use cursor "my_cursor" 
hdfql_cursor_use(&my_cursor);

// display content of cursor "my_cursor" 
while(hdfql_cursor_next(NULL) == HDFQL_SUCCESS) 
{
    printf("Current element of cursor \"my_cursor\" is %f\n",
*hdfql_cursor_get_float(NULL)); 
}
```

The output of executing the code snippet would be similar to the following:

```text
Number of elements in HDFql default cursor is 1
Current element of HDFql default cursor is 8
Number of elements in cursor "my_cursor" is 4
Current element of cursor "my_cursor" is 3.2
Current element of cursor "my_cursor" is 5.3
Current element of cursor "my_cursor" is 7.4
Current element of cursor "my_cursor" is 9.5
```

When populating a cursor with data from a dataset or attribute with two or more dimensions, the data is always linearized into a single dimension. The linearization process is depicted in [Figure 4.1](#figure-4-1). Subsequently, if needed, it is up to the programmer to access the data (stored in the cursor) according to its original dimensions. In this case, the functions [hdfql_cursor_get_dimension_count](5-2-14-hdfql-cursor-get-dimension-count.md) and [hdfql_cursor_get_dimension](5-2-15-hdfql-cursor-get-dimension.md) may be useful to help with such a task.

![Figure 4.1 – Linearization of a two-dimensional dataset into a (one-dimensional) cursor](assets/figure-2.png)

Figure 4.1 – Linearization of a two-dimensional dataset into a (one-dimensional) cursor

In addition, in case of the need to select (i.e. read) and process datasets that do not fit in (RAM) memory due to their size, cursors in HDFql have the capability to be populated with only one slice/portion[^1] of a dataset at a time by sliding along its first or last dimension. This enables effective out-of-core[^2] processing of such datasets in a seamless manner. In detail, when a sliding cursor is positioned outside the slice/portion it currently stores (i.e. before or after the first or last element stored in the cursor, respectively), HDFql will automatically: (1) discard the slice/portion stored in the sliding cursor, (2) read a new slice/portion of the dataset to include/cover the intended position, and (3) populate the sliding cursor with the new slice/portion.

[^1]: To accomplish this task, HDFql uses a hyperslab selection under the hood and without the programmer’s intervention.
[^2]: The term out-of-core typically refers to algorithms that are designed to process data that are too large to fit in the (RAM) memory of a single machine at once.
