 |
MS SQL Server 7.0/2000
Extended Stored Procedure component for Delphi
A Better Way To Write Your Own Extended Stored Procedures
|
Table of content
|
People say... |
Thank you for the component — it works fine.
Winfried Schöttler (Cologne, Germany)
|
It helped me to save time and nerves.
Harald Peki (Wels, Austria)
|
Much easier to write extended procedures.
Anatoly Korobkov (Moscow, Russia)
|
A great time saver.
Brad Couper (Gold Coast, Australia)
|
|
Component feature overview
Feature |
Description |
Input/Output Parameters |
- Allowed all SQL datatypes which are listed below (except BLOBs)
- Number, order, and parameters' datatypes can vary even for a particular procedure
|
Output Recordsets |
- Allowed all SQL datatypes which are listed below (include BLOBs)
- An arbitrary number of recordsets can be returned from a procedure
|
Access to I/O Parameters and Recordset Fields |
Either by the index or by the name, by use familiar properties such as AsString , AsInteger , AsCurrency , etc.
For example:
myXProc.Fields[0].AsInteger := 2*myXProc.Params.ByName('@IntVal').AsInteger;
|
SQL Datatypes |
(recoginzed as numeric )binarybitdatetimefloatint |
or decimal (see TxpItem.AsNumeric property for details)nvarcharvarbinaryvarchar |
|
SQL BLOB Datatypes |
Note: SQL Server 7.0 doesn't supported SELECT INTO/INSERT of text/ntext/image
fields from the extended procedure recordsets while SQL 2000 (and later) does.
|
Thread Safety |
The component itself is a threadsafe (but your code should be a thread safe too). |
Small size of DLL |
Compiled DLLs are of small size (since huge Classes.pas and DB.pas aren't used by the component). |
Easy To Install |
This component does not have to be installed into the Delphi Component Palette. Just copy 2 files into any appropriate directory. |
Reliability |
A real world code: it works as a part of the complex accounting system, 7 days a week, 24 hours a day |
Pure ODS API compared with the Component
Why the Component is much more handy than pure ODS API
Thing to do |
Using ODS API |
Using Component |
Easy to start, easy to use |
– No You have to rewrite much of ODS code to match to specifics of every new procedure. |
+ Yes The component offers to you a clear and familiar Delphi's class idea. |
You have to do only your task |
– No You have much more things to implement, to control and to care about with pure ODS API. |
+ Yes The component isolates you from the ODS 'housekeeping'. You're saving your time. |
No need to handle I/O parameters, determine I/O datatypes, produce recordsets, and manage memory buffers — manually |
– No You have to do that everytime when you write every particular procedure. |
+ Yes The component transparently does it for you. |
No need to debug your ODS code and resolve it's API function specifics |
– No You should always to check the flow of your code. |
+ Yes The component has been good tested with real world applications.
Only two minor bugreports from the respective customers since the first sale! |
Simple Samples
Delphi code
File xprocdemo.dpr contains two of Extended Stored Procedures with the same functionality. They are both listed here.
Function listed first uses the TXProc component, and function listed next uses only pure Open Data Services API.
Let's see how it looks!
[+] Listing #1: Using the TXProc component. Clear and short.
// Listing #1: Using the TXProc component
function xp_Comp(Handle: TXProcHandle): Integer; cdecl;
var
i, iStart: Integer;
begin
with TXProc.Create(Handle) do // This also creates collection of the
try // input parameters if they are present.
if Params.Count > 0 then // Check if input parameters are present...
iStart := Params[0].AsInteger // Let's use the 1st input parameter!
else
iStart := 0; // No input parameters? Use default value instead.
Fields[0].Name := 'Line Number'; // OPTIONAL: Give names to the recordset columns.
Fields[1].Name := 'Value'; // But we're not required to do that.
for i := 1 to 20 do // Begin output...
begin
Fields[0].AsInteger := i; // Put data into the row
Fields[1].AsInteger := i + iStart;
Fields.Next; // Send the row
end;
finally
Free; // Finally send data back to SQL Server
end;
result := 1; // Report success (1 = SUCCEED, 0 = FAIL)
end; |
[+] Listing #2: Using pure Open Data Services API. Complicated and ambigous.
// Listing #2: Using pure Open Data Services API
function xp_PureAPI(pSrvProc: SRV_PROC): Integer; cdecl;
var
i, iStart, n: Integer;
bType: Byte;
fNull: Bool;
cbMaxLen, cbActualLen: ULONG;
begin
if srv_rpcparams(pSrvProc) = 1 then // Check if input parameters are present...
srv_paraminfo(pSrvProc, 1, @bType, // Let's use the 1st input parameter!
@cbMaxLen, @cbActualLen, // NOTE: We assume here what only one parameter
@iStart, @fNull) // of type INT can be passed!!!
// INT is an easy case: we have no need
// to allocate memory buffers.
else
iStart := 0; // No input parameters? Use default value instead.
// Describe one row with two columns of type INT and give names to them
// (Actually, you're to do that for every
// datatype and every column you want to return).
srv_describe(pSrvProc, 1, 'Line Number', SRV_NULLTERM,
SRVINTN, SizeOf(Integer),
SRVINTN, SizeOf(Integer), nil);
srv_describe(pSrvProc, 2, 'Value', SRV_NULLTERM,
SRVINTN, SizeOf(Integer),
SRVINTN, SizeOf(Integer), nil);
for i := 1 to 20 do // Begin output...
begin
srv_setcoldata(pSrvProc, 1, @i); // Put data into the row
n := i + iStart;
srv_setcoldata(pSrvProc, 2, @n);
srv_sendrow(pSrvProc); // Send the row.
end;
// Let return output parameter (just for example).
if (srv_rpcparams(pSrvProc) = 1) and
(srv_paramstatus(pSrvProc, 1) and SRV_PARAMRETURN <> 0) then
srv_paramsetoutput(pSrvProc, 1, @n, SizeOf(n), FALSE);
srv_senddone(pSrvProc, // Finally send data back to SQL Server:
(SRV_DONE_COUNT or SRV_DONE_MORE), 0, 20); // send results completion message.
result := 1; // Report success (1 = SUCCEED, 0 = FAIL)
end;
|
SQL scripts
Place the xprocdemo.dll into the SQL Server's BINN\ directory,
then open and execute the xprocdemo.sql script from the Query Analyzer.
Here is a step-by-step description what the script does.
[+] Register and unregister the extended procedure
First we need to register our procedure at the SQL Server.
Xp can be only registered in the Master database:
use master
go
sp_addextendedproc 'xp_Comp', 'xprocdemo.dll'
go
sp_addextendedproc 'xp_PureAPI', 'xprocdemo.dll'
go
In case we need to unregister some procedures:
use master
go
sp_dropextendedproc 'xp_Comp'
go
sp_dropextendedproc 'xp_PureAPI'
go
While debugging, it is often needed to replace the DLL locked by SQL Server.
To do so execute the following command from the Query Analyzer:
use master
go
DBCC xprocdemo(FREE)
go
Or, if DLL is still locked, stop SQL Server and start it again. Now DLL can be replaced.
|
[+] Test script results
Let's execute the following test script from the Query Analyzer.
Let's use integer value '15' as the input parameter for both of procedures (note using of the master.. prefix):
exec master..xp_Comp 15 -- an arbitrary int value
exec master..xp_PureAPI 15
go
The xp_Comp and xp_PureAPI will return two recordset with the same results. One of them is listed here:
Line Number Value
----------- -----------
1 16
2 17
3 18
...
18 33
19 34
20 35
(20 row(s) affected)
|
Debug in Delphi
Install SQL 7.0/2000 on your workstation
(or install Delphi on workstation where SQL Server is installed).
Register your procedure on SQL Server.
In Delphi, select from main menu Run -> Run Parameters.
In the Host Application field, specify a pathname to your
SQL Server executable (sqlservr.exe ). For SQL 7.0, this is enough.
For SQL 2000, in the Parameters field specify a command
line parameter "-sYOUR_SQL_NAME ", where YOUR_SQL_NAME
is the name assigned to your SQL Server during the installation.
Set breakpoints in your Delphi code and run project.
SQL Server will start as a console application.
You can execute your procedure from Query Analyzer and trace code in Delphi.
To exit application, press Ctrl+Pause in the SQL Server console window.
Component reference
[+] Types |
TXProcHandle
Type of the Extended Stored Procedure handle.
type TXProcHandle = SRV_PROC;
Remarks
It is simple redeclared here just to avoid to include the MSOdsAPI unit into the uses clause of your code.
|
|
TXProc, class
Create an instance of the TXProc to work with Extended Stored Procedure.
TXProc type has an alias named TXtendedProc .
[+] Properties |
Handle
Read-only value of the Extended Stored Procedure handle.
property Handle: TXProcHandle;
Remarks
This handle is used for low-level operations with the ODS API.
|
Params
Collection of the input/output parameters of type TxpItem .
property Params: TxpParams;
Remarks
Params are created automatically upon creation of the TXProc class.
|
Fields
Collection of the recordset fields of type TxpField .
property Fields: TxpFields;
Remarks
Set fields values to define the data row.
Call Fields.Next to send current row to the SQL Server.
Call Fields.Done to finish the current recordset and prepare
to start new
(Note: when you create a single recordset, you should not call Fields.Done .
It is called automatically when the TXProc being destroyed.
In other words, there is no need to call Fields.Done
when only one recordset is returned).
Field items are created automatically while you accessing them by their indexes.
Fields describe the data row what must be returned back to the SQL Server.
Column types of the returned recordset are determined by the
Field.DataType during the first call to
Fields.Next and can not be changed until Fields.Done .
|
|
[+] Methods |
GetBindToken
Returns a bind token to use it's value to organize a loopback bound connections.
function GetBindToken: String;
Remarks
Bind token is used to share a common transaction lock space with the extended procedure and it's caller from SQL Server side.
In order to receive a valid bind token value, the procedure must be executed within transaction.
Transaction must be explicitly set in T-SQL or in a stored procedure (note what triggers are always run within a transaction context).
Otherwise function will return an empty string and bound connection can not be established.
A loopback connection can be established by using ADO, ODBC, BDE, DB-Lib, or similar third-party products.
See also SQL Books Online or MSDN for "Using Bound Connections",
srv_getbindtoken, sp_bindsession ,
and examples coming with SQL Server installation on how to use loopback connections
with DB-Lib and ODBC.
|
Create
Creates an instance of extended procedure class.
constructor Create(AHandle: TXProcHandle); virtual;
- AHandle
- [in] A handle for a client connection. Passed from the SQL Server
Remarks
Constructor also creates the associated collections of
Fields and Params .
|
Destroy
Frees allocated extended procedure object.
destructor Destroy; override;
Remarks
Finalizes the pending recordset and destroys Fields and
Params collections.
|
RaiseError
Sends an informational or error message to the client.
procedure RaiseError(const Value: String; MsgNum: Integer = 50000; Severity: Integer = 10; State: Byte = 1);
- Value
- [in] A message text to be sent to the client.
- MsgNum
- [in] A message number
- Severity
- [in] Specifies the error severity.
- State
- [in] An error state number for the current message.
Remarks
You may only specify Value and keep other values by default. This simplifies use of that method in most cases.
See SQL Books Online, srv_sendmsg , where input parameter values are explained.
|
|
TxpItems, class
A base class both for Params and Fields collections.
[+] Properties |
Items
A default class property. Returns a collection item by its index.
property Items[Index: Integer]: TxpItem;
Remarks
When Index is out of bounds, nil is returned.
Normally you access to the items of Params and
Fields directly:
if Params[0].AsInteger <> 0 then
Fields[i].AsInteger := Fields[i].AsInteger * Params[0].AsInteger;
|
|
[+] Methods |
ByName
Provides access to fields and params by their names.
function ByName(const Value: String): TxpItem; virtual;
Remarks
Accessing input/output procedure parameter by the name is possible only if the
param's name was specified during the call from the SQL Server.
Field's name defines the column's name in the resulting recordset.
|
Clear
Deletes all items from list.
procedure Clear;
|
Count
Returns the number of the items in list.
function Count: Integer;
|
|
TxpParams, class
Class TxpParams is derived from TxpItems . It is used to access to I/O procedure parameters.
Class TxpParams has only overriden the TxpItems 's constructor and destructor.
When instantiating TXProc , then it's constructor creates TxpParams ,
which constructor, in turn, reads data and attributes of the I/O parameters.
When TXProc destroys, then it calls to TxpParams destructor,
which, in turn, sends data of output parameters back to SQL Server.
Note: Parameters of the particular procedure are not fixed either by their number, datatype, and position.
Any number of parameters, of any valid datatypes, and in any order can be passed to the same procedure.
This gives us a kind of great flexibility, but here is also our responsibility to handle the input parameters properly.
TxpFields, class
Class TxpFields is derived from TxpItems .
Fields are used to create and return recordsets from the procedure.
[+] Properties |
Items
Returns field at index Index .
property Items[Index: Integer]: TxpItem;
Remarks
If the field does not exist, it is created.
Also all not existing fields from range 0 to Index-1
are created. This makes it possible to assign values to the fields in any order.
|
|
[+] Methods |
Next
Sends data row to the SQL Server and then sets all field values to NULL.
procedure Next;
|
Done
Finalizes sending the recordset and deletes the field definitions.
procedure Done;
Remarks
This method is called automatically during the execution of TXProc.Destroy
This means you have no need to call to Fields.Done when you return a single recordset.
|
|
TxpItem, class
A base class for the items of the Params and Fields collections.
Provides basic properties and methods to read and write data values.
[+] Properties |
Size
Read-only. Returns actual length of the allocated data, in bytes.
property Size: DWORD;
|
MaxSize
Read/write value of maximum allowed data size of the recordset column, in bytes.
Can reduce memory overhead when return large recordsets.
property MaxSize: DWORD;
Remarks
This method is only effective for items of Fields
(e.g. only for the recordset columns), and
datatypes of variable length (such as ftBlob ,
ftMemo , ftBytes , ftString
and ftWideString ).
For example, if guaranteed that values of the particular varchar column
did not exceed, say, 50 bytes, then set MaxSize of that column to 50.
Set MaxSize to 0 to restore default maximum data length for the column.
Important Note: MaxSize can be set only before Fields.Next executed
for the first time or after Fields.Done
(and again, before first Fields.Next since Fields.Done ).
Use MaxSize with items of Params has no effect.
Default Maximum Lengths:
ftBlob, ftMemo : MAXLONG bytes;
ftBytes, ftString : 8000 bytes;
ftWideString : 4000 bytes;
For other datatypes, value of Size() is returned.
|
IsNull
Read-only. Returns True when item has no data
property IsNull: Boolean;
|
DataType
Read-only value of the datatype what the item holds.
property DataType: TFieldType;
Remarks
Type TFieldType is compatible with the same Delphi type.
It is simply redeclared here to avoid using the huge DB.pas .
|
Output
Read-only. Returns True when the parameter has OUTPUT
modifier while executing procedure from the SQL Server.
property Output: Boolean;
Remarks
Valid only for Params .
|
AsBlob
Read-only. Returns value of field of SQL type image .
property AsBlob: Pointer;
Remarks
Valid only for Fields .
Returns pointer to an allocated data buffer.
Call item's method size() to read the data buffer's length.
To set a BLOB field value, call SetBlob().
|
AsBoolean
Read/write value of type bit .
property AsBoolean: Boolean;
|
AsBytes
Read/write value of type varbinary .
property AsBytes: String;
|
AsCurrency
Read/write value of type money .
property AsCurrency: Currency;
|
AsDateTime
Read/write value of type datetime .
property AsDateTime: TDateTime;
|
AsFloat
Read/write value of type float .
property AsFloat: Double;
|
AsInteger
Read/write value of type int .
property AsInteger: Integer;
|
AsString
Read/write value of type varchar .
property AsString: String;
Remarks
Fields of type ftString and ftMemo are always
returned in Unicode (as nvarchar and ntext ).
|
AsMemo
Read/write value of type text/ntext .
property AsMemo: String;
Remarks
Valid only for Fields since it is impossible to pass
text/ntext to the procedure parameters.
See also remarks for AsString .
|
AsNumeric
Read/write value of type numeric (decimal) .
property AsNumeric: Double;
Remarks
This datatype is currently supported with the limited precision due to the conversion problems.
It uses precision of 38 (maximum allowed) and scale of 10.
SQL Server 2000 introduces a new datatype — bigint . API recognizes it as numeric (decimal) .
|
AsWideString
Read/write value of type nvarchar .
property AsWideString: WideString;
|
|
[+] Methods |
SetBlob
Sets value of field of SQL type image .
procedure SetBlob(ABuf: Pointer; ALen: DWORD);
- ABuf
- [in] Buffer containing the data to be set.
- ALen
- [in] Length of the data in ABuf
Remarks
Valid only for Fields .
You need to allocate memory buffer, fill it with data, and pass that buffer and it's length to SetBlob.
Then SetBlob will deallocate item's current data and set it to it's new value specified by ABuf.
After that you may not deallocate this memory buffer manually.
If you need to free item's data, call it's Clear method.
See also properties Blob and Size .
|
Clear
Sets item value to NULL.
procedure Clear;
|
|
Download Demo
Demo Package | Download Now! |
- Manual in HTML (is what you currently read)
- Microsoft Open Data Services API unit (
MSODSAPI.pas , a Srv.h translation)
- Component unit interface part, no full source code (
XProc.int )
- Component compiled demo units for Delphi 5,6,7 (
XProc.dcu5 , XProc.dcu6 ,
and XProc.dcu7 respectively) with limited demo functionality:
only parameters and fields of SQL datatype int can be accessed
(see TxpItem.AsInteger property);
supported recordsets; not supported Output parameters
- Sample project (source code, compiled DLL, test T-SQL script)
- License Agreement
|
xprocdemo.zip (80K) |
Order Products
Products To Order |
Volume Discount Prices |
Purchase |
XProc.pas - component for Delphi 5,6,7 (full functional, WITH FULL SOURCE CODE)
|
Show |
Order Now |
DCUs for Delphi 5,6,7 (full functional, NO SOURCE) |
Show |
Order Now |
DCU for Delphi 5 (full functional, NO SOURCE) |
Show |
Order Now |
DCU for Delphi 6 (full functional, NO SOURCE) |
Show |
Order Now |
DCU for Delphi 7 (full functional, NO SOURCE) |
Show |
Order Now |
LicenseProducts are licensed on per-developer basis. For example, team of 3 developers of extended stored procedures needs to purchase 3 copies of the product.
DeliveryBy the email in the short time since you have to order.
Bonus Offers
LicenseThe bonus products are licensed for the same number of licenses as for the master product.
For example, when order 3 copy of the XProc component, then receive 3 licenses for "File Read/Write procedures" and "MC FolderJump" as well.
|