SQLRDD Manual PDF
SQLRDD Manual PDF
SQLRDD Manual PDF
User’s guide
© 2004, Marcelo Lombardo
[email protected]
Introduction....................................................................................................................... 3
Requirements..................................................................................................................... 5
Supported databases......................................................................................................... 6
Components ....................................................................................................................... 7
The connection Classes ..................................................................................................... 9
The RDD .......................................................................................................................... 13
The SQL Parser............................................................................................................... 19
The Code Generator ....................................................................................................... 20
SQLRDD Features .......................................................................................................... 21
Deleting records .......................................................................................................... 21
Record numbering ...................................................................................................... 21
Supported data types .................................................................................................. 21
Data Types Serialization............................................................................................. 22
Index Management ..................................................................................................... 22
Transactional Control and Locking .......................................................................... 24
Tracing SQL calls ........................................................................................................... 26
SQLRDD Version Control ............................................................................................. 27
SQLRDD Language Support ......................................................................................... 28
Compiling and linking the application.......................................................................... 29
Performance tuning tips ................................................................................................. 30
Compatibility and Limitations....................................................................................... 33
APPENDIX 1:.................................................................................................................. 34
APPENDIX 2:.................................................................................................................. 35
SQLRDD was created to access SQL database servers like Oracle, Microsoft SQL
Server, IBM DB/2, Postgres, and Sybase, MySQL, from xHarbour xBase language
(www.xharbour.com).
You will need to have a fully working installation of one of these supported relational
database management system (RDBMS) to make use of SQLRDD.
SQLRDD is more than a RDD. It is a complete suite to interact with SQL databases from
within xHarbour compiler.
So, we strongly recommend you to learn about SQL Databases and SQL Statements, as
well as the SQL Architecture and topology before using this product. Please, take some
time to read the “Getting Started” manual of your preferred database system, and study
the SQL commands syntax to define and manipulate data inside the database.
We also recommend that you read this complete guide before you start using SQLRDD.
• Creates the database components using xBase Data Definition Language (DDL)
like dbCreate(), INDEX ON, etc.
• Translates RELATIONS into JOINS or OUTER JOINS inside the database server
to have a better use of the SQL Engine features and gain performance in xBase
code
• Ability to maintain historic data inside the tables (this is a new feature that has no
equivalent feature, in any other RDD engine!). Using historic data you get track
of table updates and inserts in the time line, and the actual date determines the
visible record. As an example, you can add an employee “next week”, and this
record will appear to the users on next week. You can raise the price of a product
“next month” and this change will only become visible in next month. You can
also set the actual date to each workarea. Thus, if you set the actual date to past
month or to yesterday, you will see the records as they were last month or
• Code once, run anywhere: The same application runs with all database systems
without modifications
• ODBC Drivers to the target database (if not using native connection) at
workstation or Server (Linux).
• The connection classes: These are a set of classes used to connect to the database
systems. We have different classes for ODBC, Postgres. MySQL and RPC, and
more classes will be built in future versions. These classes act as an abstraction
layer for the RDD access to the database server, and have the exact same syntax
in all of then. With the connection classes, you can also issue SQL Commands
direct to the database system and explore the result set, if it exists.
• The RDD: The RDD is the low level DML and DDL interface, and it translates
the xBase commands into SQL Statements. These statements are issued to the
database server through the connection classes.
• The SQL Parser: Using the Connection Classes, you can send any command
direct to the database system. But unfortunately, the SQL Syntax from the
different database vendors is quite different. It means that you have to create a
different SQL sentence to every target database, if you want to have a portable
application. With the SQL Parser, all you need to get portable with the major
database systems is to use the xHarbour SQL Language. The parser “understands”
the xHarbour SQL Syntax and generates an intermediate portable code from the
SQL phrase. Than you can use the Code Generator to automatically create the
specific-system SQL command.
The Connection Classes are a set of classes used to connect to the different database
systems. These classes are used inside the RDD to establish the needed communication
between your application and the database.
The programmer must use these classes to connect to the database server before using
any table or SQL function, except the SQL Parser.
We can have several simultaneous connections opened, and select the active connection
before using a workarea.
The SQLRDD have some friendly functions to encapsulate all the complexity of the
Connection Classes. The most important functions are:
CONNECT_ODBC
“DSN=ODBCdataSourceName;UID=username;PWD=password;DTB=database“
Where:
SR_AddConnection(CONNECT_ODBC, “DSN=Northwind;UID=sa;PWD=“ )
“HST=ServerNameOrIP;UID=username;PWD=password;DTB=DataBaseName;PRT=Port“
Where:
SR_AddConnection(CONNECT_POSTGRES,
“HST=localhost;UID=root;PWD=1234;DTB=test“ )
Note: The “HST” option name can be replaced with the native interface internal
name. The internal names are:
Examples:
SR_AddConnection(CONNECT_POSTGRES,
“PGS=localhost;UID=root;PWD=1234;DTB=test“ )
SR_AddConnection(CONNECT_ORACLE,
“OCI=localhost;UID=system;PWD=manager;DTB=test“ )
CONNECT_FIREBIRD
Firebird native connection is very similar to the above, but can specify the char set
as an option. Example:
HST=C:\firebird\DESENV.GDB;uid=SYSDBA;pwd=masterkey;charset=ISO8859_1
Or
SR_DetectDBFromDSN( <cDSN> ) or
DetectDBFromDSN( <cDSN> ) Î ConnectionConstant
Detects the database connection you plan to use based on the internal interface
name in DSN. It is planned to be used together with SR_AddConnection() to
determine the correct connection constant. Example:
It checks for OCI=, PGS=, DSN=, MYSQL= , FB= or FIREBIRD= and returns:
CONNECT_ORACLE , CONNECT_POSTGRES, CONNECT_ODBC,
CONNECT_MYSQL, CONNECT_FIREBIRD respectively.
SR_SetActiveConnection( <nHandle> )
Sets the actual active connection to the next dbUseArea() function, or USE
command. This is not needed if the application has only one connection to the
database server.
SR_GetActiveConnection() Î nHandle
Retrieves the active connection object. You can use the connection object to issue
SQL Statements direct to the target database system, using following syntax:
Where:
Note: Please read chapter about SQL Parser / Code Generator if you want to
create cross-platform applications
<lMsg> Set it to .F. if you don’t want to generate a Run Time error if command
fails. Default is .T.
<lFetch> Set it to .T. if you want to fetch the resulting record set. You can choose
to fetch row(s) to an array or file. Default is .F.
Note: If you try to fetch the result set of a DDL command or a INSERT, UPDATE
or DELETE, database may generate a Run Time Error.
<cFile> Is the target filename to output record set. If file is already opened (with
alias name like filename) SQLRDD will push result set in this file. File should have
exact same structure as result set, or Run Time Error will be raised. If file does not
exist, it will be created using default RDD. At the end of operation, file will be
opened in exclusive mode and record positioned in Top.
<lNoRecno> If you set to .T., SR_RECNO column will not be fetched to target file
or array. Default is .F.
You should declare the needed connection class in your main PRG, choosing one or more
from the following options:
Please check the Reference Guide for the complete set of functions and Connection Class
Methods.
The RDD is the SQLRDD’s component which translates xBase commands into SQL
statements, and the SQL result set into workarea data.
This translation might be quiet involved, because DBF tables and SQL tables have
different concepts. DBF Tables (and xBase DML in general) have an ISAM-like
behavior.
ISAM is abbreviation for Indexed Sequential Access Method, a method for managing
how a computer accesses records and files stored on a hard disk. While storing data
sequentially, ISAM provides direct access to specific records through an index. This
combination results in quick data access regardless of whether records are being accessed
sequentially or randomly. Thus, indexes are used not only for search data. They define
the record order in the same time.
On the other hand, SQL tables are record-set driven. With this technique, you send a
query to the database system, and it sends back the records that match your query.
Indexes are used for search data and not to order the data. Ordering is a second step
internally executed in the database server.
To acquire the best results in translating xBase to SQL, we need to have different
approaches to address large and small tables. If we execute a “SELECT * FROM
<table>” (it means “give-me all the records from <table>”) in a small table, depending on
your system, the database server will likely respond instantly. But if we execute the same
command on a very large table, you might have to wait a long time for the reply. Further,
you might utilize excessive server resources creating a temporary table, with thousands of
records and sorting it, but you might end-up using only few of these records in your
application.
• Cache workarea: All the records of the target table are sent to the application and
stored in memory. With this method, the xBase workarea runs very fast, because
all of the commands are executed in memory, without requesting the database
server. But it may be very slow with huge tables, and might utilize excessive
• ISAM Simulator: ISAM simulator will create intelligent queries to request only a
small piece of the table each time, as needed by the application. This is the
recommended choice when working with huge tables, but it is slower than Cache
Workareas when used with smaller tables.
You can determine how the RDD manages the tables with a callback code block. You can
set this codeblock with the function:
SR_SetTableInfoBlock( <bInfoBlock> )
Usually, you do not need to set this code block in your application. SQLRDD default
codeblock fits good most of the cases.
<bInfoBlock> is a codeblock used to retrieve the table information. When you execute
the dbUseArea() function, SQLRDD calls this codeblock passing the table name as
parameter. The codeblock must return an array the following information, defined in
sqlrdd.ch:
#define TABLE_INFO_SIZE 16
#define TABLE_INFO_TABLE_NAME 1
#define TABLE_INFO_FILTERS 2
#define TABLE_INFO_PRIMARY_KEY 3
#define TABLE_INFO_RELATION_TYPE 4
#define TABLE_INFO_OWNER_NAME 5
#define TABLE_INFO_ALL_IN_CACHE 6
#define TABLE_INFO_CUSTOM_SQL 7
#define TABLE_INFO_CAN_INSERT 8
#define TABLE_INFO_CAN_UPDATE 9
#define TABLE_INFO_CAN_DELETE 10
#define TABLE_INFO_HISTORIC 11
#define TABLE_INFO_RECNO_NAME 12
#define TABLE_INFO_DELETED_NAME 13
#define TABLE_INFO_CONNECTION 14
#define TABLE_INFO_QUALIFIED_NAME 15
#define TABLE_INFO_NO_TRANSAC 16
The default bInfoBlock from SQLRDD translates the file name’s attributes like “\/:.” Into
underscores. Example:
"c:\data\accounts\chart.dbf"
Note: See in APPENDIX 1 the full source code from this routine.
TABLE_INFO_TABLE_NAME
Returns the table name in this position. It is very useful when migrating from DBF. Your
code may translate, as an example:
“c:\company1\products.dbf” to “COMPANY1_PRODUCTS”
The default behavior removes drive letters, pathnames and file extensions from the table
name parameter as shown above.
TABLE_INFO_FILTERS
This position may hold an array with several filter expressions. The filter expression may
be macro-executed in run time depending on the SR_EvalFilters( ) setup and results in a
SQL expression to be added to the WHERE clause of the SQL Statements.
If you execute SR_EvalFilters( .T. ) the expressions will be macro-executed. The default
value in SQLRDD is FALSE.
Assuming that cCustomer has the value “ACM”, after the RDD internal macro execution
of the filter expression (SR_EvalFilters(.T.)), we will have:
When we have more than one table in query (when we use SetJoin() function), SQLRDD
needs to specify the table to be used by the filter expression. To solve this problem, you
must always use the <ALIAS> argument inside the expression. So the correct expression
is:
The SQLRDD engine will translate the <ALIAS> argument into the correct alias name
before sending the statement to the database server:
TABLE_INFO_PRIMARY_KEY
This position is used to inform the RDD the primary key column to be used by the
Historic Data filters. Using historic data you get track of table updates and inserts in the
time line, and the actual date determines the visible record.
TABLE_INFO_RELATION_TYPE
This position describes how the RDD will handle the relations via SetJoin() function:
#define TABLE_INFO_RELATION_TYPE_SELECT 0
#define TABLE_INFO_RELATION_TYPE_JOIN 1
#define TABLE_INFO_RELATION_TYPE_OUTER_JOIN 2
This position is used to inform the table user name or schema. See the RDBMS
documentation to learn how this feature can be used.
TABLE_INFO_ALL_IN_CACHE
When you pass .T. in this position you tell the RDD to use cache workarea. With .F.,
ISAM Simulator will be used.
TABLE_INFO_CUSTOM_SQL
In this position you can specify a custom SQL command to be used as the table name.
Updates, Deletes and Inserts neither are nor allowed when you use this feature.
These options set the user rights for this workarea, with .T. or .F. in each position.
TABLE_INFO_HISTORIC
Notes:
When you set this option .T., it is required to supply the position
TABLE_INFO_PRIMARY_KEY with the table’s primary key;
When you set this option .T., it is required that the table was created with this option set,
so the RDD also creates the specific control fields to the Historic Management.
TABLE_INFO_RECNO_NAME
TABLE_INFO_DELETED_NAME
Use this option to set a different name to the Deleted() column. This column will be
internally used by the system to store the physical deleted() status.
TABLE_INFO_CONNECTION
Use this option to set a different connection object to this table. Connection Object can be
obtained by the SR_GetConnection() function.
Using the Connection Classes, you can send any command direct to the database system.
But unfortunately, the SQL Syntax from the different database vendors is quite different.
These differences are most evident when comparing:
• Date strings
• Joins and outer joins
• Sub queries and nested queries
• Identifiers
• Row / Table locking
• Transaction control
• Optimizations
It means that you normally have to use different SQL syntax with every target database,
which becomes a hurdle when you want to have a portable application.
With SQL Parser, all you need to get portable with the major database systems is to use
the xHarbour SQL Language. The parser “understands” the xHarbour SQL Syntax and
generates an intermediate code, which can later be used by the Code Generator to
automatically create the specific syntax supported by the SQL Server in use. This Parser
is implemented with a Bison parser, and it means a very fast and reliable routine.
The SQL Parser supports SELECT, INSERT, UPDATE and DELETE commands. See
the reference guide for the complete xHarbour SQL Syntax.
The “?” is a variable data parameter that will be supplied in the code generation phase. So
you can compile the SQL command one time and execute it several times changing the
parameters and consequently saving processor resources and gaining performance.
This component generates the SQL statement from the pCode created with the SQL
Parser’s SqlParse() function. The main function to generate the SQL code is:
You can pass parameters to the code generator function, when required by the SQL
Command:
Local apCode
Local cCommand
Local cResult
SELECT A.* FROM TABLE1 A WITH (NOLOCK) WHERE A.[ID] = ‘100’ AND
A.[NUM] = 2
You can submit the resulting query to the database server and obtain the result set with
the Connection Classes and their helper functions. Please check the reference guide to the
complete
Deleting records
SQLRDD fully supports records “marked for deletion” using an auto-created field named
“DELETED” to store the status. This column name may be changed in each workarea
with the TABLE_INFO_DELETED_NAME information array position or globally
defined with the SR_DeletedName() function.
Record numbering
SQLRDD uses an auto-created field named “SR_RECNO” to hold the record number.
The method for filling this field depends on the target database system, but usually works
with IDENTITY or ENUM.
In SQLRDD, record numbers never change and their number can be higher than the
quantity of records in the table. The PACK command has no meaning in SQLRDD if
SR_UseDeleteds(lUseDeleteds) is set to false.
SQLRDD internally translates the xBase data types (CHARACTER, DATE, MEMO,
NUMERIC and LOGICAL) into SQL data types.
...
Note for Oracle users: Memo fields under Oracle using ODBC connection are limited to
4000 bytes (VARCHAR2 datatype). Using Oracle Native Access you will not
experience such problem.
Index Management
SQLRDD fully supports complex index expressions, index bag and tag names, in the
exact same fashion as DBFCDX RDD. Drive letters, path names and file extensions are
internally eliminated from the index name, and are limited to 30 characters.
Each time you create a complex index expression, SQLRDD automatically adds a new
column in your table to store the index expression result. These columns are named
“INDKEY_001… INDKEY_999”, and the expression result is limited in 256 characters
long. The INDKEY_nnn field contents is automatically maintained wherever the
corresponding index is in use at the moment or not. This kind of index is named
“Synthetic Index” in SQLRDD.
You may want to use Synthetic Indexes practice even with regular indexes when working
with very large tables in databases like Postgres and Oracle. Benefits are best SKIP,
SEEK, GOTOP and GOBOTTOM performance with very large tables. Disadvantages are
waste of physical space and prohibition to UPDATE or INSERT in table from outside
SR_SetSyntheticIndex(.T.) Î lOldSet
Note: Index key expression length may be limited by database. Firebird has the lowest
limit we could find, so you should try to use small index keys when possible.
Additionally, SQLRDD introduces a new concept for using indexes, based on the SQL
engine capabilities:
In SQL Database Servers, indexes are used in general to search data, and not for
ordering. All of the modern database systems have an extreme optimizer and statistics
engine, and this is responsible for choosing what index will be used or not used in each
query. This can be reviewed in any RDBMS that shows the “Execution Plan”. Thus, in
SQLRDD, seeking data by any index is not a guarantee that the RDBMS will internally
use this index.
Indexes in SQLRDD can be used to declare the fields that will be used in the resulting
SQL sentence when you use a dbSeek() function, and for sorting the result set. It is not
related to the index physical existence. But if an index with the same fields used in a
query exists in the database, you have better chances to work with acceptable
performance.
Examples:
/* CODE_ID and DESCR are character, DAYS is numeric (3) and DATE_LIM date */
This sample illustrates how to create a physical index in any database system. Note that it
is not necessary to convert any data type. Functions like dbCreateInd() can be used as
well as commands.
/* CODE_ID and DESCR are character, DAYS is numeric (3) and DATE_LIM date */
• You can use functions in index expressions, but SQLRDD will criate a column in
the table to store the resulting index expression.
• You can mix data types in index expressions without having to use type
conversion functions
• You can specify an index that does not exist physically (3rd index), but it can be
low in performance
Important Notes:
• If workstation is turned off during a transaction block, RDBMS will Roll Back all
changes automatically.
• All changed and inserted lines remains locked until the end of the transaction. All
dbCoommit() calls are ignored inside transaction blocks
• Any DML command (like dbCreate(), INDEX ON…, etc.) will automatically end
any open transaction and release all locks.
• Tracelog: Trace to a dbf file named SQLLOG.DBF all the SQL commands sent to
the database system. This is very useful for debugging purposes. To start and stop
tracing, use:
SR_StartLog([<nConnection>] )
SR_StopLog( [<nConnection>] )
• TimeTrace: Trace to a dbf file named LONG_QRY.DBF all the SQL commands
sent to the database system that takes more than 1000 milliseconds to be executed
by the RDBMS. This is very useful for fine tuning your application. To change
the minimum trace time, use:
SR_StartTrace([<nConnection>] )
SR_StopTrace( [<nConnection>] )
sqlerror.log Logs all failed SQL sentences and all failed connections
<databaseName>.log Logs database-specific erros codes and events
To check for the current SQLRDD linked version, as well as the current connected
database, use the following code:
/*------------------------------------------------------------------------*/
#include "sqlrdd.ch"
#include "dbinfo.ch"
REQUEST SQLRDD
REQUEST SR_ODBC
FUNCTION MAIN(cDsn)
If empty( cDsn )
Return NIL
EndIf
If nCnn < 0
? "Connection error. See SQL1.LOG for details."
Return NIL
EndIf
i := select()
Return
/*------------------------------------------------------------------------*/
You can write your own language messages redefining the SR_Msg(nMsg) function. The
SR_Msg(nMsg) source code is supplied in APPENDIX 2.
To compile, link and execute SQLRDD in your programs, follow these steps:
REQUEST SQLRDD
And also declare one or more from the following connection classes:
Notes:
When using Postgres Native Support, you should add pgs.ch to your first PRG
file and libpq.dll to system path. These files are shipped with SQLRDD and you
can find then in xHb\dll and xHb\lib folders.
When using MySQL Native Support, you should add mysql.ch to your first
PRG file and libmysql.dll to system path. These files are shipped with SQLRDD
and you can find then in xHb\dll and xHb\lib folders.
Under Windows, the xBuild utility will automatically add libpq.lib or libmysql.lib
to your link script when it finds the includes (pgc.ch or mysql.ch) in your sources.
or
or
RddSetDefault( “SQLRDD” )
The following tips are very useful to have your application running with acceptable
performance:
1. Use correct, nom repetitive indexes. Keep in mind that every index consumes
performance when adding records. So if you already have an index on
COLUMN1 + COLUMN2, another index on COLUMN1 is useless.
4. Open your tables in SHARED mode. EXCLUSIVE mode requires much more
system resources and should be used only under extreme circumstances. To be
100% sure you are not using EXCLUSIVE table open, issue the following
command:
SR_SetFastOpen(.T.)
Example:
-----------------------------------------------------------
IF fLock()
PACK
ENDIF
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
Note: Same code runs with all supported SQL databases with no change.
Example:
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
-----------------------------------------------------------
Note: Same code runs with all supported SQL databases with no change.
8 – Synthetic Indexes: Synthetic indexes are those created by adding a hidden column in
table to hold index key expression return value (See Index Management chapter for a
better explanation about this method). Setting synthetic index on, all orders are created
this way. Benefits are best SKIP, SEEK, GOTOP and GOBOTTOM performance with
very large tables. Disadvantages are waste of physical space and prohibition to UPDATE
or INSERT in table from outside xHb application. Anyway, you can browse a million
records table as fast as a 200 records table.
This speed gain is not noticeable in all databases. Please avoid using Synthetic Indexes
with other databases than Postgres and Oracle.
SQLRDD is a RDD almost compatible with DBFCDX. Here we have a list of know
compatibility and limitation issues regarding SQLRDD:
• Any DML command (like dbCreate(), INDEX ON…, etc.) will automatically end
any open transaction and release all locks.
• If you choose to use Firebird, please check for index key size limit. It’s a known
Firebird bug.
• You can not use another workarea’s field as an index expression in SQLRDD
/*------------------------------------------------------------------*/
#include "hbsql.ch"
#include "error.ch"
#include "sqlrdd.ch"
#include "msg.ch"
if cTableName[2] == ":"
/* Remove drive letter */
cTableName := SubStr( cTableName, 3 )
endif
cOwner := SR_SetGlobalOwner()
If (!Empty(cOwner)) .and. cOwner[-1] != "."
cOwner += "."
EndIf
aRet := { upper(cTableName),;
{},;
"",;
TABLE_INFO_RELATION_TYPE_OUTER_JOIN,;
SR_SetGlobalOwner(),;
.F.,;
"",;
.T.,;
.T.,;
.T.,;
.F.,;
,;
,;
,;
cOwner + upper(cTableName) }
Return aRet
/*-------------------------------------------------------------------*/
Static nMessages := 28
GLOBAL nBaseLang := 1
GLOBAL nSecondLang := LANG_EN_US
GLOBAL nRootLang := LANG_PT_BR // Root since I do it on Brazil
Static aMsg1 := ;
{ ;
"Attempt to write to an empty table without a previous Append Blank",;
"Undefined SQL datatype: ",;
"Insert not allowed in table : ",;
"Update not allowed without a WHERE clause.",;
"Update not allowed in table : ",;
"Invalid record number : ",;
"Not connected to the SQL dabase server",;
"Error Locking line or table (record# + table + error code):",;
"Unsupported data type in : ",;
"Syntax Error in filter expression : ",;
"Error selecting IDENTITY after INSERT in table : ",;
"Delete not allowed in table : ",;
"Delete Failure ",;
"Last command sent to database : ",;
"Insert Failure ",;
"Update Failure ",;
"Error creating index : ",;
"Index Column not Found - ",;
"Invalid Index number or tag in OrdListFocus() : ",;
"Seek without active index",;
"Unsupported data type at column ",;
"Unsupported database : ",;
"Could not setup stmt option",;
"RECNO column not found (column / table): ",;
"DELETED column not found (column / table): ",;
"Invalid dbSeek or SetScope Key Argument",;
"Error Opening table in SQL database",;
"Data type mismatch in dbSeek()" ;
}
Static aMsg2 := ;
{ ;
"Tentativa de gravar um registro em tabela vazia sem antes adicionar uma linha",;
"Tipo mde dado SQL indefinido : ",;
"Inclusão não permitida na tabela : ",;
"Update não permitido SEM cláusula WHERE.",;
"Alteração não permitida na tabela : ",;
"Número de Registro inexistente :",;
"Não conectado ao servidor de banco de dados",;
"Erro travando registro ou tabela (num.registro + tabela + cod.erro):",;
"Tipo de dado não suportado em : ",;
"Erro de sitaxe em expressão de filtro : ",;
"Erro ao selecionar IDENTITY após INSERT na tabela : ",;
"Exclusão não permitida na tabela : ",;
"Erro na exclusão ",;
"Último comando enviado ao banco de dados : ",;
"Erro na inclusão ",;
"Erro na alteração ",;
"Erro criando índice : ",;
"Coluna de índice não existente - ",;
"Indice ou tag inválido em OrdListFocus() : ",;
Static aMsg3 := ;
{ ;
"Attempt to write to an empty table without a previous Append Blank",;
"Undefined SQL datatype: ",;
"Insert not allowed in table : ",;
"Update not allowed without a WHERE clause.",;
"Update not allowed in table : ",;
"Invalid record number : ",;
"Not connected to the SQL dabase server",;
"Error Locking line or table (record# + table + error code):",;
"Unsupported data type in : ",;
"Syntax Error in filter expression : ",;
"Error selecting IDENTITY after INSERT in table : ",;
"Delete not allowed in table : ",;
"Delete Failure ",;
"Last command sent to database : ",;
"Insert Failure ",;
"Update Failure ",;
"Error creating index : ",;
"Index Column not Found - ",;
"Invalid Index numner or tag in OrdListFocus() : ",;
"Seek without active index",;
"Unsupported data type at column ",;
"Unsupported database : ",;
"Could not setup stmt option",;
"RECNO column not found (column / table): ",;
"DELETED column not found (column / table): ",;
"Invalid dbSeek or SetScope Key Argument",;
"Error Opening table in SQL database",;
"Data type mismatch in dbSeek()";
}
Static aMsg4 := ;
{ ;
"Attempt to write to an empty table without a previous Append Blank",;
"Undefined SQL datatype: ",;
"Insert not allowed in table : ",;
"Update not allowed without a WHERE clause.",;
"Update not allowed in table : ",;
"Invalid record number : ",;
"Not connected to the SQL dabase server",;
"Error Locking line or table (record# + table + error code):",;
"Unsupported data type in : ",;
"Syntax Error in filter expression : ",;
"Error selecting IDENTITY after INSERT in table : ",;
"Delete not allowed in table : ",;
"Delete Failure ",;
"Last command sent to database : ",;
"Insert Failure ",;
"Update Failure ",;
"Error creating index : ",;
"Index Column not Found - ",;
"Invalid Index number or tag in OrdListFocus() : ",;
"Seek without active index",;
"Unsupported data type at column ",;
"Unsupported database : ",;
"Could not setup stmt option",;
"RECNO column not found (column / table): ",;
"DELETED column not found (column / table): ",;
"Invalid dbSeek or SetScope Key Argument",;
Static aMsg5 := ;
{ ;
"Attempt to write to an empty table without a previous Append Blank",;
"Undefined SQL datatype: ",;
"Insert not allowed in table : ",;
"Update not allowed without a WHERE clause.",;
"Update not allowed in table : ",;
"Invalid record number : ",;
"Not connected to the SQL dabase server",;
"Error Locking line or table (record# + table + error code):",;
"Unsupported data type in : ",;
"Syntax Error in filter expression : ",;
"Error selecting IDENTITY after INSERT in table : ",;
"Delete not allowed in table : ",;
"Delete Failure ",;
"Last command sent to database : ",;
"Insert Failure ",;
"Update Failure ",;
"Error creating index : ",;
"Index Column not Found - ",;
"Invalid Index number or tag in OrdListFocus() : ",;
"Seek without active index",;
"Unsupported data type at column ",;
"Unsupported database : ",;
"Could not setup stmt option",;
"RECNO column not found (column / table): ",;
"DELETED column not found (column / table): ",;
"Invalid dbSeek or SetScope Key Argument",;
"Error Opening table in SQL database",;
"Data type mismatch in dbSeek()";
}
Static aMsg6 := ;
{ ;
"Attempt to write to an empty table without a previous Append Blank",;
"Undefined SQL datatype: ",;
"Insert not allowed in table : ",;
"Update not allowed without a WHERE clause.",;
"Update not allowed in table : ",;
"Invalid record number : ",;
"Not connected to the SQL dabase server",;
"Error Locking line or table (record# + table + error code):",;
"Unsupported data type in : ",;
"Syntax Error in filter expression : ",;
"Error selecting IDENTITY after INSERT in table : ",;
"Delete not allowed in table : ",;
"Delete Failure ",;
"Last command sent to database : ",;
"Insert Failure ",;
"Update Failure ",;
"Error creating index : ",;
"Index Column not Found - ",;
"Invalid Index number or tag in OrdListFocus() : ",;
"Seek without active index",;
"Unsupported data type at column ",;
"Unsupported database : ",;
"Could not setup stmt option",;
"RECNO column not found (column / table): ",;
"DELETED column not found (column / table): ",;
"Invalid dbSeek or SetScope Key Argument",;
"Error Opening table in SQL database",;
"Data type mismatch in dbSeek()";
}
Static aMsg7 := ;
{ ;
"Attempt to write to an empty table without a previous Append Blank",;
Static aMsg
/*------------------------------------------------------------------------*/
INIT PROCEDURE SR_Init2
RETURN
/*------------------------------------------------------------------------*/