Dynamic Object Allocators in C++

The eXtremeSQL engine manages dynamic data objects like Blob, String, Array etc. with an object of class Allocator as an argument in order to construct (and make a copy of) the dynamic object using memory managed by the Allocator. There are three types of Allocator provided by the eXtremeSQL engine:

Alternatively, the developer can manually create an object of class Allocator via C++ dynamic memory allocation or on the stack and use it to construct SQL dynamic objects.

Any dynamic object created by any allocator can be explicitly deleted at any moment by calling DELETE_OBJ(Allocator *allocator, DynamicObject *object);

All dynamic objects that are not deleted explicitly are alive as long as their Allocator object is alive. And all of these objects are automatically deleted when allocator is destroyed.

Below is a code snippet showing usage of Allocators with explanatory comments:

 
    {
        SqlEngine engine;
        ... // Some initialization code
        {
            QueryResult result( engine->executeQuery(query));
            Cursor* iterator = result->records();
            while ( iterator->hasNext() ) 
            {
                // Locally created Allocator object
                Allocator on_stack_variable_allocator;
                Record* rec = iterator->next();
                 
                // Get the string value of result-set column 0
                // Create object 'on_stack_alloc_string' using
                // locally created Allocator
                String* on_stack_alloc_string = rec->get(0)
                    ->stringValue(&on_stack_variable_allocator);
 
                // Create object 'result_set_alloc_string' using
                // result-set allocator. This is actually field
                // ResultSet::allocator returned by wrapper class
                // QueryResult
                String* result_set_alloc_string = rec->get(0)
                    ->stringValue(result->allocator);
 
                // Create object 'session_alloc_string' using session allocator.
                // Please note that this Allocator object is not re-entrant.
                // So in separated threads methods getAllocator of a
                // specific object of class McoSqlSession must be used.
                String* session_alloc_string = rec->get(0)
                    ->stringValue(engine.getAllocator());
                ...
                // Use string objects here
                 
                // Explicitly delete object on_stack_alloc_string (otherwise its
                // memory would be released at point 1)
                DELETE_OBJ(&on_stack_variable_allocator, on_stack_alloc_string);
 
                // Explicitly delete object result_set_alloc_string (otherwise its
                // memory would be released at point 2)
                DELETE_OBJ(result->allocator, result_set_alloc_string);
 
                // Explicitly delete object session_alloc_string (otherwise its
                // memory would be released at point 3)
                DELETE_OBJ(engine.getAllocator(), session_alloc_string);
 
            } 	// Point 1. Here local Allocator 'on_stack_variable_allocator' is
            // destroyed and all its remaining allocated objects are deleted, too
         
        } 	// Point 2. Here result-set Allocator 'result->allocator' is destroyed and
        // all its remaining allocated objects are deleted, too
         
        ... // Some more code
    } 	// Point 3. Here session Allocator 'engine.getAllocator()' is destroyed and
    // all its remaining allocated objects are deleted, too
     

Using ValueRef and String classes

Alternatively, to work with dynamic objects, the template class ValueRef can be used. This helper class automatically uses the allocator of the original object to construct a copy. For example:

 
    QueryResult result(engine.executeQuery(query));
    Cursor* iterator = result->records();
    while ( iterator->hasNext() ) 
    {
        Record* rec = iterator->next();
        ValueRef nameRef(rec->get(0));
        String * pName = nameRef.as<String>();
        // Object pName is constructed using the result-set Allocator object
    }
     

Specifically about the stringValue() method and String classes, it should be noted that every class inherited from base class Value implements a method String *stringValue(). In all implementations of this method (except for the class String) a new object of type *String is created, and it should be deleted after its use. The class String method returns its "this" pointer instead.

Thus if the application deletes the string object, and then also deletes the object that the current object was inherited from, the object is deleted twice! Also once deleted, it is not possible to use the parent object either.

So, to avoid confusion, instead of using the String *stringValue() method for converting the Value to a string, it is recommended to use the Ref<String> stringRef(Allocator* allocator) method that takes care of correct deletion. There are two ways to use it:

 
    Ref<String> strval(v->stringRef(allocator));
     

or

 
    Ref<String> str = v->stringRef(allocator);
     

The scope and usage of the reference is illustrated as follows:

 
    {
        Ref<String> strval(v->stringRef(allocator));
        /* use a reference as a local object String */
        printf("%s", strval.cstr());
    } 	/* Ref is automatically deleted here */
     

or

 
    {
        Ref<String> str = v->stringRef(allocator);
        /* use reference as a pointer to the String object*/
        printf("%s", str->cstr());
    } 	/* Ref is automatically deleted here */