Plugin SDK Database Reference
From Real Software Documentation
Writing Real Studio Database Plug-Ins
Database plugins are a bit more complex than most other types of Real Studio plug-ins. The database plugin API is described here. All the general plugin comments in the Plugin SDK Documentation apply here as well.
A database plugin may register the following entities:
- Database Engine
- Database Cursor
- Database Source
- Database Source Interface
- Required Data Structures
The plugin header files make use of three data structures which are not defined. You must define these as appropriate for your own particular database; Real Studio doesn't care what you put in them. The three structures you must define are:
- struct dbDatabase
- struct dbTable
- struct dbCursor
Registering a Database Engine
The REALRegisterDBEngine function passes a reference to a REALdbEngineDefinition structure. This is the set of functions which operate primarily on a dbDatabase object, and make up the core of the database functionality.
The fields of the REALdbEngineDefinition structure are as follows:
- version: the version of the plugin architecture that the control was built under; just pass the constant kCurrentREALControlVersion
- forSystemUse: just pass 0 (zero)
- flags1:
- flags2:
- flags3:
- closeFunc: pointer to a function with prototype: closeDB( dbDatabase* database );
- getTableSchemaCursorFunc: pointer to a function with prototype: REALdbCursor getTableCursor(dbDatabase *database, REALstring tableName, REALgetColumn *columns, REALcolumnConstraints *constraints);
- getFieldSchemaCursorFunc: pointer to a function with prototype : REALdbCursor getFieldSchema(dbDatabase *database, REALstring tableName);
- directSQLSelectFunc: nil, or a pointer to a function with prototype : REALdbCursor directSQLSelect(dbDatabase *, REALstring selectString);
- directSQLExecuteFunc: nil, or a pointer to a function with prototype : void directSQLExecute(dbDatabase *, REALstring executeString);
- createTableFunc: pointer to a function with prototype: void createTable(dbDatabase *database, REALstring name, REALnewColumn *columns, unsigned char *primaryKey, int primaryKeyCount);
- addTableRecordFunc: pointer to a function with prototype: void addRecord(dbDatabase *database, REALstring tableName, REALcolumnValue *values);
This function gets called when the InsertRecord method of the Database class is invoked. Note that this is different from calling the SQL statement "INSERT INTO ..." since the InsertRecord method is now responsible for correctly formatting the SQL string. If no implementation is provided, a default SQL INSERT string is passed to SQLExecute, however, some databases cannot handle our default formatting, so you can implement this function to override that feature.
- getTableCursorFunc: pointer to a function with prototype: REALdbCursor getTableCursor(dbDatabase *database, REALstring tableName, REALgetColumn *columns, REALcolumnConstraints *constraints);
- updateFieldsFunc: nil, or a pointer to a function with prototype: void updateFields(dbDatabase *, REALfieldUpdate *fields);
- addTableColumnFunc: nil, or a pointer to a function with prototype: void addTableColumn(dbDatabase *, REALstring, REALnewColumn *);
- getDatabaseIndexesFunc: pointer to a function with prototype: REALdbCursor getDatabaseIndexes(dbDatabase *database, REALstring tableName);
- getLastErrorCodeFunc: nil, or a pointer to a function with prototype: long (*getLastErrorCode)(dbDatabase *);
- getLastErrorStringFunc: nil, or a pointer to a function with prototype: REALstring getLastErrorString(dbDatabase *);
- commitFunc: pointer to a function with prototype:void commitTransaction(dbDatabase *database);
- rollbackFunc: pointer to a function with prototype: void rollbackTransaction(dbDatabase *database);
- getPropertyFunc: nil, or a pointer to a function with prototype: REALstring getProperty(dbDatabase *database, REALstring propertyName);
- getSupportedTypes: nil, or a pointer to a function with prototype: void supportedTypes(long **dataTypes, char **dataNames, long *count);
This function is used in the table schema editor of the IDE. The popupmenu will be populated with the names of the datatypes you provide it, else it will default to the datatypes support by the REALdb.
For Example:
dbTypeLong, dbTypeFloat, dbTypeDouble,
dbTypeBoolean, dbTypeCurrency, dbTypeDate,
dbTypeTime, dbTypeTimeStamp };
static char sPostgreSQLDataNames[] = {"char,varchar,smallint,"
"integer,float4,float8,"
"boolean,money,date,"
"time,timestamp" };
static long sPostgreSQLTypesCount = 11;
static void supportedTypes(long **dataTypes, char **dataNames, long *count)
{
*dataTypes = (long *) sPostgreSQLTypes;
*dataNames = (char *) sPostgreSQLDataNames;
*count = sPostgreSQLTypesCount;
}
Registering a Database Cursor
The REALRegisterDBCursor function passes a reference to a REALdbCursorDefinition structure. This is the set of functions which operate on a database cursor, which is essentially a set of records from a database query. You may register more than one type of cursor, e.g., one for normal queries and one for table schema.
The fields of the REALdbCursorDefinition structure are as follows:
- version the version of the plugin architecture that the control was built under; just pass the constant kCurrentREALControlVersion
- forSystemUse: just pass 0 (zero)
- closeCursorFunc: pointer to a function with prototype:void closeCursor(dbCursor *);
- cursorColumnCountFunc: pointer to a function with prototype: int cursorColumnCount(dbCursor *);
- cursorColumnNameFunc: pointer to a function with prototype: REALstring cursorColumnName(dbCursor *, int column);
- cursorRowCountFunc: nil, or a pointer to a function with prototype: int cursorRowCount(dbCursor *);
- cursorColumnValueFunc: pointer to a function with prototype:void cursorColumnValue(dbCursor *, int column, Ptr *outData, unsigned char *outType, int *outLength);
- This is called when the RB code does "recordset.field("column")" or "recordset.idxfield(1)".
- column -- a 0-based index of a column whose value we want
- outData -- pointer to the column's value
- outType -- a dbFieldType indicating the type of data to expect from outData
- outLength -- specifies the size of outData
- cursorReleaseValueFunc: nil, or a pointer to a function with prototype: void cursorReleaseValue(dbCursor *);
- cursorNextRow: pointer to a function with prototype: Boolean cursorNextRow(dbCursor *);
- cursorDeleteFunc: nil, or a pointer to a function delete the current record; prototype: void cursorDelete(dbCursor *);
- cursorDeleteAllFunc: nil, or a pointer to a function to delete all records in the set; prototype: void cursorDeleteAll(dbCursor *);
- cursorFieldKeyFunc: nil, or a pointer to a mysterious function with prototype: Boolean cursorFieldKey(dbCursor *, int, Ptr *, int *, Ptr *, int *);
- cursorUpdateFunc: nil, or a pointer to a function with prototype: void cursorUpdate(dbCursor *cursor, REALcursorUpdate *fields);
- cursorEditFunc: nil, or a pointer to a function with prototype: void cursorEdit(dbCursor *cursor);
- This is called when the RB code does "cursor.Edit".
- cursorPrevRow: pointer to a function with prototype: void cursorPrevRow(dbCursor *);
- Moves the cursor to the previous record.
- cursorFirstRow: pointer to a function with prototype: void cursorFirstRow(dbCursor *);
- Moves the cursor to the first record.
- cursorLastRow: pointer to a function with prototype: void cursorLastRow(dbCursor *);
- Moves the cursor to the last record.
- cursorColumnType: pointer to a function with prototype:int cursorColumnType(dbCursor *, int index);
- Gets the column type of a specified column (index is 0 based).
- CursorIsBOF: pointer to a function with prototype: Boolean CursorIsBOF(dbCursor *);
- Determines whether or not the cursor is at the beginning of the selection
- CursorIsEOF: pointer to a function with prototype: Boolean CursorIsEOF(dbCursor *);
- Determines whether or not the cursor is at the end of the selection
REALcursorUpdate structure
When the user modifies field values in a cursor, the changes are stored in a REALcursorUpdate struct. This is then passed to your cursorUpdate function. The update data is actually a linked list, defined as:
{
REALcursorUpdate *next;
int fieldIndex;
REALstring columnValue;
};
Your code is responsible for converting the given string data to the appropriate datatype for the backend database.
Subclassing the database class
Since REALbasic 4.5, you can now subclass the database class and provide your own Connection implementation.
For example:
kCurrentREALControlVersion,
"MyNewDatabase",
"Database",
sizeof(dbDatabase),
0,
(REALproc) MyNewDatabaseConstructor,
(REALproc) MyNewDatabaseDestructor,
MyNewDatabaseProperties,
sizeof(MyNewDatabaseProperties) / sizeof(REALproperty),
MyNewDatabaseMethods,
sizeof(MyNewDatabaseMethods) / sizeof(REALmethodDefinition),
nil, // no events
0
};
// You will want to override Database classes' Connect method
REALmethodDefinition MyNewDatabaseMethods[] = {
{ (REALproc) MyNewDatabaseConnect, REALnoImplementation, "Connect() as boolean"},
};
// you need to construct the DB in your constructor method so
// that RB knows about its REALdbEngineDefinition
static void MyNewDatabaseConstructor(REALobject instance)
{
ClassData (MyNewDatabaseClass, instance, dbDatabase, db);
db->mSomeValue = 1; // initialize your data
REALConstructDBDatabase((REALdbDatabase) instance, db, &myNewDBEngine);
}
static Boolean MyNewDatabaseConnect(REALdbDatabase dbObject)
{
// the host, database, user and password properties are
// stored within RB, unless you override them of course.
REALstring host = REALGetDBHost(dbObject);
REALstring database = REALGetDBDatabaseName(dbObject);
REALstring user = REALGetDBUserName(dbObject);
REALstring password = REALGetDBPassword(dbObject);
// You can get back your dbDatabase object with this call
dbDatabase *db = REALGetDBFromREALdbDatabase(dbObject);
// stuff back in the proper connection stuff...
db->mConnection = whatever_connection;
db->mErrorMessage = nillywilly;
db->mTransactionOpen = false;
db->mUsingConnect = true;
// remember to unlock your strings
}
REALConstructDBDatabase
Populates the Database object with the necessary information from the user.
void REALConstructDBDatabase( REALdbDatabase db, dbDatabase *mydb, REALdbEngineDefinition engine );
- db: RB's database object
- mydb: User's database object
- engine: Pointer to database engine definition
Return Value none
REALDBConnectionDialogAddField
Adds a new field to the database connection dialog. This function serves no purpose at runtime.
void REALDBConnectionDialogAddField( REALDBConnectionDialogRef dialogRef, REALstring label, REALstring defaultText, Boolean maskField );
- dialogRef: The reference returned when creating a new connection dialog
- label: The text label displayed beside the input editfield
- defaultText: The default text to be displayed in the input editfield
- maskField: Specifies whether or not the editfield is masked (usually for passwords)
Return Value none
REALDBConnectionDialogCreate
Creates and initializes a new Database Connection Dialog window for the IDE. This serves no purpose at runtime. When a user is adding a data source item to thier project, you can create your own customized connection dialog with this API. Use the ref value that this function returns to add fields to your dialog using REALDBConnectionDialogAddField.
REALDBConnectionDialogRef REALDBConnectionDialogCreate( void );
parameters: none
Return Value a reference to the newly created database connection dialog
REALDBConnectionDialogDelete
Disposes of the database connection dialog. This function serves no purpose at runtime.
void REALDBConnectionDialogDelete( REALDBConnectionDialogRef dialogRef );
- dialogRef: The reference returned from REALDBConnectionDialogCreate
Return Value none
REALDBConnectionDialogShow
Displays the database connection dialog in the IDE. This function serves no purpose at runtime. Show the dialog after creating it and adding all the necessary fields. If the user clicks the OK button, then the values of the fields are concatenated (separated by nulls) and returned as a REALstring. If the user clicks Cancel, then a null REALstring is returned instead.
REALstring REALDBConnectionDialogShow( REALDBConnectionDialogRef dialogRef, REALstring title );
- dialogRef: The reference returned from REALDBConnectionDialogCreate
- title: Title for the connection dialog
Return Value null-delimited string that contains the connection information
REALdbCursorFromDBCursor
This function is used by database plugins to convert an internal dbCursor structure into a REALbasic RecordSet object. Note that the dbCursor pointer you pass is simply stored in the RecordSet object; however you allocated it, you must deallocate it accordingly in your cursor close callback.
REALdbCursor REALdbCursorFromDBCursor( dbCursor *cursor, REALdbCursorDefinition *defn );
- cursor: dbCursor object to wrap in a REALdbCursor (i.e. RecordSet)
- defn: structure defining callbacks for this cursor
Return Value newly created RecordSet object
REALdbDatabaseFromDBDatabase
Constructs a new REALbasic Database object
REALdbDatabase REALdbDatabaseFromDBDatabase( dbDatabase *database, REALdbEngineDefinition *defn );
- database: user's database object
- defn: Pointer to database engine definition
Return Value newly created Database object
REALDesignAddDataSource
Database plugins use this method to add a data source. Generally this is done within the callback registered via REALRegisterDataSourceInterface. Pass in a string containing whatever data you need to relocate the database; you'll get this same string back when your REALRegisterDataSource callback is invoked.
void REALDesignAddDataSource( const char *baseName, const char *szDataSourceName, Ptr data, int dataLen );
- baseName: name of the database as it should appear in the Project window
- szDataSourceName: string constant identifying the database type
- data: arbitrary data to be passed back to your data source getter
- dataLen: length of the arbitrary data
Return Value none
REALGetCursorFromREALdbCursor
Get the user's database cursor object structure from the REALdbCursor structure.
dbCursor *REALGetCursorFromREALdbCursor( REALdbCursor cursor );
- cursor: the REAL database cursor object
Return Value pointer to the user's cursor object
REALGetDBDatabaseName
Gets the database name.
REALstring REALGetDBDatabaseName( REALdbDatabase db );
- db: the database object
Return Value the database name
REALGetDBFromREALdbDatabase
Get the user's database object structure from the REALdbDatabase structure.
dbDatabase *REALGetDBFromREALdbDatabase( REALdbDatabase db );
- db: the Database object
Return Value pointer to the user's database object
REALGetDBHost
Gets the hostname of the database.
REALstring REALGetDBHost( REALdbDatabase db );
- db: Database object
Return Value hostname of the database
REALGetDBPassword
Gets the password to be used to open the database.
REALstring REALGetDBPassword( REALdbDatabase db );
- db: the Database object
Return Value the password
REALGetDBUserName
Gets the user name with which to access the database.
REALstring REALGetDBUserName( REALdbDatabase db );
- db: the Database object
Return Value the user name
REALRegisterDataSource
Used by database plugins to register a callback to be invoked when the system needs to load a database. The string passed in is whatever arbitrary data was given to RB via the call to REALDesignAddDataSource. The callback function takes parameters "Ptr data, int dataLen" and returns a newly created REALdbDatabase.
void REALRegisterDataSource( const char *szDatasourceName, REALDataSourceProc proc );
- szDatasourceName: string constant identifying the database type
- proc: callback function
Return Value none
REALRegisterDataSourceInterface
Used by database plugins to add an appropriate entry to the "Add Data Source" submenu in the IDE. The callback function should provide UI to locate the database, and then package whatever data is needed to relocate it and pass that to REALDesignAddDataSource.
void REALRegisterDataSourceInterface( const char *szMenuName, REALDataSourceInterfaceProc proc );
- szMenuName: text to appear in the Add Data Source submenu
- proc: callback function
Return Value none
REALRegisterDBCursor
Call this method to register a database cursor.
void REALRegisterDBCursor( REALdbCursorDefinition *defn );
- defn: pointer to a database cursor definition
Return Value none
REALRegisterDBEngine
Call this method to register a database engine.
void REALRegisterDBEngine( REALdbEngineDefinition *defn );
- defn: pointer to a database engine definition
Return Value none
REALRegisterDBTable
Call this method to register a database table.
void REALRegisterDBTable( REALdbTableDefinition *defn );
- defn: pointer to a DB table definition
Return Value none
REALSetDBIsConnected
This function is used by database plug-ins to let the runtime know then a database object connects or disconnects from its source (whether that's a network server, a file, or whatever). The database is assumed to be open after a call to REALdbDatabaseFromDBDatabase or REALConstructDBDatabase; if that's not true, call this function to let the runtime know. Similarly, if you open a connection later through some custom method on your database class, call this function to let RB know the database is open.
void REALSetDBIsConnected( REALdbDatabase database, Boolean connected );
- database: database to mark as open or closed
- connected: true if DB connection is open, false if it's closed
Return Value none
