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:
- Session allocator:
SqlEngine::getAllocator()
orMcoSqlSession::getAllocator()
- covers the lifetime of the session (application thread).- Prepared statement allocator:
PreparedStatement::getAllocator()
- covers the lifetime of the prepared statement.- Result-set allocator:
QueryResult::allocator
– covers the lifetime of the result-set.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, tooUsing 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 methodString *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 theRef<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 */