BTR2SQL SDK¶
Mertech BTR2SQL comes with a software development kit (SDK) that gives you access to extended Btrieve functionality. The SDK contains a runtime dll and migration dll for each backend (MS SQL, Oracle or PostgreSQL). The SDK also contains header files to define the constants and structures required for advanced programming and it contains detailed documentation and examples.
The BTR2SQL SDK is installed in the <Program Files>\Mertech Data Systems\DB Drivers\Btrieve\sdk directory.
Runtime dll with extended APIs¶
During the migration process, you replace the Btrieve runtime dll with the Mertech runtime dll of the same name. For 32-bit applications this is wbtrv32.dll or w3btrv7.dll; for 64-bit applications the equivalents are wbtrv64.dll and w64btrv.dll. The Mertech runtime dll codes the standard Btrieve operations (for example, B_OPEN) so that they will work with the selected SQL backend. This means your application can access the SQL backend without any changes to your existing code or BTRCALL calls.
In addition to the standard Btrieve operations, the Mertech runtime dll includes extended APIs to:
How do I use the extended APIs?¶
The runtime dll is dynamically linked to your application when the application is loaded, so the extended APIs are automatically available and accessible just like the standard Btrieve operations through the BTRV/BTRCALL function interface. Simply use the provided constants (or the operation code) and structures defined in the mds_btrieve header. For example:
#include <mds_btrieve.h>
MDS_SQL_LOGIN loginInfo = {"WIN-P6C\\SQLEXPRESS", "btrtest", "mertech",
"btrtest", ""};
status = BTRCALL(B_SQL_LOGIN, NULL, &loginInfo, &(dataLen=sizeof(loginInfo)), NULL, 0, 0);
Direct call interfaces are also available for some of the extended APIs. For example you can encrypt a password by calling:
#include <mds_btrieve.h>:
status = BTRCALL(B_ENCRYPT_PASSWORD, NULL databuf, &datalen, keybuf, 0, 0);
Or, you can call MdsEncryptPassword directly:
#include <mds_btrieve.h>
status = MdsEncryptPassword(password, encpw, encpwlen);
Migration dll¶
The migration dll (sql_btr.dll, ora_btr.dll, or pgs_btr.dll) includes all the standard and extended Btrieve functions plus functions to write your own migration interface.
How do I use the migration APIs?¶
In order to use the migration APIs, the migration dll must be dynamically linked at runtime using
LoadLibraryand the function addresses must be obtained usingGetProcAddress. This is done for you in theBtrieveFileclass, which is a simple wrapper class capable of loading any dll. TheBtrieveFileclass is defined inBtrieveFile.h. An MS SQL code extract is shown below. For additional examples, see thesamples/migrationsubdirectory.#include <BtrieveFile.h> void useMdsMigrateOneTable() { BtrieveFile* pBF = new BtrieveFile("sql_btr.dll"); MDSRESULT status; static const char ddfLocation[] = "c:\\pvsw\\demodata\\"; static const char tableName[] = "billing"; status = pBF->MdsMigrateOneTable(ddfLocation, tableName, MDS_MIG_FULL, NULL); } //useMigrateOneTable()
Supported Btrieve API Functions¶
BTR2SQL fully supports all Btrieve API functions with the exception of the functions listed below, which are not required in a relational database.
Functions that are not supported
Constant |
Opcode |
Explanation |
|---|---|---|
B_EXTEND |
16 |
Split an existing Btrieve data file over an additional drive. Operation is ignored, status 0 is returned. |
B_SET_OWNER |
29 |
Assign owner name and optionally encrypt Btrieve data file. Operation is ignored, status 0 is returned. Owner names can be implemented through SQL security by porting a file to a different login. |
B_CLEAR_OWNER |
30 |
Clear owner name for a Btrieve data file. Operation is ignored, status 0 is returned. |
B_CONTINUOUS |
42 |
Place the specified file in or remove the file from continuous operation mode, for use in system backups. Operation is ignored, status 0 is returned. |
B_LOGIN |
78 |
Login to a Btrieve database.
|
NOWRITE_WAIT |
500 |
This operation bias code is ignored. |
Additional MDS status codes¶
MDS_errGeneric = 18000 ///Unknown
MDS_errNotLoggedIn = 18001
MDS_errTableNotFound = 18002
MDS_errInvalidTableName = 18003
MDS_errInvalidColumnNumber = 18004
MDS_errInvalidColumnName = 18005
MDS_errInvalidDataType = 18006
MDS_errDuplicateColumnName = 18007
MDS_errInvalidDataSize = 18008
MDS_errInvalidColumnOrder = 18009
MDS_errInvalidIndexName = 18010
MDS_errStringTooShort = 18011
MDS_errDirectoryError = 18012
MDS_errDuplicateTable = 18013
MDS_errISRNotSupported = 18014
MDS_errInvalidDatabaseName = 18015
MDS_errXmlMissingElement = 18016 ///< Required Element missing from XML
MDS_errXmlMissingAttribute = 18017 ///< Required Attribute missing from XML
MDS_errXmlIncorrectDriver = 18018 ///< XML indicated an incorrect driver target
MDS_errXmlInvalidValue = 18019 ///< Invalid value for an XML attribute
Extended DDF Data Types¶
Btrieve natively supports about 28 basic data types.
fieldext.ddfwas introduced to augment this list and support a number of other interesting types. BTR2SQL reads this information and makes a wiser choice for the type used in SQL. For instance, in addition to the standardB_DATEtype, there are several Julian based date formats and dates in various string forms. All of these are migrated to a realDATEtype in SQL instead of a generic string or integer as defined infield.ddf. Pervasive’spedtconv.dllis utilized to do the data conversions. This dll can be found in the Pervasive SDK downloaded from their website.
See also SDK documentation: Modules tab > Btrieve Types
B_CREATE API¶
The B_CREATE syntax was expanded so your application can provide the name of a pre-existing INT file to use as a pattern when creating a new table on the SQL server.
Background¶
The original B_CREATE syntax is:
status = BTRCALL(B_CREATE, NULL dataBuffer, &dataLength, keyBuffer, 0, 0);
Where keyBuffer is <filename>
The only information available to the driver for creating a new file was the filename and indexes. The driver had to examine the indexes to determine there was a field of a particular data type and size at some specific offset in the record. However, the driver could not determine the names of all of the fields, so you might end up with a table such as:
CREATE TABLE cust1_dat (mds_field1 int, mds_filler1 binary(10), mds_field2
varchar(50), mds_filler2 binary(500))
Auto template option¶
B_CREATE was later expanded to make use of an existing int file (filename.int).
When an application issues a B_CREATE, if an INT file already exists for the file, the driver compares its contents with the index definitions in the new create structure. If they match, the driver uses the definition in the INT file to create the table on the server. If the INT file does not look compatible, it is ignored and you are stuck with the MDS_FIELDx and MDS_FILLERx field names.
Several flags control the way the auto template option works. The ‘flag’ becomes the keyNumber for the operation.
Flag |
Description |
|---|---|
B_CM_OVERWRITE_IGNORE_INT_FILE= -6 |
Overwrite the existing table; ignoring the definition in the existing INT file |
B_CM_OVERWRITE_FORCE_INT_FILE = -99 |
Overwrite the existing table; force usage of the existing INT file (ignore FILESPEC in databuffer) |
B_CM_NOOVERWRITE_FORCE_INT_FILE= -100 |
Do not overwrite the existing table; force usage of existing INT file (ignore FILESPEC in dataBuffer) |
The B_CREATE syntax using the auto template option is:
status = BTRCALL(B_CREATE, NULL dataBuffer, &dataLength, keyBuffer, keyNumber, 0);
Where keyNumber is one of the above flags.
INT File template option¶
The latest enhancement to B_CREATE expands the contents of keyBuffer to include the new filename (as before), the template filename, and the table name to be created on the server. Now you can specify an INT file for an existing file that has a pattern you want to use to create the new file. For example you can create bar.btr with the same structure (but different table name) as foo.btr.
The new B_CREATE syntax is:
status = BTRCALL(B_CREATE, NULL dataBuffer, &dataLength, keyBuffer, keyNumber, 0);
Where keyNumber is one of the above flags and keyBuffer is:
<filename> | <template filename> | <table name>
If a pipe character ( | ) appears in the keyBuffer, <template filename> is assumed to be the next (required) string. The second pipe and <table name> are optional. If not included, the driver defaults to filename_ext for the table name.
A space on either side of the pipe is ignored. Quotes around any sub-string are removed. Quotes around the entire string are not supported. Quotes on the interior of any sub-string causes an invalid filename error.
All forms of filenames are supported for both the filename and template parts of the string. This includes just a filename (relative to the “current directory”), current directory relative paths (.pathfilename or ..pathfilename), root relative paths (pathfilename), full paths (c:datafilename) and UNC paths (serversharepathfilename).
Notes¶
An INI file in the folder where the template filename is specified is read. So it is possible for the template INT file to be in a different folder than what is directly specified for the template file.
The template INT should be marked as Permanent.
Example 1¶
Your Magic (or non-Magic) application has a DB table custmain.mcf with table name “Customers Main”. Custmain.mcf exists in C:\MyApp\Data and C:\MyApp\Data\mds.ini has an entry: INT-Folder=..\intfiles
During migration to SQL, table “Customers Main” and C:\MyApp\IntFiles\custmain_mcf.int are created.
The application later wants to access a new customer table with the same definition as “Customers Main”. C:\MyApp\Data is the current folder at runtime.
The application opens
cust1.dat | custmain.mcf | Cust – John Smith.C:\MyApp\IntFiles\cust1_dat.intdoes not exist so the driver returns status 12.Magic then issues a
B_CREATEpassing the same path string as before.The driver reads
C:\MyApp\IntFiles\custmain_mcf.intinto the usual internal file classes and updates the table name to “Cust – John Smith” and the physical name tocust1.dat.The driver then issues a call to Create Table on the SQL backend, creates
C:\MyApp\IntFiles\cust1_dat.int, and returns success.Magic reissues the open and continues successfully.
Example 2¶
You can also use this new B_CREATE option in a multi-xxx (company, customer, site, etc.) environment where you utilize the same schema in multiple folders (one for each customer for instance).
Scenario
c:\
myapp
data
intfiles_mssql (all files marked as Permanent and OS Read/Only attribute set)
people_btr.int
jobs_btr.int
salaries_btr.int
intfiles_ora (all files marked as Permanent and OS Read/Only attribute set)
people_btr.int
jobs_btr.int
salaries_btr.int
cust1
mds.ini (says intfiles=..intfiles_mssql and ..intfiles_ora, prefix=cust1_)
cust2
mds.ini (says intfiles=..intfiles_mssql and ..intfiles_ora, prefix=cust2_)
cust3
mds.ini (says intfiles=..intfiles_mssql and ..intfiles_ora, prefix=cust3_)
When either B_OPEN or B_CREATE references one of the customer files (for example, c:\myapp\data\cust1\people.btr), the driver looks at the INI file to determine where the INT files are located and then opens the INT file for the required server type (for example, c:\myapp\data\intfiles_mssql\people_btr.int).
So, rather than having thousands of INT files in each of the customer folders (all having the same contents), the INI file directs the driver to a common location for the INT files.
Creating temporary files¶
When the EOY Report is generated, various data is collected into a temporary file. The definition for this table, ReportTemplate.mkd, is in the DDFs. Use the Migration Utility to generate the corresponding INT file, ReportTemplate_mkd.int.
When the application executes a B_CREATE("eoyreport_2014090901011545.mkd | ReportTemplate.mkd | EOYReportData_2014090901011545"), the Mertech driver reads ReportTemplate_mkd.int, creates a new table EOYReportData_2014090901011545 and generates a new INT file, eoyreport_2014090901011545_mkd.int.
Later, when B_DROP_TABLE is called, the INT file and the table on the SQL backend are removed.
Avoid OS calls¶
Some applications manipulate the data files directly with OS calls by copying an existing data file and deleting the file when done. These direct operations are not supported by the drivers. Instead, you must fully rely on the B_CREATE API to create the temporary table and B_DROP_TABLE to remove the table when it is no longer needed.
See also Additional Btrieve APIs
Multi-user scenarios¶
Extra precaution must be taken to avoid multiple processes from using the same temporary file simultaneously. With Btrieve, the temp file could be created locally and thus each client could have a dedicated file. Now that the table is created in a shared SQL resource, multiple clients have access to the same table.
Several options are available to avoid this problem.
Assign a globally unique filename (like a timestamp or guid for the filename) each time a temp table is created. This option does not use an INT file as the template for creating the table.
Create the INT file for the temporary file. Then, use the extended
B_CREATEsyntax to specify a new filename each time a temporary table is created (see examples under the B_CREATE API).Create the INT file for the temporary file. Assign each installation a dedicated prefix (e.g.
Region1,Region2,Region3) or postfix. Make a copy of the temp file for each installation and add the assigned prefix (e.g.,INT) or postfix to the file. Then, restrict each region to using the assigned filename (e.g.,Region1_Temp) inB_CREATEcalls (see examples under the B_CREATE API).Note
In all cases, make sure to use the
B_DROP_TABLEAPI to delete stale temp tables.
Access rights¶
SQL administrators can be very strict when assigning access rights to users. For example, they may insist that users do not have Create Table permission. Without Create Table permission, users cannot create temporary files on the server.
One solution to this problem is to assign a different login for temporary tables. The main application data can be accessed under a login that has Read/Write access. The temporary data can be accessed under a login that does not have access to the main data but does have Create Table permission.
The easiest way to assign a different login for temporary tables is to place the temporary files in a separate directory and setup the
mds.inifile in that folder to use the assigned login.Main Data Files Directory:
C:\MyApp\DATAmds.ini (UseTrustedConnection=no, User=mydomain\me, Password=b7fba9c0e1b0f217) MARKETING.MKD MARKETING_MKD.INT PURCHASING.MKD PURCHASING_MKD.INTTemporary Files Directory:
C:\MyApp\TEMPmds.ini (UseTrustedConnection=no, User=mydomain\temp, Password=9dc42401d47a3fd0) TEMP1.INT TEMP2.INT TEMP3.INT
Note
A user name and password stored in an INT file override settings in the mds.ini file.
SQL Login and Direct Query Execution¶
The Embedded SQL API can be used to run SQL Queries directly to the SQL backend. The advantage of using the Embedded SQL API together with the regular
BTRCALLAPI instead of technologies such as ADO is that no new connection to the server is opened. This also allows for the nesting of embedded SQL commands together with standardBTRCALLfunctions in the same transaction.All string data should be passed and will be returned as null terminated strings.
See also SDK documentation: Modules tab > SQL Login and Direct Query Execution
B_SQL_MAX_OPEN_CURSOR (10000)
Retrieves or sets the maximum number of open cursors in use by the driver. A higher number of cursors allow the driver to cache a lot of queries into open tables, but leads to higher resource usage on the server.
B_SQL_OPEN_CURSOR (10001)
Opens a new cursor that is used to execute commands. The position block parameter contains a handle to the cursor.
B_SQL_CLOSE_CURSOR (10002)
Closes a previously opened cursor.
B_SQL_PREPARE (10003)
Prepare SQL Statement for execution.
B_SQL_EXECUTE (10004)
Execute a (previously prepared) SQL statement.
B_SQL_FETCH (10005)
Fetch the next row from a previously executed query. If there are no more rows, then the return value is zero.
B_SQL_BIND (10006)
Binding parameters are used in conjunction with other queries with parameters (queries such as
SELECT * FROM CUSTOMER WHERE CUSTOMER_ID = ?). The query is prepared once and can then be executed multiple times by repeatedly rebinding (by callingB_SQL_BIND) and re-executing it (by callingB_SQL_EXECUTE). If there are multiple parameters in the query, then callB_SQL_BINDonce for each parameter in order before callingB_SQL_EXECUTE. This process can then be repeated again, with calls toB_SQL_BIND, followed by a new call toB_SQL_EXECUTE._SQL_GET_RET_COLUMNS (10007)
Returns (as the return code) the number of columns in the most recently executed query.
B_SQL_GET_RET_ROWS (10008)
Get number of rows returned from the previously executed SQL statement. In certain cases, the number of rows cannot be determined in advance, whereby the return value is zero.
B_SQL_GET_COL_NAME_SIZE (10009)
Retrieves the name and size of a given column in a result set from a previously executed query. The name is returned in the
dataBufferand the size is the return value of the function.B_SQL_GET_COLUMN (10010)
Returns the data in a column in the current row. All fields are returned as strings.
B_SQL_START_TRAN (10011)
Starts a transaction in the database.
B_SQL_COMMIT (10012)
Commits a previously started transaction.
B_SQL_ROLLBACK (10013)
Aborts a previously started transaction, rolling back any changes done since the start of the transaction.
B_SQL_GET_ERROR (10014)
Returns the native SQL error for the last failed operation.
B_SQL_LOGIN (10015)
Programmatically login to an SQL Server to suppress the popup login dialog box. The format of the login structure is:
struct MDS_SQL_LOGIN
{
char server[30];
char username[30];
char password[30];
char database[30];
char schema[30]; /* only used by B_SQL_GET_LOGIN */
}
If the UserName and Password members are zero-length strings, trusted connections will be used in MS SQL Server.
B_SQL_LOGOUT (10016)
Programmatically logs out from an SQL Server. If no server name is provided, then a logoff will be sent to all of the servers to which the driver is currently connected.
B_SQL_GET_LOGIN (10017)
Retrieve the login information for the file identified.
For the given file, return the Server, Database name, User name and password used to login with. This information could have originally come from the
.INT,.INI, save information in the registry, or from the Login dialog box._SQL_ATTACH (10018)
Attach a database to a current SQL Server instance.
B_SQL_DETACH (10019)
Detach a database from a current SQL Server instance.
B_SQL_LDB_CREATE_INSTANCE (10020)
Create a new LocalDB instance.
B_SQL_LDB_DELETE_INSTANCE (10021)
Remove a LocalDB instance.
B_SQL_LDB_FORMAT_MESSAGE (10022)
Return the localized description for a LocalDB error. Use this function to retrieve the error details when a
B_SQL_LDB_call failsB_SQL_LDB_GET_INSTANCE_INFO (10023)
Get information for a LocalDB instance, such as whether it exists, version information, whether it is running, and so on.
B_SQL_LDB_GET_INSTANCES (10024)
Return all the LocalDB instances with a specified version.
B_SQL_LDB_GET_VERSION_INFO (10025)
Return information for a specified LocalDB version.
B_SQL_LDB_GET_VERSIONS (10026)
Return all LocalDB versions available on a computer.
B_SQL_LDB_SHARE_INSTANCE (10027)
Share a specified LocalDB instance.
B_SQL_LDB_START_INSTANCE (10028)
Start a specified LocalDB instance.
B_SQL_LDB_START_TRACING (10029)
Enable API tracing for a user.
B_SQL_LDB_STOP_INSTANCE (10030)
Stop a specified LocalDB instance from running.
B_SQL_LDB_STOP_TRACING (10031)
Disable API tracing for a user.
B_SQL_LDB_UNSHARE_INSTANCE (10032)
Stop sharing a specified LocalDB instance.
Fast Inserts¶
The Fast Insert APIs allow an application to use native SQL backend functionality to get data into a table as fast as possible. They also allow a full 32-bit data buffer to support variable length records (normally containing Note or BLOB data) which usually require Chunk operations after the
B_INSERT.
See also SDK documentation: Modules tab > Fast Inserts
Inserting variable length data
If the table has variable length records then there are three options for delivering the variable portion:
The entire record is in memory in one structure (no matter the size). Pass the entire structure to the
B_FAST_INSERTwith appropriatedataLength. Set thekeyBuffertoNULL. This is the fastest method.Variable data still needs to be read and the length is known. Pass a pointer to a callback routine in
callBackand length of the “BLOB” inchunkDataLen.Variable data is not in memory; the length is not yet known Pass a 0 in the
chunkDataLenvalue and allow the chunk callback to dynamically read the data during the Fast Insert.If the application does not want to submit all variable length data together with the fixed portion in
B_FAST_INSERT(option 1), pass anMDS_FAST_INSERTstructure in the key buffer, and set the key buffer length to the size of the structure. The values of the structure are shown in options 2 and 3.The callback function is repeatedly called to allow the application to fill up the variable data portion of the record. The function should return the number of bytes of variable data it has copied into the
dataparameter each time, or zero to indicate completion.B_BEGIN_FAST_INSERT (5001)
Begin Fast Insert Mode.
Structures:
Specify the bulk copy batch parameters in the
MDS_FAST_INSERT_MSSQLstructure.Note
BATCHSIZEtakes precedence overROWS_PER_BATCHorKILOBYTES_PER_BATCH{ long batchSize; // Number of rows per transaction long kbytesPerBatch;// Kilobytes Sent per Batch long rowsPerBatch; // Rows Sent per Batch long indexOrder; // Btrieve index number data is ordered by bool lockTable; // Table will be locked with TABLOCK if True }; const long NoIndexOrder = -1;Note
Only Fast Insert operations should be performed until the
B_END_FAST_INSERTis complete. RegularB_INSERToperations may produce unpredictable results.B_FAST_INSERT (5002)
Insert a record in Bulk / Fast mode.
Structures:
typedef void (__stdcall *MDS_CHUNKCALLBACK) (CHAR** data, LONG* size); struct MDS_FAST_INSERT { MDS_CHUNKCALLBACK callBack; // Callback function for retrieving variable length portion of record (chunks) long chunkDataLen; // Known Length of variable portion (chunk) };B_END_FAST_INSERT (5003)
Stop Fast Insert mode and commit new data to the database.
Driver Trace Control¶
Several functions are available to control the trace output and allow the application to add information to the trace.
See also SDK documentation: Modules tab > Driver Trace Control
B_TRACE_INIT (5017)
Enable tracing and define the filename and level of information.
B_TRACE_GET_SETTINGS (5018)
Retrieve current settings of trace file.
B_TRACE_START (5019)
Start tracing (after perhaps pausing it for a while).
B_TRACE_STOP (5020)
Pause the trace output.
B_TRACE_MSG (5042)
Send a message or comment to the trace file.
Schema Functions (DTIFunction Support)¶
Sometimes an application needs to add new tables to the database or modify existing tables or other changes to the schema. Pervasive.SQL provided the Distributed Tuning Interface (DTI) for this purpose. BTR2SQL introduces similar APIs. All are found in
mds_btrieve.h.
See also SDK documentation: Modules tab > Schema Functions
MdsSetDatabase
MDSRESULT MdsSetDatabase(const char *lpszName)Establish the current database. This function sets the current database in the SQL Server.
MdsAddTable
MDSRESULT MdsAddTable(MDS_TABLEINFO *tableProps, MDS_COLUMNMAP *columnList, WORD columnCount)Create a new table in the server as well as set up the definition file (
.int).Note
Index creation is handled through the regular Btrieve API
B_BUILD_INDEX.MdsAddFields
MDSRESULT MdsAddFields(const char *fileLocation, MDS_COLUMNMAP *columnList, WORD columnCount)Add new fields to the end of an existing table and update the definition files on the client.
MdsGetFileInfo
MDSRESULT MdsGetFileInfo(const char *fileLocation, const bool IncludeSqlData, MDS_TABLEINFO *tableProps, MDS_COLUMNMAP *columnList, WORD columnCount)Add new fields to the end of an existing table and update the definition files on the client.
Renaming tables and files¶
The following APIs are available to rename files and tables.
Note
All of the parameters (except table name in
MdsRenameTable) reference the originally named Btrieve data filename, NOT the INT filename. So, when you issue a request to renameBilling.mkd(which does not actually exist), the driver actually renamesBilling_mkd.int.
MdsRenameTable
MDSRESULT MdsRenameTable(const CHAR *fileLocation,
const CHAR *newTableName)
Rename an existing table on the server as well as the definition file (.INT) on the client. For example, the following code renames table receipts_2014 to 2014 Receipts on the server and updates the TABLE_NAME in the receipts_2014_mkd.INT file.
char oldFileName[] = "receipts_2014.mkd";
char newTableName[] = "2014 Receipts";
MdsRenameTable (oldFileName, newTableName);
MdsRenameFile
MDSRESULT MdsRenameFile(const CHAR *fileLocation,
const CHAR *newTableName)
Rename the local data file (i.e., the INT file). Use MdsRenameTable and MdsRenameFile together to accomplish both. For example, the following code renames file receipts_2014_mkd.INT to 2014 Receipts_mkd.INT.
char oldFileName[] = "receipts_2014.mkd";
char newFileName[] = "2014 Receipts.mkd";
MdsRenameFile (oldFileName, newFileName);
B_RENAME_FILE
B_RENAME_FILE is also available; it is a subfunction of B_CREATE. B_RENAME_FILE renames the local data file (i.e., the INT file). For example, the following code renames file receipts_mkd.INT to receipts_2014_mkd.INT.
#define B_RENAME_FILE (B_CREATE) // = 14 #define B_KEY_RENAME_FILE = -127 char oldFileName[] = "receipts.mkd"; char newFileName[] = "receipts_2014.mkd"; strcpy(databuf, oldFileName); strcpy(keybuf, newFileName); BTRCALL(B_RENAME_FILE, posBlock, databuf, &datalen, keybuf, B_KEY_RENAME_FILE);
In summary |
Renames Server Table |
Renames Local Data File (the INT file) |
|---|---|---|
MdsRenameTable |
yes |
no |
MdsRenameFile |
no |
Yes |
B_RENAME_FILE |
no |
yes |
Deleting tables and files
MdsDropTable
MDSRESULT MdsDropTable(const CHAR *fileLocation)Delete an existing table on the server and remove the definition file (
.INT) on the client. The.INTis not removed if it hasPERMANENT_INTset toYES. For example, the following code removes theBillingtable.MdsDropTable ("Demodata\\Billing.mkd")
MdsDeleteFile
MDSRESULT MdsDeleteFile(const CHAR *fileLocation)
Delete the local INT file (call after using MdsDropTable). For example, the following code deletes the Billing_mkd.INT file.
MdsDeleteFile ("Demodata\\Billing.mkd")
B_DROP_FILE
Drop the SQL table and remove the INT file. If present, the actual MicroKernel file and the definition in the DDFs (or XML source files) remain.
Note
To prevent an INT file from being deleted when a table is dropped, you can set the
PERMANENT_INTtoken in the INT file toYES(the default setting isNO). This is useful if for example, you want to drop and then later recreate a file using the same structure.#define B_DROP_FILE (B_OPEN + 3000) char FileName[] = "receipts.mkd"; strcpy(keybuf, FileName); BTRCALL(B_DROP_FILE, NULL, NULL, NULL, keybuf, 0);
B_DELETE_FILE
B_DELETE_FILEis also available; it is a subfunction ofB_CREATE. It drops the SQL table and deletes the INT file.#define B_DELETE_FILE (B_CREATE) // = 14 #define B_KEY_DELETE_FILE = -128 char FileName[] = "receipts.mkd"; strcpy(databuf, FileName); BTRCALL(B_DELETE_FILE, posBlock, databuf, &datalen, NULL, B_KEY_DELETE_FILE);
In summary
Deletes Server Table
Deletes Local Data File (the INT file)
MdsDropTable
yes
no
MdsDeleteFile
no
yes
B_DROP_FILE
yes
yes
B_DELETE_FILE
yes
yes
Truncating files¶
B_TRUNCATE_FILE
Truncate file (delete all records).
#define B_TRUNCATE_FILE (B_OPEN + 2000) char FileName[] = "receipts.mkd"; strcpy(keybuf, FileName); BTRCALL(B_TRUNCATE_FILE, NULL, NULL, NULL, keybuf, 0);
Migration Functions¶
If you would like to write your own interface or have more control over migrating files from Btrieve to SQL, you can use these migration functions. They make it easy to migrate the entire database in one call, migrate certain tables with different settings, or migrate one table at a time.
See also SDK documentation: Modules tab > Migration Functions
MdsPrepareDB
MDSRESULT MdsPrepareDB(const char *ddfLocation, const char *tableName, MdsMigrationControl whatToDo, const char *xmlConfig, MDS_MIGCALLBACK callback)Setup the server and/or database with needed objects for running your Btrieve application. Depending on the server, create stored procedures, views, etc. as needed to properly support the Mertech driver. Generally, the Mertech driver will not be able to function correctly until the server and specific database have been initialized with a few objects. This is normally done by the migration tool. This API needs to be called before running the application.
MdsMigrateOneTable
MDSRESULT MdsMigrateOneTable(const char *ddfLocation, const char *tableName, MdsMigrationControl whatToDo, const char *xmlConfig, MDS_MIGCALLBACK callback)Migrate a table from Btrieve to SQL.
MdsMigrateAllTablesInDdfs
MDSRESULT MdsMigrateAllTablesInDdfs(const char *ddfLocation,
MdsMigrationControl whatToDo,
const char *xmlConfig,
MDS_MIGCALLBACK callback)
Migrate a database from Btrieve to SQL.
MdsMigrateAllTablesInConfig
MDSRESULT MdsMigrateAllTablesInConfig(const char *ddfLocation,
MdsMigrationControl whatToDo,
const char *xmlConfig,
MDS_MIGCALLBACK callback)
Migrate multiple tables from Btrieve to SQL.
Additional Btrieve APIs¶
These additional APIs augment the standard Btrieve API and are implemented in the same manner – utilizing the familiar BTRV() or BTRCALL() functions. A direct call interface is also available for some of the APIs.
See also the SDK documentation: Modules tab > Extended Btrieve API, Modules tab > Version Information¸ Modules tab > Choosing Fields to Fetch
B_ENCRYPT_PASSWORD (3005) )**or **MdsEncryptPassword
const MDSRESULT MdsEncryptPassword(char *password,
char *encryptpwd,
unsigned short encryptpwdlen)
Encrypt a password for usage in an mds.ini file.
B_SET_MDS_OPTION (3017) )**or **MdsSetOption
const MDSRESULT MdsSetOption(unsigned short option,
char *value)
Set the value of an option specific to the BTR2SQL driver (deprecated, use MdsSetSetting).
MdsSetSetting
const MDSRESULT MdsSetSetting(char *foldername,
char *settingname,
char *values)
Set a global or folder-specific value for an option for the current BTR2SQL driver.
You can set global and folder specific settings programmatically through a call to MdsSetSetting.
The format for the call is:
MdsSetSetting(foldername, settingname, value)
foldername is the full or relative path to the folder where the
mds.inifile would reside (for example,c:\myapp\ver4\data\cust5). The folder does not have to exist, it is simply the virtual location for themds.inifile. If the setting being changed is in themds_global.inifile, specifyNULLforfoldername.settingname is any entry in the
mds.inifile (for example,INT-Folder) ormds_global.inifile (for example,Trace_Level). Setting names are not case sensitive.
If a call to MdsSetSetting is made to enable TRACE_ON, that should be the last trace setting updated. If a trace is already active, it is stopped and a new file takes over using the latest trace settings.
value is the value to be assigned to the setting. If a Password value is provided and it starts with
0x, it is assumed that the password was encrypted usingMdsEncryptPassword.
There is no need to specify a Section Name (for example, [SQL_BTR]). The assumption is that the setting is for the current driver. All parameters are enclosed in quotes, so the value itself should never include quotes.
Examples:
// (mds.ini) Set the password for data files opened in the cust5 folder
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5", "Password", "0xed8ee3e72436da3169")
// (mds_global.ini) Limit buffer output in the trace to just KeyBuffers
MdsSetSetting(NULL, "Trace_Buffers", "kb")
How does MdsSetSetting work?¶
The example below walks through how global and folder-specific settings are loaded and overwritten.
When the application starts:
a. Registry values are read and stored (global settings):
Trace_On=c:\trace\sql_btr.tra, Trace_Level=0, TRACE_FLUSH=no, Server_Side_Locking=yes
b. Environment variable values are read and stored, overwriting any existing values:
Trace_On=c:\trace\sql_btr.tra, Trace_Level=1, ← changed TRACE_FLUSH=no, Server_Side_Locking=yes
c. mds_global.ini values are read and stored, overwriting any existing values:
Trace_On=c:\trace\sql_btr.tra, Trace_Level=1, TRACE_FLUSH=yes, ← changed Server_Side_Locking=yes, Trace_Query_Times=yes
When the application calls
MdsSetSetting(NULL, "Trace_Level", "2")to set a global value, any existing global value is overwritten:Trace_On=c:\trace\sql_btr.tra, Trace_Level=2, ← changed TRACE_FLUSH=yes, Server_Side_Locking=yes, Trace_Query_Times=yes
When the application calls
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5", "Schema", "cust5")to set a folder-specific setting:a. If an mds.ini file exists in the specified directory (and the file was not already read when a create or open call was made) then the mds.ini file is read and the values are stored (folder-specific settings):
c:\myapp\ver4\data\cust5: Server=testserver, Database=testdb
b. MdsSetSetting is run and in-memory values are overwritten:
c:\myapp\ver4\data\cust5: Server=testserver, Database=testdb, Schema=cust5 ← changed
The values set by MdsSetSetting remain in effect until the process stops.
Note
Be careful changing values for Schema, Database, Table-Prefix and Table-Postfix using MdsSetSetting since the changes affect all subsequent create and open calls for the selected directory.
Note
If there is an mds.ini file on disk, settings in that file are NOT changed by a call to MdsSetSetting. Values set by MdsSetSetting are not stored on disk.
MdsSetSetting Example¶
Below is an example using MdsSetSetting to create a virtual mds.ini file and then using these settings to create and open a new table.
// First, execute an embedded sql call to create new schema 'cust5'
// Next, create a virtual mds.ini file for 'c:\\myapp\\ver4\\data\\cust5'
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5","INT_Folder","c:\\myapp\\ver4\\data\\intfiles_mssql")
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5","Server","testserver")
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5","Database","testdb")
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5","Schema","cust5")
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5","User","testuser")
MdsSetSetting("c:\\myapp\\ver4\\data\\cust5","Password","password")
// Now, create and open Person.mkd using the virtual mds.ini settings
B_CREATE "c:\\myapp\\ver4\\data\\cust5\\Person.mkd"
B_OPEN "c:\\myapp\\ver4\\data\\cust5\\Person.mkd"
The INT_Folder setting in the virtual mds.ini directs the application to c:\myapp\ver4\data\intfiles_mssql where the Person_mkd.int file is stored.
A sample directory structure is shown below. The cust1, cust2, and cust5 folders (shaded in gray) do not actually exist, they are virtual folders, created in memory by MdsSetSetting to contain a virtual mds.ini file. The application must include calls to MdsSetSetting to create these virtual data folders before it opens or creates any tables for these schemas on the server.
c:\myapp
ver4
data
mds.ini(int-folder=.\intfiles_mssql server=testserver schema=common)
intfiles_mssql
person_mkd.int
cust1
mds.ini(int-folder=.\intfiles_mssql server=testserver schema=cust1)
cust2
mds.ini(int-folder=.\intfiles_mssql server=testserver schema=cust2)
cust5
mds.ini(int-folder=.\intfiles_mssql server=testserver schema=cust5)
MdsGetSetting
const MDSRESULT MdsGetSetting(char *foldername,
char *settingname,
char *values,
unsigned short valueSize)
You can retrieve global and folder specific settings programmatically with a call to MdsGetSetting. The format for the call is:
MdsGetSetting(foldername, settingname, return value)
All settings in the mds_global.ini file and all settings in the mds.ini file (except Password) can be retrieved. Additionally these global settings, displayed at the top of the trace file, can be retrieved:
Driver_Info: for example,BTR2SQL 7.0.10675.0Driver_Location: for example,C:\myapp\w3btrv7.dllProcess_Info: for example,C:\myapp\myapp.exe, Process ID 3628, WTS ID 0Computer_Name: for example,mycomputerLicense_Info: for example,Serial Number 2000 for 10 users registered to MY COMPANYCurrent_Working_Folder: for example,C:\MyAppIni_Location: for example,C:\MyPath\mds_global.iniorC:\MyApp\data\mds.ini
MdsGetError
const MDSRESULT MdsGetError(char *errorDesc,
WORD *size)
Retrieve the last error message. This function can be called to retrieve a detailed error message (usually the message generated by the database server) after an error code has been received from an operation.
B_IS_MIGRATED (5000)
Used to determine whether or not a file has been migrated to an SQL Backend.
Note
This function does not verify the existence on the server or validity of the table. It simply indicates that the Btrieve file was migrated (the
.INTfile exists). It is possible that the SQL table may have been removed.
B_FAST_STAT (5015)
This is the same as a
B_STAT(15)except the record count is not returned.
B_STATis a common operation to perform after theB_OPEN— especially for Btrieve class wrappers. The operation usually includes the current record count for the file. Obtaining the number of rows in a table in SQL is a relatively slow process. So, if the application does not need this extra piece of information, utilize theB_FAST_STATinstead.
B_EXTENDED_VERSION (5026)
Retrieve information about the Mertech driver or attached SQL backend.
The Extended Version function can do two things: retrieve information about the Mertech driver (version, dll name, etc.) or retrieve a version string from the active SQL server. Making this call against the Pervasive access dll causes a return code of 1. So this is a useful way to know if your application is running against a Btrieve or a SQL backend.
B_SET_FIELD_FETCH (2042)
Control which fields are retrieved from the server.
This is very useful for performance especially for large records. This API allows the application, without dealing with
GetExtendedbuffers, to narrow the list of fields that are retrieved from the server.
Accessing Both Btrieve and SQL Backends¶
Consider the following scenario. During the migration process, some files are left in Btrieve, while others are migrated to an SQL backend. This could be an interim step during the migration process to test the migration. Or, a decision may have been made to leave private tables in Btrieve on the local machine, while common tables are stored on a central SQL Server. The Mertech drivers support this scenario.
Rather than replacing the Pervasive dll with the Mertech dll of the same name, an application needs to load Pervasive’s wbtrv32.dll and one of the Mertech migration dlls (sql_btr.dll, ora_btr.dll, or pgs_btr.dll) to simultaneously access the Btrieve and SQL backend.
Load the required Mertech dll (MS SQL in the example). Then, declare a function to be called in place of BTRCALL when accessing the MS SQL backend. For example:
#include <BtrieveFile.h>
// Load Pervasive's Btrieve access dll
BtrieveFile* btrieve = new BtrieveFile("w3btrv7.dll");
// Load Mertech's MicrosoftSQL library
BtrieveFile* mssql = new BtrieveFile("sql_btr.dll");
// Open an old Btrieve file
bStat = btrieve->BTRCALL(B_OPEN, NULL, &(datalen=0), FILE_NAME, MAX_KEY_BUF_SIZE, 0);
// Open a Btrieve file migrated to MSSQL
bStat = mssql->BTRCALL(B_OPEN, NULL, &(datalen=0), FILE_NAME2, MAX_KEY_BUF_SIZE, 0);
By loading the different dlls explicitly, the application is able to access the BTRCALL function from each; providing access to both Pervasive Btrieve files and SQL-migrated files.
You can also use this same method to access multiple SQL backends. For example, load the sql_btr.dll and the ora_btr.dll to simultaneously access MS SQL and Oracle backends.
Note
The INT files that are produced during migration are specific to each SQL backend so you will need to create a folder for each backend and provide an mds.ini (sample in the SDK) that specifies the INT-Folder location and login information for each server.
Visual Basic Linking:¶
For MS SQL Server:
Declare Function BTRCALL_MSSQL Lib "sql_btr.dll" Alias "BTRCALL" (ByVal OP, ByVal Pb$, Db As Any, DL As Integer, ByRef Kb As Any, ByVal Kl, ByVal Kn)As Integer
For Oracle:
Declare Function BTRCALL_Oracle Lib "ora_btr.dll" Alias "BTRCALL" (ByVal OP, ByVal Pb$, Db As Any, DL As Integer, ByRef Kb As Any, ByVal Kl, ByVal Kn)As Integer
For PostgreSQL:
Declare Function BTRCALL_PGSQL Lib "pgs_btr.dll" Alias "BTRCALL" (ByVal OP, ByVal Pb$, Db As Any, DL As Integer, ByRef Kb As Any, ByVal Kl, ByVal Kn)As Integer
With these additional definitions, the application can call the original BTRCALL() function to access Btrieve as well as for example BTRCALL_ORACLE() to access an Oracle backend.