Mega Code Archive

 
Categories / Delphi / ADO Database
 

MS SQL Extended Stored Procedures Tutorial (Part 3)

Title: MS SQL Extended Stored Procedures Tutorial (Part 3) Question: As we saw from Part 2 the ODS API is rather complex and in places unwieldy. Here's where the power of Delphi comes to the rescue. We will create a class thst hides and encapsulates the workings of the API. The class must be able to .... 1) Tell us if it's creation has the correct number of passed parameters and there type are correct. 2) Be able to return the actual parameter values in a string list together with the count. 3) Be able to add data rows to the returned dataset via a string array. 4) Be able to create the returned data set with returned success or error. 5) handle Errors and Usage messages gracefully. The API is able to handle all sorts of input and output data types. To keep things nice and simple we will restrict ALL input parameters and output columns to type string. This is not as limiting as it seems as within the DLL we can covert Parmams[x] to any type via StrToInt(), StrToDate() etc. Also the output columns are no problem as Delphi TQuery ability to typecast fields solves this problem.. eg. Query1.Fields[0].AsString; Query1.Fields[0].AsInteger; Next .. Using the class to create a user friendly DLL template Answer: unit MahMsSqlXP; interface // ============================================================================= // // Mike Heydon 2006 // // ============================================================================= uses Classes,SysUtils,MsOdsApi; const XP_NOERROR = 0; XP_ERROR = 1; type {TXpStoredProc} TXpStoredProc = class(TObject) private FResultHeaders : array of string; FErrors : boolean; FSrvProc : SRV_PROC; FResultCols, FParamCount : integer; FParams : TStringList; FUsageHelp : string; FDataList : TList; procedure _PrintError(AErrorMsg : PChar); public constructor Create(ASrvProc : SRV_PROC; AXpProcName : string; AParamNames : array of string; AResultHeaders : array of string); destructor Destroy; override; procedure AddResultRow(AStrArray : array of string); function CreateResultSet : SRVRETCODE; // Properties property ParamCount : integer read FParamCount; property Params : TStringList read FParams; property Errors : boolean read FErrors; end; // ----------------------------------------------------------------------------- implementation // ======================================= // Create and Destroy the instance // ======================================= constructor TXpStoredProc.Create(ASrvProc : SRV_PROC; AXpProcName : string; AParamNames : array of string; AResultHeaders : array of string); var i,iRequiredCount, iMaxLen,iActualLen : integer; iType : byte; bNull : longbool; pData : pointer; sData : string; begin inherited Create; FErrors := false; FDataList := TList.Create; FParams := TStringList.Create; FParamCount := 0; FSrvProc := ASrvProc; FResultCols := length(AResultHeaders); SetLength(FResultHeaders,FResultCols); for i := low(AResultHeaders) to high(FResultHeaders) do FResultHeaders[i] := AResultHeaders[i]; // Build up usage help string FUsageHelp := 'Usage: ' + AXpProcName + ' '; for i := low(AParamNames) to high(AParamNames) do FUsageHelp := FUsageHelp + ','; Delete(FUsageHelp,length(FUsageHelp),1); // Count number of input parameters - Must Match ParamNames FParamCount := srv_RpcParams(ASrvProc); iRequiredCount := length(AParamNames); if iRequiredCount FParamCount then begin _PrintError(PChar(FUsagehelp)); FErrors := true; end; // Check all Params are of type string if not FErrors then begin for i := 1 to FParamCount do begin // Get parameter type and length information. if (srv_ParamInfo(ASrvProc,i,@iType,@iMaxLen,@iActualLen, nil,@bNull) = FAIL) then begin _PrintError('srv_paraminfo failed...'); FErrors := true; exit; end; // Make sure parameter is of char or varchar datatype (string) if (iType SRVBIGVARCHAR) and (iType SRVBIGCHAR) then begin _PrintError('Parameters MUST be of type string'); FErrors := true; exit; end; // Create Delphi string from pointer and add to Params pData := srv_ParamData(ASrvProc,i); SetLength(sData,iActualLen); move(pData^,sData[1],iActualLen); FParams.Add(sData); sData := ''; end; end; end; destructor TXpStoredProc.Destroy; var i : integer; begin for i := 0 to FDataList.Count - 1 do TStringList(FDataList[i]).Free; FreeAndNil(FDataList); FreeAndNil(FParams); inherited Destroy; end; // Internal Calls -------------------------------------------------------------- // =================================================================== // Display error message // =================================================================== procedure TXpStoredProc._PrintError(AErrorMsg : PChar); begin srv_SendMsg(FSrvProc,SRV_MSG_ERROR,20001,SRV_INFO,1, nil,0,0,AErrorMsg,SRV_NULLTERM); srv_SendDone(FSrvProc,(SRV_DONE_ERROR or SRV_DONE_MORE),0,0); end; // Public Calls ---------------------------------------------------------------- // =================================================================== // Add a result row to data array // =================================================================== procedure TXpStoredProc.AddResultRow(AStrArray : array of string); var oStrArray : TStringList; i : integer; begin if not FErrors and (length(AStrArray) 0) then begin oStrArray := TStringList.Create; for i := low(AStrArray) to high(AStrArray) do oStrArray.Add(AStrArray[i]); FDataList.Add(oStrArray); end; end; // ======================================================= // Generate the result set from data list and return // XP_ERROR(1) or XP_NOERROR(0) // ======================================================= function TXpStoredProc.CreateResultSet : SRVRETCODE; var i,ii,iResult : integer; oRow : TStringList; begin iResult := XP_NOERROR; if (not FErrors) then begin if (FResultCols 0) then begin // Define Colums for i := low(FResultHeaders) to high(FResultHeaders) do begin srv_Describe(FSrvProc,i + 1, PChar(FResultHeaders[i]),SRV_NULLTERM,SRVBIGCHAR, 8000,SRVBIGCHAR,8000,nil); end; // Send Rows for i := 0 to FDataList.Count - 1 do begin oRow := TStringList(FDataList[i]); // Add Blanks for missing cols for ii := oRow.Count to FResultCols - 1 do oRow.Add(' '); // Set data Pointers for columns for ii := 0 to oRow.Count - 1 do srv_SetColData(FSrvProc,ii + 1,PChar(oRow[ii])); // Send a row if (srv_SendRow(FSrvProc) = FAIL) then begin _PrintError('srv_sendrow failed...'); iResult := XP_ERROR; break; end; end; srv_SendDone(FSrvProc,(SRV_DONE_COUNT or SRV_DONE_MORE),0,1); end; end else iResult := XP_ERROR; Result := iResult; end; end.