Menu
Simba Technologies
Simba Technologies

SimbaEngine X SDK 10.1.3
Developing Drivers for Data Stores Without SQL

SimbaEngine X SDK Documentation > SQL Data Types > ODBC Custom SQL Data Types

Adding Custom SQLDataType

Using the C++ SimbaEngine X SDK, you can add custom SQLDataTypes to your DSII. Each custom data type that you add must be based on an existing data type. This allows applications to handle your custom types transparently without requiring additional logic.

This functionality is available to drivers that use the SQL Engine, and to those that do not.

Example:

You can add a type called Money that is based on Numeric, but is restricted to two decimal places. It may also contain a custom conversion to character types that adds the currency character.

SimbaEngine X SDK uses the following classes to handle custom SQLDataTypes:

  • UtilityFactory class create a SqlTypeMetadataFactory object, which creates the metadata about the custom types.
  • SqlDataFactory creates the object which represents the custom type.
  • SqlConverterFactory converts the custom type to other data types.

This functionality is explained more in the instructions below.

To Add a Custom SQLDataType:

  1. Modify your custom DSIIDriver object to override and implement the virtual method CreateUtilityFactory. In this method, return a CustomerDSIIUtilityFactoryClass. This class provides the other factories that implement custom data type behavior.
  2. Create a CustomerDSIIUtilityFactory class, which subclasses Simba::Support::UtilityFactory. This factory class provides classes that handle the custom type metadata, data, and conversion of the custom data types.
    1. CreateSqlConverterFactory() creates a factory to create converters that convert custom data types to other types.
    2. CreateSqlDataFactory() creates a factory to create the actual SqlData objects that represent the custom data types.
    3. CreateSqlTypeMetadataFactory() creates a factory to create the metadata about the custom data types.
  3. Create a CustomerDSIISqlConverterFactory class which subclasses Simba::Support::SqlConverterFactory, and override and implement the following virtual methods:
    1. CreateNewCustomSqlToCConverter() – Takes a SqlData and SqlCData object representing the source and target types, and an IWarningListener for posting any conversion warnings to. The returned converter is responsible for converting from the source SQL data type to the target C data type.
    2. CreateNewCustomCToSqlConverter() – Takes a SqlCData and SqlData object representing the source and target types, and an IWarningListener for posting any conversion warnings to. The returned converter is responsible for converting from the source C data type to the target SQL data type.
  4. Create a CustomerDSIISqlDataFactory class which subclasses Simba::Support::SqlDataFactory, and override and implement the following virtual methods:
    1. CreateNewCustomSqlData() – Takes a SqlTypeMetadata object representing a SQL data type, which is used to determine what SqlData object to create. Return a subclass of SqlData that represents the custom type if supported, otherwise return NULL.
  5. Create a CustomerDSIISqlTypeMetadataFactory class which subclasses Simba::Support::SqlTypeMetadataFactory, and override and implement the following virtual methods:
    1. CreateNewCustomSqlTypeMetadata() – Create a new SqlTypeMetadata object that represents the custom data type specified. The helper function SetupStandardMetadata is provided to set up the standard type metadata for the standard SQLDataTypes. Return NULL if the specified type is not supported.
    2. SetCustomTypeDefaults() – Set the default metadata for the specified data type on the specified SqlTypeMetadata object. This allows for reuse of existing SqlTypeMetadata objects, rather than creating new objects.
  6. Ensure that the custom data types are reported in the metadata source for type information. In particular, the DSI_USER_DATA_TYPE_COLUMN_TAG should return the custom type identifier.

    If your driver is using the Simba SQLEngine, then you can customize the behavior of the data types within the SQLEngine as well by making the following changes:

  7. In the CustomerDSIISqlConverterFactory class, override and implement the following virtual methods:
    1. CreateNewSqlToSqlConverter() – Takes a SqlData and SqlData object representing the source and target types, and an IWarningListener for posting any conversion warnings to. The returned converter is responsible for converting from the source SQL data type to the target SQL data type.
  8. Modify your CustomerDSIIDataEngine object to override and implement the virtual method CreateBehaviorProvider() to return a CustomerDSIIBehaviorProvider. This class will provide the other factories that provide the custom data type SQLEngine behavior.
  9. Create a CustomerDSIIBehaviorProvider class which subclasses Simba::SQLEngine::DSIExtCustomBehaviorProvider. This factory class will initialize classes that handle the type coercion and custom type behavior within the SQLEngine. In this context, type coercion is the conversion of one type to a new type with similar content, where the conversion happens automatically.
    1. InitializeCellComparatorFactory() – Initializes the m_cellComparatorFactory member with a CustomerDSIICellComparatorFactory that implements ICellComparator for comparing two data type values.
    2. InitializeCoercionHandler() – Initializes the m_coercionHandler member with a CustomerDSIICoercionHandler that implements ICoercionHandler for coercing different data types into one data type.
    3. InitializeCollatorFactory() – Initializes the m_collatorFactory member with a CustomerDSIICollatorFactory that implements ICollatorFactory for comparing/collating text.
    4. InitializeFunctorFactory() – Initializes the m_functorFactory member with a CustomerDSIIFunctorFactory that implements IFunctorFactory for returning functors that perform operations on specific data types.
    5. Note:

      The behavior provider does not need to override all of the functions. You only need to override the functions that need to be customized.

  10. If custom comparisons are needed, create a CustomerDSIICellComparatorFactory class which implements ICellComparatorFactory. The factory should override and implement the following virtual functions:
    1. MakeNewCellComparator – Create a cell comparator that can compare two values of the type specified by the passed in SqlTypeMetadata. The cell comparator should be able to do comparisons such that it can determine when two values are equal, or one is greater than the other. Return NULL if the comparison is not supported.
  11. If custom coercions between two types are needed, create a CustomerDSIICoercionHandler class which extends DSIExtCoercionHandler. The handler should override and implement the virtual functions with signature that are similar to the following:
    1. Coerce*Type() – For example, CoerceLikeType. Takes SqlTypeMetadata objects and coerces them to create one result SqlTypeMetadata that would result from the operation that the metadata is being coerced for. Return NULL if the coercion is not supported.
    2. Coerce*ColumnMetadata() – Takes ColumnMetadata objects and coerces them to create one result ColumnMetadata that would result from the operation that the metadata is being coerced for. Return NULL if the coercion is not supported.
     
  12. If custom collations are needed, create your own collation interface by extending ICollation. Override and implement the required virtual functions as shown in ICollation.h.

    You can choose to implement the CreateHasher method in the ICollation interface. This method returns an IHasher interface that the SimbaEngine SDK uses to perform join operations. By default, ICollation::CreateHasher returns null.

    If you implement your own ICollation and your own IHasher, queries that contain equality joins on your custom type columns will complete more quickly, because SimbaEngine can use a hash-based join algorithm.

    Follow these guidelines when implementing IHasher::Hash:

    • Return a uint64 value for each string buffer that is passed in.
    • Always return the same value for the same string buffer and/or seed value input.
    • For optimal performance, return unique values for each different string buffer and seed input. That is, try to prevent collisions in your hash implementation.
    • For optimal performance, create return values that evenly span a uint_64 range. That is, the return values should have a uniform and independent distribution of bits.

    For an example IHasher implementation, see DSIUnicodeHasher or DSIBinaryHasher. These classes use the Murmur hash function, which satisfies the requirements listed above. An alternative to using Murmur is to use the DSIBinaryHasher on a collation normalized buffer, also called a “collation key”.

  13. If custom operation behavior is needed, create a CustomerDSIIFunctorFactory class which extends DSIExtFunctorFactory. The factory should override and implement the following virtual functions:
    1. CreateBinaryArithmeticFunctor() – Create a new DSIExtBinaryValueFunctor subclass which handles the specified arithmetic operation of two values of the type specified by the SqlTypeMetadata. The DSIExtBinaryValueFunctor should operate on the m_leftData and m_rightData member values during Execute().
    2. CreateComparisonFunctor – Create a new DSIExtBinaryBooleanFunctor subclass which handles the specified comparison between two values of the type specified by the SqlTypeMetadata. The DSIExtBinaryBooleanFunctor should operate on the m_leftData and m_rightData member values during Execute, and use the ICollator supplied by the ICollatorFactory if needed.
    3. CreateNegationFunctor – Create a new DSIExtUnaryValueFunctor subclass which handles negation of the type specified by the SqlTypeMetadata. The DSIExtUnaryValueFunctor should operate on the m_data member during Execute.
    4. CreateExistsFunctor – Create a new DSIExtBinaryValueFunctor subclass which handles the EXISTS clause for the type specified by the SqlTypeMetadata. The DSIExtBinaryValueFunctor should operate on the m_leftData and m_rightData member values during Execute.
    5. CreateInFunctor – Create a new DSIExtBinaryValueFunctor subclass which handles the IN clause for the type specified by the SqlTypeMetadata. The DSIExtBinaryValueFunctor should operate on the m_leftData and m_rightData member values during Execute().
    6. CreateLikeFunctor() – Create a new DSIExtBinaryValueFunctor subclass which handles the LIKE clause for the type specified by the SqlTypeMetadata. The DSIExtBinaryValueFunctor should operate on the m_leftData and m_rightData member values during Execute().