# Indexes and Cursors in C

> As explained in the Indexes and Cursors page, eXtremeDB supports a variety of index types. The following sections give C API implementation details for managing each of these index types: B-Tree, Patricia, R-Tree, KD-Tree, Hash, OID, Autoid, User-defined.

> An eXtremeDB class can not be accessed without an index, so a class must have at least one index. If no index is explicitly defined, and the class has no dynamic-size fields (such as strings, vectors, optional structs and etc.), then a list index will be created for this class using the fixedrec allocator. Via the fixedrec allocator the location of all the database object instances is well known, which permits access to these objects through a chain of objects instead of a B-tree for this list index implementation. This index implementation has a minimal performance cost compared to a normal B-tree index.

> However, note that the implicit creation of a list index happens only for classes with fixed size. If the class contains one or more dynamic fields, the fixedrec allocator cannot be used to generate the "chain of objects". So when dynamic field(s) are present the application must explicitly define a list or some other type of index.

> ### C-Specific Features

> C applications can optimize B-Tree index performance using the `inclusive` declaration. (Please refer to the Optimizing tree Indexes section below.)

> Another additional feature available to C applications is the ability to use the `voluntary` qualifier in schema definitions for an index to indicate that the index can be created or dropped at runtime. In other words, `voluntary` indexes are not built until the application explicitly calls the generated function `<classname_indexname>_create()`. In the same fashion, the application can remove a `voluntary` index by calling the generated function `<classname_indexname>_drop()`.

> Also the eXtremeDB specialized Object ID (`oid`) index is possible in C applications. (Please refer to the OID Indexes section below.)

> Another significant feature available for C developers is the ability to define custom indexes. (Please refer to the Userdef Indexes section below.)

> ## Persistent Databases

> For persistent databases there are optimization features that pertain to particularly to B-Tree indexes. Please refer to the Persistent Database Index Optimization in C page for further details.

> ## B-Tree Indexes

> As explained in the B-Tree Indexes page, `tree` indexes can be used for ordered (sorted) retrieval, range retrieval and pattern matching. A B-Tree index is specified in the schema file with the `tree` declaration and optional modifiers `unique` or `nonunique`. If no modifier is specified the default is `nonunique`.

> For example:

>         class anObject
>         {
>             uint4 value;

>             tree <value> Idx;
>         };

> The mcocomp schema compiler generates cursor functions and search functions for `tree` indexes.

> To obtain a `cursor` for a `tree` index:

>         MCO_RET classname_indexname_index_cursor( /*IN*/  mco_trans_h t, /*OUT*/ mco_cursor_h c );

> The `_search()` functions generated for all `tree` indexes are of the following form:

>         MCO_RET classname_indexname_search( /*IN*/ mco_trans_h trans,
>                             /*INOUT*/ mco_cursor_h cursor,
>                             /*IN*/ MCO_OPCODE op,
>                             /*IN*/ [const] <type> [*]param1,
>                             [[/*IN*/ uint2 len1,]
>                             [/*IN*/ [const] <type> [*]param2,
>                             [/*IN*/ uint2 len2,] …]);

> Here `MCO_OPCODE` represents a compare operation as defined in cursor operator codes.

> To obtain the object handle from the current cursor position:

>         MCO_RET classname_from_cursor( /*IN*/  mco_trans_h t,
>                             /*IN*/  mco_cursor_h c,
>                             /*OUT*/ classname *object);

> Note that the cursor must be properly initialized with a cursor positioning function (see the following section) to retrieve a valid database object handle.

> The `_locate()` function is used to position a `tree` index cursor based on an object reference. The cursor must have been previously instantiated using the `_index_cursor()` function. (Note that the `_locate()` function applies only to `tree`-based cursors, not to `list` or `hash` cursors):

>         MCO_RET classname_indexname_locate( /*IN*/    mco_trans_h t,
>                             /*INOUT*/ mco_cursor_h c,
>                             /*IN*/    classname * handle);

> ### Cursors and Searches

> The standard cursor positioning functions `mco_cursor_first(), mco_cursor_last(), mco_cursor_next()` and `mco_cursor_prev()` are used to initialize the cursor to the first or last and iterate through the result sets of searches.

> Searches are performed using the generated `_search()` function. Please refer to the Searches page for implementation details. Also, as explained in the Searches page, `tree` indexes can be used for pattern matching, range retrieval and ordered (sorted) retrieval.

> ### Optimizing tree indexes

> Normally, the B-Tree implementation doesn't store key values in the index. However, for in-memory databases, sometimes it is beneficial to keep the key value on the index pages.

> As explained in the Inclusive and Covering Indexes page, B-Tree index performance can be optimized by declaring the index `inclusive`.

> A “key-value-inclusive” index for a class is defined in a C API schema through the `inclusive` keyword, for example:

>         class Tab
>         {
>             string name;
>             int4   code;
>             string body;

>             inclusive tree<name,code> pk;
>         };

> If it is desired to make all B-Tree indexes for transient classes “key-value-inclusive”, the `MCO_DB_INCLUSIVE_BTREE` option can be specified in the `db_params` argument to C API `mco_db_open_dev()`, for example:

>         MCO_RET            rc;
>         mco_db_h           db = 0;
>         mco_device_t       dev[N_DEVICES];
>         mco_db_params_t    db_params;

>         ...

>         db_params.mode_mask |= MCO_DB_INCLUSIVE_BTREE;

>         ...

>         rc = mco_db_open_dev(db_name, mydb_get_dictionary(), dev, N_DEVICES, &db_params );

> In addition, a “covering” index (which contains all of the fields of interest in a class) can be also declared inclusive to leverage CPU cache usage, for example:

>         class Tab
>         {
>             string name;
>             int4   code;
>             string body;

>             inclusive tree<*> pk;
>         };

> (Note that a “key-value-inclusive” or “covering” index can only be a B-Tree index so the inclusive declaration can only precede the `tree` keyword.)

> ## Patricia trie Indexes

> As explained in the Patricia Indexes page, the eXtremeDB `patricia` index is particularly useful for network and telecommunications applications. A `patricia` index can be declared over scalar and `boolean` data types as well as arrays and vectors of those types. In fact, the `boolean` data type allows bit arrays to be used to store IP addresses. A `patricia` index can also be declared `unique`; in the absence of the `unique` keyword it defaults to allowing duplicates. Unlike other eXtremeDB indexes, the `patricia` index cannot be compound; it is always declared for a single field.

> The following schema illustrates some possible declarations:

>         class xyz
>         {
>             boolean			b1[32];
>             vector<boolean>		b2;
>             uint4			b3;
>             char<10>		b4[10];
>             vector<string>		b5;

>             patricia <b1>		Ib1;
>             patricia <b2>		Ib2;
>             patricia <b3>		Ib3;
>             patricia <b4>		Ib4;
>             patricia <b5>		Ib5;
>             unique patricia <b1>	Ib1U;
>             unique patricia <b2>	Ib2U;
>             unique patricia <b3>	Ib3U;
>             unique patricia <b4>	Ib4U;
>             unique patricia <b5>	Ib5U;
>         }

> Note that `boolean` fields `b1`, `b2` are used as bit arrays for `patricia` indexes `Ib1U` and `Ib2U`. The `boolean` data type is treated in a special way by the `mcocomp` schema compiler and requires some explanation in the following section. Also, in addition to the standard tree index generated functions, the following section discusses functions that are generated for each `patricia` index.

> ### Boolean Data Type

> The `boolean` data type can be used to define a single bit field, a fixed size array of bits, or a variable length array of bits. The only index type possible for the `boolean` data type is the `patricia` index. A single-bit field cannot be indexed, nor is it advisable to index a short array of bits.

> For example:

>         class xyz
>         {
>             boolean b1;  		// a bit field
>             boolean b2[32]; 	// fixed-size array of 32 bits

>             vector<boolean>  b3; 	// variable-length bit array
>         };

> For C applications, the following functions are generated for fields of type `boolean`:

>         classname_fieldname_get(classname *handle, /*OUT*/ uint1 * result);

>         classname_fieldname_put(classname *handle, uint1 value);

> For a fixed-size bit array the following interfaces are also generated:

>         // reads the specified bit out of the array.
>         // Note: It’s OK to read one bit, but in order
>         // to read several bits it’s better to read the
>         // entire array and mask it.
>         classname_fieldname_at(classname* handle, uint2 index, /*OUT*/ uint1 *result);

>         // read range of bits.
>         // Note the start_index bit number must be a power of 8
>         classname_fieldname_get_range(classname* handle, uint2 start_index, uint2 num,
>                             /*OUT*/ uint1 *dest );

>         // again, the bit-by-bit _put is not the most effective
>         // way of writing the array.  Better to mask the entire
>         // array and use _put_range
>         classname_fieldname_put(classname* handle, uint2 index, uint1 value );

>         // write range of bits.
>         // Note the start_index bit number must be a power of 8
>         classname_fieldname_put_range(classname* handle, uint2 start_index,
>                             uint2 num, const uint1 *src );

>         // read the entire field
>         classname_fieldname_get(classname *handle, /*OUT*/ uint1 * result);

> For variable-size arrays (`vectors`), the following interfaces are also generated:

>         // vector size (in bits)
>         classname_fieldname_size(classname* handle, /*OUT*/ uint2 *result );

>         // allocate a vector (size in bits)
>         classname_fieldname_alloc(classname* handle, uint2 size);

>         // read an element at a specified position
>         classname_fieldname_at(classname* handle, uint2 index,
>                         /*OUT*/ uint1 *result);

>         // read range of bits.
>         // Note the start_index bit number must be a power of 8
>         classname_fieldname_get_range(classname* handle, uint2 start_index,
>                             uint2 num, /*OUT*/ uint1 *dest );

>         // write range of bits.
>         // Note the start_index bit number must be a power of 8
>         classname_fieldname_put_range(classname* handle, uint2 start_index,
>                             uint2 num, const uint1 *src );

>         // write an element (not very efficient)
>         classname_fieldname_put(classname* handle, uint2 index, uint1 value );

> ### Generated Functions for patricia Indexes

> The generated functions applicable only to `patricia` indexes are `_longest_match(), _exact_match(), _prefix_match()` and `_next_match()`. These will have slightly different forms depending on the type of field being indexed.

> A `patricia` index created over a scalar field will cause the following functions to be generated:

>         classname_indexname_next_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type mask, int number_of_bits);

>         classname_indexname_prefix_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type mask, int number_of_bits);

>         classname_indexname_longest_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type mask, int number_of_bits);

>         classname_indexname_exact_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type mask, int number_of_bits);

> The `type` argument is the scalar type (for example `uint4`) and `mask` is the key value to match. If the indexed field is a fixed-length array or a `vector` of scalars these functions will be of the form:

>         classname_indexname_next_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type * mask, int number_of_bits);

>         classname_indexname_prefix_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type * mask, int number_of_bits);

>         classname_indexname_longest_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type * mask, int number_of_bits);

>         classname_indexname_exact_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             type * mask, int number_of_bits);

> Here `type` is the type of each element of the array or `vector` (for example `uint4`) and `mask` is the key value to match. If the indexed field is an array of `boolean` these functions will be of the form:

>         classname_indexname_next_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             char* mask, int number_of_bits);

>         classname_indexname_prefix_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             char* mask, int number_of_bits);

>         classname_indexname_longest_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             char* mask, int number_of_bits);

>         classname_indexname_exact_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             char* mask, int number_of_bits);

> Here `mask` is a key value that is a packed bit array (each byte contains 8 bits).

> ### Cursors and Searches

> The standard cursor positioning functions `mco_cursor_first(), mco_cursor_last(), mco_cursor_next()` and `mco_cursor_prev()` are used to iterate through the result sets of `patricia` index searches. But instead of a `_search()` function, searches are performed using one of the generated `_match()` functions. Initially a cursor's position is not defined and needs to be set with a lookup or a call of `mco_cursor_first()` or mco_cursor_last().

> Please refer to the Searches page for implementation details.

> ## RTree Indexes

> As explained in the R-Tree Index page, an `rtree` index is commonly used to speed spatial searches. An `rtree` index is typically defined for an array field containing the number of coordinates required to describe a "rectangle". For example:

>         class rtree_class
>         {
>             int2 	square[4];

>             rtree <square>  ridx;
>         };

> The `rtree` index-related functions generated for this class will be as follows:

>         MCO_RET  rtree_class_ridx_index_cursor( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET  rtree_class_ridx_search       ( mco_trans_h t_, /*IN*/ MCO_OPCODE op_,
>                             /*INOUT*/ mco_cursor_h c_,
>                             const int2* square );

> ### Cursors and Searches

> As explained in the R-Tree Index page, `rtree` searches are performed using the generated `_search()` function with one of the four search operation opcodes: `MCO_EQUAL, MCO_CONTAIN, MCO_OVERLAP` or `MCO_NEIGHBORHOOD`. Please refer to the Searches page for implementation details.

> ## KDTree Indexes

> As explained in the KD-Tree Index page, `kdtree` indexes are ideal for multi-dimensional key value searches. The `kdtree` index is defined in the schema using the `kdtree` declaration. For example:

>         class Car
>         {
>             string vendor;
>             string model;
>             string color;
>             uint4 year;
>             uint4 mileage;
>             boolean automatic;
>             boolean ac;
>             uint4 price;
>             char<3> state;
>             string description;

>             kdtree <year, mileage, color, model, vendor, automatic, ac, price> index;
>         };

> The `kdtree` index-related functions generated for this class will be as follows:

>         MCO_RET      Car_index_index_cursor            ( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET      Car_index_search                  ( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                         /*IN*/ Car * range_start, /*IN*/ Car * range_end );

>         MCO_RET      Car_index_locate                  ( mco_trans_h t, /*OUT*/ mco_cursor_h c, Car * handle);

> ### Cursors and Searches

> As explained in the KD-Tree Index page, searches with `kdtree` indexes are performed using the Query-By-Example approach. Once a selection of database objects are found by the search operation, the standard cursor positioning functions `mco_cursor_first(), mco_cursor_last(), mco_cursor_next()` and `mco_cursor_prev()` are used to iterate through the result sets. Please refer to the Searches page for implementation details.

> ## Trigram Indexes

> As explained in the Trigram Index page, `trigram` indexes are ideal for text searches when the exact spelling of the target object is not precisely known. The `trigram` index is defined in the schema using the `trigram` declaration. For example:

>         class anObject
>         {
>             uint4 id;
>             string text;

>             trigram<text> trigramIdx;
>         };

> The `trigram` index-related functions generated for this class will be as follows:

>         MCO_RET      anObject_trigramIdx_index_cursor           ( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET      anObject_trigramIdx_search                 ( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                                 MCO_OPCODE op_,
>                                 const char *text_key_,
>                                 uint2 sizeof_text_key_ );

>         MCO_RET      anObject_trigramIdx_locate                 ( mco_trans_h t, /*OUT*/ mco_cursor_h c,
>                                 anObject * handle);

> ### Cursors and Searches

> As explained in the Trigram Index page, searches with `trigram` indexes are performed using the generated `_search()` function. Once a selection of database objects are found by the search operation, the standard cursor positioning functions `mco_cursor_first(), mco_cursor_last(), mco_cursor_next()` and `mco_cursor_prev()` are used to iterate through the result sets. Please refer to the Searches page for implementation details.

> ## Hash Indexes

> As explained in the Hash and Autoid Indexes page, `hash` indexes are ideal for quick lookup of individual database objects. `Hash` indexes require an extra parameter, expected-number-of-entries, following the index name. It is an integer number that the runtime uses to allocate the initial hash table for the index. It must be specified but is not required to be exact. For example:

>         class Record
>         {
>             uint4  iIdx;                  /* Index      */
>             uint4  iSeries;							  /* Series of measurement */

>             hash <iIdx>  I_Index[10000];
>             nonunique hash <iSeries> I_Series[10000];
>         };

> Note that the `[10000]` specification for indexes `I_Index` and `I_Series` cause the runtime to allocate initial hash tables with space for `10000` key values.

> The index-related functions generated for the (unique) `hash I_Index` in this class will be as follows:

>         MCO_RET      Record_I_Index_index_cursor           ( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET      Record_I_Index_compare                ( mco_trans_h t, mco_cursor_h c, uint4 iIdx_key_,
>                             /*OUT*/ int *result_ );

>         MCO_RET      Record_I_Index_find                   ( mco_trans_h t, uint4 iIdx_key_, /*OUT*/ Record *handle_);

> The index-related functions generated for the `nonunique hash I_Series` in this class will be as follows:

>         MCO_RET      Record_I_Series_index_cursor          ( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET      Record_I_Series_compare               ( mco_trans_h t, mco_cursor_h c, uint4 iSeries_key_,
>                             /*OUT*/ int *result_ );

>         MCO_RET      Record_I_Series_search                ( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                             uint4 iSeries_key_ );

> Note that for `unique hash` indexes the exact match `_find()` function is generated, but for `nonunique hash` indexes the `_search()` function is generated.

> ### Dynamic Hash Table Allocation

> An eXtremeDB C API feature called dynamic hash can be enabled to automatically re-allocate the hash table as needed. This will avoid (a) long collision chains if the estimated number of objects is too small, or (b) wasting memory in the case of an overly cautious large estimate.

> Note that when dynamic hash is enabled it applies to all `hash` indexes in the database as well as to the `hash` table maintained for `oid` and `autoid` indexes.

> The `dynamic_hash` parameter (a boolean value) passed to mco_db_open_dev() in the `mco_db_params_t` structure determines if dynamic hash table extending is enabled or not (it is enabled by default). The initial hash table is allocated using two values: the estimated number of objects specified for this class in the database schema, and the `hash_load_factor` parameter (a percentage value, 100% by default), also part of the `mco_db_params_t` structure. The initial hash table is estimated `number of objects * 100 / hash_load_factor`. (the value of zero for `hash_load_factor` signifies the default value of 100%).

> So if `hash_load_factor` is 100 (ie. 100%), then the initial size of the hash table is the specified estimated number of objects. If `hash_load_factor` is 50, then initial size of hash table is twice the estimated number of objects. And if `hash_load_factor` is 200, then the initial size of the hash table is half of the estimated number of objects.

> When dynamic hash is enabled the `hash_load_factor` parameter is used to determine when to extend (reallocate) the hash table. For example, if the initial hash table size is 1000 and the `hash_load_factor` is 50, then the hash table will be extended when the 501st object is inserted; if the `hash_load_factor` is 150, then the hash table will be extended when the 1501st object is inserted.

> If it is important to impose a definite limit on the size of the hash table, the application can disable dynamic hash by setting the dynamic_hash parameter to false. This will have the consequence that more hash conflicts can occur, so the application must be willing to pay that performance cost.

> ### Cursors and Searches

> As explained in the Hash and Autoid Indexes page, "exact match" searches with `unique hash` indexes are performed using the `_find()` function. For `nonunique hash` indexes the `_search()` function is used and once a selection of database objects are found by the search operation, the standard cursor positioning functions `mco_cursor_first(), mco_cursor_last(), mco_cursor_next()` and `mco_cursor_prev()` are used to iterate through the result sets. Please refer to the Searches page for implementation details.

> ## OID Indexes

> Whether an `oid` is provided by an external source or retrieved with an object as a reference to another object in the database, in C applications, an `oid` can be used to quickly retrieve the object it identifies. Uniqueness is enforced during object creation by the eXtremeDB runtime and an `oid` index can be used to establish relationships between classes in the database.

> For example, to establish a relationship between an Order and Execution using `oids`, consider the following schema:

>         struct OrderId
>         {
>             uint4	Id;
>         };

>         declare oid OrderId[1000]; // 1000 is the number of expected values (database-wide)

>         class Order
>         {
>             . . .
>             vector <ref> Executions;
>             . . .
>         };

>         class Execution
>         {
>             float	quantity;
>             oid;
>             . . .
>         };

> Note that the `declare` statement is used to identify a unique object identifier with the expected number of objects that will be stored with an `oid`. The expected-number-of-entries is used by the runtime to calculate the initial hash table size for the index. (See the Hash Indexes section for an explanation of how to specify static and dynamic hash tables and how they are managed by the eXtremeDB runtime.)

> Once the `oid` structure is declared, classes with this unique identifier can then be declared (as in the Execution class above). The runtime maintains an internal index referencing all objects of such classes. Objects can reference each other by `oid` using the `ref` data type (as in the Order class above).

> The `oid` must be a user-defined structure, even if the `oid` has a single field. Each `oid` value must be unique for the entire database and only one `oid` declaration is allowed within a database schema. And only one `oid` statement is allowed per class. `Oids` must be assigned a value that is unique in the entire database (not just in the class, as is the case for hash and unique tree indexes).

> In this example, the class Order contains a variable length array (a `vector`) `Executions` of references to `oids` of the class Execution. Each element of the `vector` is the `oid` of an instance of the class Execution and can be used to quickly reference (locate) the associated Execution object.

> (For another example of creating `oid` references please refer to the Class Relationships page.)

> ### OID Structures and Functions

> If an `oid` is declared for the database, the `mcocomp` schema compiler generates a C structure corresponding to the structure defining the `oid`. For instance, for a schema containing an `oid` declaration like the following:

>         declare database my_db;

>         struct structname
>         {
>             uint4 num_in;
>         };

>         declare oid structname[10000];

> the following definitions and functions will be generated:

>         typedef struct my_db_oid__
>         {
>             uint4           num_in;
>         }

>         my_db_oid;

>         static const uint2 my_db_oid_size = sizeof(my_db_oid);

>         MCO_RET  my_db_delete_object( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid * oid );

>         MCO_RET my_db_get_class_code( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid * oid,
>                             /*OUT*/ uint2 *classcode );

> Note the prepended "my_db" in these definitions. This evidences the fact that only one `oid` can be defined for a given database and uniqueness for all values of that `oid` will be enforced by the eXtremeDB runtime. The `_delete_object()` function deletes an object based on its `oid`. The `_get_class_code()` function returns an integer that identifies the class of the object referenced by a specified `oid` value.

> For classes containing an `oid`, the following functions are generated to create an object, locate an object based on its `oid` and to extract the `oid` of an object:

>         MCO_RET classname_new( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid *id,
>                             /*OUT*/ classname *handle );

>         MCO_RET classname_oid_find( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid *id,
>                             /*OUT*/ classname *handle );

>         MCO_RET  classname_oid_get( /*IN*/ classname *handle, /*OUT*/ my_db_oid *id );

> ### Cursors and Searches

> Only "exact match" searches are possible for `oid` which are performed using the `_oid_find()` function. Please refer to the Searches page for implementation details.

> ## Autoid Indexes

> The `autoid` is a guaranteed unique value generated by the eXtremeDB runtime. For C applications, the `autoid` is declared within the DDL class definition with a specified number of estimated objects of that class. An `autoid` index can be used to establish relationships between classes in the database. (For an example of creating `autoid` references please refer to the Class Relationships page.)

> When a class is declared to have an `autoid`, the schema compiler will generate the following two functions for the class:

>         MCO_RET classname_autoid_find( /*IN*/ mco_trans_h t, /*IN*/ autoid_t id,
>                             /*OUT*/ classname *handle );

>         MCO_RET classname_autoid_get( /*IN*/ classname *handle, /*OUT*/ autoid_t *id );

> When an object of a class with an `autoid` is created, the runtime generates and inserts a unique `autoid` value for the object into an internally maintained `hash` index. This value can be retrieved with the generated `_autoid_get()` function for the given class. An `autoid` value, whether stored in a program variable or a field of another database object as a reference, can be used in the `_autoid_find()` function to locate the referenced object as demonstrated in the following code snippet:

>         Schema snippet:

>         class referenced
>         {
>             ...
>             autoid[4000];
>             ...
>         };

>         class referencing
>         {
>             ...
>             autoid_t refd_object;
>             ...
>         };

>         Application code snippets:

>         autoid_t	id;

>         mco_trans_start( db, MCO_READ_WRITE, MCO_TRANS_FOREGROUND, &t );

>         // create new object, autoid assigned by system
>         referenced_new( t, &refd_obj );

>         // get the autoid value
>         referenced_autoid_get( &refd_obj, &id );
>         rc = referencing_new( t, &refg_obj ); // create new object

>         // store ref’d autoid in it
>         referencing_refd_object_put( &refg_obj, id );
>         rc = mco_trans_commit( t );

>         /* object retrieval */
>         rc = mco_trans_start( db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t );

>         /* first locate a ‘referencing’ object by some method (not shown) */
>         // get the autoid of the referenced obj
>         referencing_refd_object_get( &refg_obj, &id );

>         // locate the referenced object
>         rc = referenced_autoid_find( t, id, &refd_obj );
>         rc = mco_trans_commit( t );

> This snippet shows the process of creating a new object of a class that has an `autoid` defined, retrieving the system-assigned `autoid` value, and storing that value in a field of an object of the class that references it. Later, the `autoid` value is extracted from the referencing object and used to locate the referenced object through its `_autoid_find()` function.

> Note that the internal nature of the `autoid` value is not defined. The only defined property of an `autoid` is that it has size of 8 bytes and it is unique. An application should not rely on or expect numeric values of the `autoid` field.

> ### Cursors and Searches

> Only "exact match" searches are possible for `autoid` which are performed using the `_autoid_find()` function. Please refer to the Searches page for implementation details.

> ## User-defined Indexes

> For C applications, `tree` and `hash` indexes can be declared in the schema definition as `userdef`, in which case the application must provide custom compare functions that are used by the database runtime when building the index and during lookup. In addition, for `hash` indexes, the application must provide the custom hash functions. For example, consider the following sample schema:

>         declare database mydb;

>         class Obj
>         {
>             unsigned<4> first_part1;
>             usigned<2>  first_part2;
>             unsigned<4> second_part1;
>             signed<2>   second_part2;
>             string      data;

>             userdef hash <first_part1,  first_part2>   first[1000];
>             userdef tree <second_part1, second_part2> second;
>         };

> The following index-related functions are generated for `userdef hash` index `first`:

>         MCO_RET      Obj_first_index_cursor                ( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET      Obj_first_compare                     ( mco_trans_h t, mco_cursor_h c, uint4 first_part1_key_,
>                         uint2 first_part2_key_, /*OUT*/ int *result_ );

>         MCO_RET      Obj_first_find                        ( mco_trans_h t, uint4 first_part1_key_, uint2 first_part2_key_,
>                         /*OUT*/ Obj *handle_);

> And for `userdef tree` index `second`:

>         MCO_RET      Obj_second_index_cursor               ( mco_trans_h t, /*OUT*/ mco_cursor_h c );

>         MCO_RET      Obj_second_search                     ( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
>                         MCO_OPCODE op_, uint4 second_part1_key_, int2 second_part2_key_ );

>         MCO_RET      Obj_second_compare                    ( mco_trans_h t, mco_cursor_h c, uint4 second_part1_key_,
>                             int2 second_part2_key_, /*OUT*/ int *result_ );

>         MCO_RET      Obj_second_locate                     ( mco_trans_h t, /*OUT*/ mco_cursor_h c,
>                         Obj * handle);

> For the `tree` index `second` it is necessary to implement two functions: the object-to-object and object-to-key compare functions. These functions return a negative, zero or a positive value depending on whether the first parameter is less than, equal to, or greater than the second parameter. For `hash` indexes, it is necessary to implement `<classname_indexname>_hash_obj()` and `<classname_indexname>_hash_ext()` functions in addition to the compare functions, which in this case return zero if the first and the second parameters are equal and non-zero otherwise. The function prototypes are generated by the schema compiler and are placed into a file named `dbname_udf.c` (where `dbname` is the database name in the `declare database` statement).

> For example, for the above sample schema the following `mydb_udf.c` file would be generated:

>         #include "mydb.h"
>         #include "mcowrap.h"

>         /*
>         * API for the user-defined index "first"
>         */
>         /* object-to-object user-defined compare function */
>         int2  Obj_first_compare_obj ( Obj * handle1, Obj * handle2 )
>         {
>             /* TODO: add your implementation here */
>             return 0;
>         }

>         /* object-to-key user-defined compare function */
>         int2  Obj_first_compare_ext ( Obj * handle, void ** key )
>         {
>             /* TODO: add your implementation here */
>             return 0;
>         }

>         /* user-defined object hash function */
>         uint4 Obj_first_hash_obj    ( Obj * handle )
>         {
>             /* TODO: add your implementation here */
>             return 0;
>         }

>         /* user-defined key hash function */
>         uint4 Obj_first_hash_ext    ( void ** key)
>         {
>             /* TODO: add your implementation here */
>             return 0;
>         }

>         /*
>         * API for the user-defined index "second"
>         */
>         /* object-to-object user-defined compare function */
>         int2  Obj_second_compare_obj ( Obj * handle1, Obj * handle2 )
>         {
>             /* TODO: add your implementation here */
>             return 0;
>         }

>         /* object-to-key user-defined compare function */
>         int2  Obj_second_compare_ext ( Obj * handle, void ** key )
>         {
>             /* TODO: add your implementation here */
>             return 0;
>         }

> If a file `dbname_udf.c` already exists (for example if the schema was previously compiled, then modified and compiled again), the DDL compiler will generate a file `dbname_udf.c.new` and display a warning. In this case, it is the responsibility of the programmer to decide which file should be included in the project. If the user-defined indexes have been changed, then the `.new` file should be renamed to the `.c` file.

> The custom user-defined compare functions are always called within the context of a transaction. So it is okay to access the object fields using the generated `_get()` functions. To access key fields within the index structure, supplementary macro definitions are generated in the `dbname.h` file. For the example schema above these would be as follows:

>         #define Obj_first_extkey_first_part1(k)
>         #define Obj_first_extkey_first_part2(k)
>         #define Obj_second_extkey_second_part1(ek)
>         #define Obj_second_extkey_second_part2(ek)

> A sample implementation for the custom compare and hash functions could be as follows:

>         /* object-to-object user-defined compare function */
>         int2  Obj_first_compare_obj ( Obj * handle1, Obj * handle2 )
>         {
>             uint4 o1_first_part1, o2_first _part1;
>             uint2 o1_first _part2, o2_first _part2;

>             Obj_first_part1_get( handle1, &o1_first_part1 );
>             Obj_first_part1_get( handle2, &o2_first_part1 );
>             if (o1_first_part1 != o2_first_part1)
>                 return 1;

>             Obj_first_part2_get( handle1, &o1_first_part2 );
>             Obj_first_part2_get( handle2, &o2_first_part2 );
>             if (o1_first_part2 != o2_first_part2)
>                 return 1;

>             return 0;
>         }

>         /* object-to-key user-defined compare function */
>         int2  Obj_first_compare_ext ( Obj * handle, void ** key )
>         {
>             uint4 o_first_part1;
>             uint2 o_first_part2;

>             Obj_first_part1_get( handle, &o_first_part1 );
>             if ( o_first_part1 != Obj_first_extkey_first_part1(key) )
>                 return 1;

>             Obj_first_part2_get( handle, &o_first_part2 );
>             if ( o_first_part2 != Obj_first_extkey_first_part2(key) )
>                 return 1;

>             return 0;
>         }

>         /* user-defined object hash function */
>         uint4 Obj_first_hash_obj    ( Obj * handle )
>         {
>             uint4 o_first_part1;
>             uint2 o_first_part2;
>             uint4 hash;

>             Obj_first_part1_get( handle, &o_first_part1 );
>             Obj_first_part2_get( handle, &o_first_part2 );
>             hash = (o_first_part1*1000+o_first_part2) / 1000;
>             return hash;
>         }

>         /* user-defined key hash function */
>         uint4 Obj_first_hash_ext ( void ** key)
>         {
>             uint4 hash;

>             hash = ( Obj_first_extkey_first_part1(key)*1000 +
>                 Obj_first_extkey_first_part2(key)) / 1000;
>             return hash;
>         }

>         /*
>         * API for the user-defined index "second"
>         */
>         /* object-to-object user-defined compare function */
>         int2  Obj_second_compare_obj ( Obj * handle1, Obj * handle2 )
>         {
>             uint4 o1_second_part1, o2_second _part1;
>             uint2 o1_second _part2, o2_second _part2;

>             Obj_second_part1_get( handle1, &o1_second_part1 );
>             Obj_second_part1_get( handle2, &o2_second_part1 );

>             Obj_second_part2_get( handle1, &o1_second_part2 );
>             Obj_second_part2_get( handle2, &o2_second_part2 );

>             if ( o1_second_part1 < o2_second_part1 )
>                 return -1;

>             if ( o1_second_part1 > o2_second_part1 )
>                 return 1;

>             if ( o1_second_part2 < o2_second_part2 )
>                 return -1;

>             if ( o1_second_part2 > o2_second_part2 )
>                 return 1;

>             return 0;
>         }

>         /* object-to-key user-defined compare function */
>         int2  Obj_second_compare_ext ( Obj * handle, void ** key )
>         {
>             uint4 o_second_part1;
>             uint2 o_second_part2;

>             Obj_second_part1_get( handle, &o_second_part1 );
>             Obj_second_part2_get( handle, &o_second_part2 );

>             if ( o_second_part1 < Obj_second_extkey_second_part1(key) )
>                 return -1;

>             if ( o_second_part1 > Obj_second_extkey_second_part1(key) )
>                 return 1;

>             if ( o_second_part2 < Obj_second_extkey_second_part2(key) )
>                 return -1;

>             if ( o_second_part2 > Obj_second_extkey_second_part2(key) )
>                 return 1;

>             return 0;
>         }

> ### Cursors and Searches

> As seen above, for `userdef hash` indexes, only a `_find()` function is generated. But for `tree` indexes, both a `_find()` and a `_search()` function are generated.

> The standard cursor positioning functions `mco_cursor_first(), mco_cursor_last(), mco_cursor_next()` and `mco_cursor_prev()` are used to iterate through the result sets. Please refer to the Searches page for implementation details.