mssqlconn.pp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. MS SQL Server connection using DB-Library
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. The Original Code was created by (c) 2010 Ladislav Karrach (Windows)
  10. for the Free Pascal project.
  11. **********************************************************************
  12. MS SQL Server Client Library is required (ntwdblib.dll)
  13. - or -
  14. FreeTDS (dblib.dll)
  15. freetds.conf: (http://www.freetds.org/userguide/freetdsconf.htm)
  16. [global]
  17. tds version = 7.1
  18. client charset = UTF-8
  19. port = 1433 or instance = ... (optional)
  20. dump file = freetds.log (optional)
  21. text size = 2147483647 (optional)
  22. TMSSQLConnection properties:
  23. HostName - can be specified also as 'servername:port' or 'servername\instance'
  24. (SQL Server Browser Service must be running on server to connect to specific instance)
  25. CharSet - if you use Microsoft DB-Lib and set to 'UTF-8' then char/varchar fields will be UTF8Encoded/Decoded
  26. if you use FreeTDS DB-Lib then you must compile with iconv support (requires libiconv2.dll) or cast char/varchar to nchar/nvarchar in SELECTs
  27. Params - "AutoCommit=true" - if you don't want explicitly commit/rollback transactions
  28. "TextSize=16777216" - set maximum size of text/image data returned
  29. "ApplicationName=YourAppName" - Set the app name for the connection. MSSQL 2000 and higher only
  30. }
  31. unit MSSQLConn;
  32. {$mode objfpc}{$H+}
  33. interface
  34. uses
  35. Classes, SysUtils, sqldb, db, BufDataset,
  36. dblib;
  37. type
  38. { TMSSQLConnection }
  39. TMSSQLConnection = class(TSQLConnection)
  40. private
  41. type
  42. TServerInfo = record
  43. ServerVersion: string;
  44. ServerVersionString: string;
  45. UserName: string;
  46. end;
  47. var
  48. FDBLogin: PLOGINREC;
  49. FDBProc : PDBPROCESS;
  50. Ftds : integer; // TDS protocol version
  51. Fstatus : STATUS; // current result/rows fetch status
  52. FServerInfo: TServerInfo;
  53. function CheckError(const Ret: RETCODE): RETCODE;
  54. procedure Execute(const cmd: string); overload;
  55. procedure ExecuteDirectSQL(const Query: string);
  56. procedure CancelQuery;
  57. procedure GetParameters(cursor: TSQLCursor; AParams: TParams);
  58. function TranslateFldType(SQLDataType: integer): TFieldType;
  59. function AutoCommit: boolean;
  60. function IsSybase: boolean;
  61. protected
  62. // Overrides from TSQLConnection
  63. function GetHandle:pointer; override;
  64. function GetAsSQLText(Param : TParam) : string; overload; override;
  65. function GetConnectionCharSet: string; override;
  66. // - Connect/disconnect
  67. procedure DoInternalConnect; override;
  68. procedure DoInternalDisconnect; override;
  69. // - Handle (de)allocation
  70. function AllocateCursorHandle:TSQLCursor; override;
  71. procedure DeAllocateCursorHandle(var cursor:TSQLCursor); override;
  72. function AllocateTransactionHandle:TSQLHandle; override;
  73. // - Transaction handling
  74. function GetTransactionHandle(trans:TSQLHandle):pointer; override;
  75. function StartDBTransaction(trans:TSQLHandle; AParams:string):boolean; override;
  76. function Commit(trans:TSQLHandle):boolean; override;
  77. function Rollback(trans:TSQLHandle):boolean; override;
  78. procedure CommitRetaining(trans:TSQLHandle); override;
  79. procedure RollbackRetaining(trans:TSQLHandle); override;
  80. // - Statement handling
  81. function StrToStatementType(s : string) : TStatementType; override;
  82. procedure PrepareStatement(cursor:TSQLCursor; ATransaction:TSQLTransaction; buf:string; AParams:TParams); override;
  83. procedure UnPrepareStatement(cursor:TSQLCursor); override;
  84. // - Statement execution
  85. procedure Execute(cursor:TSQLCursor; ATransaction:TSQLTransaction; AParams:TParams); override;
  86. function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
  87. function RefreshLastInsertID(Query : TCustomSQLQuery; Field : TField): boolean; override;
  88. // - Result retrieving
  89. procedure AddFieldDefs(cursor:TSQLCursor; FieldDefs:TFieldDefs); override;
  90. function Fetch(cursor:TSQLCursor):boolean; override;
  91. function LoadField(cursor:TSQLCursor; FieldDef:TFieldDef; buffer:pointer; out CreateBlob : boolean):boolean; override;
  92. procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); override;
  93. procedure FreeFldBuffers(cursor:TSQLCursor); override;
  94. // - UpdateIndexDefs
  95. procedure UpdateIndexDefs(IndexDefs:TIndexDefs; TableName:string); override;
  96. // - Schema info
  97. function GetSchemaInfoSQL(SchemaType:TSchemaType; SchemaObjectName, SchemaObjectPattern:string):string; override;
  98. public
  99. constructor Create(AOwner : TComponent); override;
  100. function GetConnectionInfo(InfoType:TConnInfoType): string; override;
  101. procedure CreateDB; override;
  102. procedure DropDB; override;
  103. //property TDS:integer read Ftds;
  104. published
  105. // Redeclare properties from TSQLConnection
  106. property Password;
  107. property Transaction;
  108. property UserName;
  109. property CharSet;
  110. property HostName;
  111. // Redeclare properties from TDatabase
  112. property Connected;
  113. property Role;
  114. property DatabaseName;
  115. property KeepConnection;
  116. property LoginPrompt;
  117. property Params;
  118. property OnLogin;
  119. end;
  120. { TSybaseConnection }
  121. TSybaseConnection = class(TMSSQLConnection)
  122. public
  123. constructor Create(AOwner : TComponent); override;
  124. end;
  125. { EMSSQLDatabaseError }
  126. EMSSQLDatabaseError = class(ESQLDatabaseError)
  127. public
  128. property DBErrorCode: integer read ErrorCode; deprecated 'Please use ErrorCode instead of DBErrorCode'; // Feb 2014
  129. end;
  130. { TMSSQLConnectionDef }
  131. TMSSQLConnectionDef = Class(TConnectionDef)
  132. Class Function TypeName : String; override;
  133. Class Function ConnectionClass : TSQLConnectionClass; override;
  134. Class Function Description : String; override;
  135. Class Function DefaultLibraryName : String; override;
  136. Class Function LoadFunction : TLibraryLoadFunction; override;
  137. Class Function UnLoadFunction : TLibraryUnLoadFunction; override;
  138. Class Function LoadedLibraryName: string; override;
  139. end;
  140. { TSybaseConnectionDef }
  141. TSybaseConnectionDef = Class(TMSSQLConnectionDef)
  142. Class Function TypeName : String; override;
  143. Class Function ConnectionClass : TSQLConnectionClass; override;
  144. Class Function Description : String; override;
  145. end;
  146. var
  147. DBLibLibraryName: string = DBLIBDLL;
  148. implementation
  149. uses StrUtils, FmtBCD;
  150. type
  151. { TDBLibCursor }
  152. TDBLibCursor = class(TSQLCursor)
  153. private
  154. FConnection: TMSSQLConnection; // owner connection
  155. FQuery: string; // :ParamNames converted to $1,$2,..,$n
  156. FParamReplaceString: string;
  157. protected
  158. FRowsAffected: integer;
  159. function ReplaceParams(AParams: TParams): string; // replaces parameters placeholders $1,$2,..$n in FQuery with supplied values in AParams
  160. procedure Prepare(const Buf: string; AParams: TParams);
  161. procedure Execute(AParams: TParams);
  162. function Fetch: boolean;
  163. procedure Put(column: integer; out s: string); overload;
  164. public
  165. constructor Create(AConnection: TMSSQLConnection); overload;
  166. end;
  167. const
  168. SBeginTransaction = 'BEGIN TRANSACTION';
  169. SAutoCommit = 'AUTOCOMMIT';
  170. STextSize = 'TEXTSIZE';
  171. SAppName = 'APPLICATIONNAME';
  172. var
  173. DBErrorStr, DBMsgStr: AnsiString;
  174. DBErrorNo, DBMsgNo: integer;
  175. function DBErrHandler(dbproc: PDBPROCESS; severity, dberr, oserr:INT; dberrstr, oserrstr:PAnsiChar):INT; cdecl;
  176. begin
  177. DBErrorStr:=DBErrorStr+LineEnding+dberrstr;
  178. DBErrorNo :=dberr;
  179. Result :=INT_CANCEL;
  180. // for server messages with severity greater than 10 error handler is also called
  181. end;
  182. function DBMsgHandler(dbproc: PDBPROCESS; msgno: DBINT; msgstate, severity:INT; msgtext, srvname, procname:PAnsiChar; line:DBUSMALLINT):INT; cdecl;
  183. begin
  184. DBMsgStr:=DBMsgStr+LineEnding+msgtext;
  185. DBMsgNo :=msgno;
  186. Result :=0;
  187. end;
  188. { TDBLibCursor }
  189. procedure TDBLibCursor.Prepare(const Buf: string; AParams: TParams);
  190. var
  191. ParamBinding : TParamBinding;
  192. begin
  193. if assigned(AParams) and (AParams.Count > 0) then
  194. FQuery := AParams.ParseSQL(Buf, False, sqEscapeSlash in FConnection.ConnOptions, sqEscapeRepeat in FConnection.ConnOptions, psSimulated, ParamBinding, FParamReplaceString)
  195. else
  196. FQuery := Buf;
  197. FPrepared := True;
  198. end;
  199. function TDBLibCursor.ReplaceParams(AParams: TParams): string;
  200. var i: integer;
  201. ParamNames, ParamValues: array of string;
  202. begin
  203. if Assigned(AParams) and (AParams.Count > 0) then //taken from mysqlconn, pqconnection
  204. begin
  205. setlength(ParamNames, AParams.Count);
  206. setlength(ParamValues, AParams.Count);
  207. for i := 0 to AParams.Count -1 do
  208. begin
  209. ParamNames[AParams.Count-i-1] := format('%s%d', [FParamReplaceString, AParams[i].Index+1]);
  210. ParamValues[AParams.Count-i-1] := FConnection.GetAsSQLText(AParams[i]);
  211. end;
  212. Result := stringsreplace(FQuery, ParamNames, ParamValues, [rfReplaceAll]);
  213. end
  214. else
  215. Result := FQuery;
  216. end;
  217. procedure TDBLibCursor.Execute(AParams: TParams);
  218. begin
  219. Fconnection.Execute(Self, nil, AParams);
  220. end;
  221. function TDBLibCursor.Fetch: boolean;
  222. begin
  223. Result := Fconnection.Fetch(Self);
  224. end;
  225. procedure TDBLibCursor.Put(column: integer; out s: string);
  226. var
  227. data: PByte;
  228. datalen: DBINT;
  229. begin
  230. data := dbdata(Fconnection.FDBProc, column);
  231. datalen := dbdatlen(Fconnection.FDBProc, column);
  232. SetString(s, PAnsiChar(data), datalen);
  233. end;
  234. constructor TDBLibCursor.Create(AConnection: TMSSQLConnection);
  235. begin
  236. inherited Create;
  237. FConnection := AConnection;
  238. end;
  239. { TSybaseConnection }
  240. constructor TSybaseConnection.Create(AOwner: TComponent);
  241. begin
  242. inherited Create(AOwner);
  243. Ftds := DBTDS_50;
  244. end;
  245. { TMSSQLConnection }
  246. function TMSSQLConnection.IsSybase: boolean;
  247. begin
  248. Result := (Ftds=DBTDS_50) or (Ftds=DBTDS_42);
  249. end;
  250. function TMSSQLConnection.CheckError(const Ret: RETCODE): RETCODE;
  251. var E: EMSSQLDatabaseError;
  252. begin
  253. if (Ret=FAIL) or (DBErrorStr<>'') then
  254. begin
  255. // try clear all pending results to allow ROLLBACK and prevent error 10038 "Results pending"
  256. if assigned(FDBProc) then dbcancel(FDBProc);
  257. if DBErrorStr = '' then
  258. case DBErrorNo of
  259. SYBEFCON: DBErrorStr:='SQL Server connection failed!';
  260. end;
  261. E:=EMSSQLDatabaseError.CreateFmt('Error %d : %s'+LineEnding+'%s', [DBErrorNo, DBErrorStr, DBMsgStr], Self, DBErrorNo, '');
  262. DBErrorStr:='';
  263. DBMsgStr:='';
  264. raise E;
  265. end;
  266. Result:=Ret;
  267. end;
  268. constructor TMSSQLConnection.Create(AOwner: TComponent);
  269. begin
  270. inherited Create(AOwner);
  271. FConnOptions := [sqSupportEmptyDatabaseName, sqEscapeRepeat, sqImplicitTransaction, sqLastInsertID, sqSequences];
  272. //FieldNameQuoteChars:=DoubleQuotes; //default
  273. Ftds := DBTDS_UNKNOWN;
  274. end;
  275. procedure TMSSQLConnection.CreateDB;
  276. begin
  277. ExecuteDirectSQL('CREATE DATABASE '+DatabaseName);
  278. end;
  279. procedure TMSSQLConnection.DropDB;
  280. begin
  281. ExecuteDirectSQL('DROP DATABASE '+DatabaseName);
  282. end;
  283. procedure TMSSQLConnection.ExecuteDirectSQL(const Query: string);
  284. var ADatabaseName: string;
  285. begin
  286. CheckDisConnected;
  287. ADatabaseName:=DatabaseName;
  288. DatabaseName:='';
  289. try
  290. Open;
  291. Execute(Query);
  292. finally
  293. Close;
  294. DatabaseName:=ADatabaseName;
  295. end;
  296. end;
  297. procedure TMSSQLConnection.CancelQuery;
  298. begin
  299. // Cancel the query currently being retrieved, discarding all pending rows and all remaining resultsets
  300. if Fstatus = MORE_ROWS then begin
  301. repeat
  302. dbcanquery(FDBProc);
  303. until dbresults(FDBProc) <> SUCCEED;
  304. Fstatus := NO_MORE_ROWS;
  305. end;
  306. end;
  307. function TMSSQLConnection.GetHandle: pointer;
  308. begin
  309. Result:=FDBProc;
  310. end;
  311. function TMSSQLConnection.GetAsSQLText(Param: TParam): string;
  312. function IsBinary(const s: string): boolean;
  313. var i: integer;
  314. begin
  315. for i:=1 to length(s) do if s[i] < #9 then Exit(true);
  316. Exit(false);
  317. end;
  318. function StrToHex(const s: string): string;
  319. begin
  320. setlength(Result, 2*length(s));
  321. BinToHex(PChar(s), PChar(Result), length(s));
  322. end;
  323. begin
  324. if not Param.IsNull then
  325. case Param.DataType of
  326. ftBoolean:
  327. if Param.AsBoolean then
  328. Result:='1'
  329. else
  330. Result:='0';
  331. ftString, ftFixedChar, ftMemo:
  332. //if IsBinary(Param.AsString) then
  333. // Result := '0x' + StrToHex(Param.AsString)
  334. //else
  335. Result := 'N' + inherited GetAsSQLText(Param);
  336. ftDateTime:
  337. // ISO 8601 format is unambiguous; is not affected by the SET DATEFORMAT or SET LANGUAGE setting.
  338. Result := '''' + FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz', Param.AsDateTime, FSQLFormatSettings) + '''';
  339. ftBlob, ftBytes, ftVarBytes:
  340. Result := '0x' + StrToHex(Param.AsString);
  341. else
  342. Result := inherited GetAsSQLText(Param);
  343. end
  344. else
  345. Result:=inherited GetAsSQLText(Param);
  346. end;
  347. function TMSSQLConnection.GetConnectionCharSet: string;
  348. begin
  349. if CharSet = '' then
  350. Result := 'utf-8'
  351. else
  352. Result := CharSet;
  353. end;
  354. procedure TMSSQLConnection.DoInternalConnect;
  355. const
  356. DBVERSION: array[boolean] of BYTE = (DBVERSION_73, DBVERSION_100);
  357. IMPLICIT_TRANSACTIONS_OFF: array[boolean] of shortstring = ('SET IMPLICIT_TRANSACTIONS OFF', 'SET CHAINED OFF');
  358. ANSI_DEFAULTS_ON: array[boolean] of shortstring = ('SET ANSI_DEFAULTS ON', 'SET QUOTED_IDENTIFIER ON');
  359. CURSOR_CLOSE_ON_COMMIT_OFF: array[boolean] of shortstring = ('SET CURSOR_CLOSE_ON_COMMIT OFF', 'SET CLOSE ON ENDTRAN OFF');
  360. VERSION_NUMBER: array[boolean] of shortstring = ('SERVERPROPERTY(''ProductVersion'')', '@@version_number');
  361. begin
  362. // empty DatabaseName=default database defined for login
  363. inherited DoInternalConnect;
  364. InitialiseDBLib(DBLibLibraryName);
  365. if not DBLibInit then
  366. begin
  367. dbinit();
  368. dberrhandle(@DBErrHandler);
  369. dbmsghandle(@DBMsgHandler);
  370. DBLibInit:=true;
  371. end;
  372. FDBLogin:=dblogin();
  373. if FDBLogin=nil then DatabaseError('dblogin() failed!');
  374. // DBVERSION_100 is ATM not implemented by FreeTDS 0.91;
  375. // set environment variable TDSVER to 5.0: Windows: SET TDSVER=5.0, Unix/Linux: TDSVER=5.0
  376. // or in freetds.conf: include "tds version=5.0"
  377. dbsetlversion(FDBLogin, DBVERSION[IsSybase]);
  378. if UserName = '' then
  379. dbsetlsecure(FDBLogin)
  380. else
  381. begin
  382. dbsetlname(FDBLogin, PAnsiChar(UserName), DBSETUSER);
  383. dbsetlname(FDBLogin, PAnsiChar(Password), DBSETPWD);
  384. end;
  385. if CharSet = '' then
  386. dbsetlcharset(FDBLogin, 'UTF-8')
  387. else
  388. dbsetlcharset(FDBLogin, PAnsiChar(CharSet));
  389. if Params.IndexOfName(SAppName) <> -1 then
  390. dbsetlname(FDBLogin, PAnsiChar(Params.Values[SAppName]), DBSETAPP);
  391. //dbsetlname(FDBLogin, PAnsiChar(TIMEOUT_IGNORE), DBSET_LOGINTIME);
  392. dbsetlogintime(10);
  393. FDBProc := dbopen(FDBLogin, PAnsiChar(HostName));
  394. if FDBProc=nil then CheckError(FAIL);
  395. Ftds := dbtds(FDBProc);
  396. //CheckError( dbsetopt(FDBProc, DBQUOTEDIDENT, '') ); //in FreeTDS executes: "SET QUOTED_IDENTIFIER ON"
  397. //CheckError( dbsetopt(FDBProc, DBTEXTSIZE, '2147483647') ); //in FreeTDS: unimplemented, returns FAIL
  398. //CheckError( dbsetopt(FDBProc, DBTEXTLIMIT, '2147483647') ); //in FreeTDS: unimplemented, returns FAIL, but required by ntwdblib.dll
  399. //CheckError( dbsqlexec(FDBProc) ); //after setting DBTEXTSIZE option
  400. //CheckError (dbresults(FDBProc));
  401. //while dbresults(FDBProc) = SUCCEED do ;
  402. // Also SQL Server ODBC driver and Microsoft OLE DB Provider for SQL Server set ANSI_DEFAULTS to ON when connecting
  403. //Execute(ANSI_DEFAULTS_ON[IsSybase]);
  404. Execute('SET QUOTED_IDENTIFIER ON');
  405. if Params.IndexOfName(STextSize) <> -1 then
  406. Execute('SET TEXTSIZE '+Params.Values[STextSize])
  407. else
  408. Execute('SET TEXTSIZE 16777216');
  409. if AutoCommit then
  410. Execute(IMPLICIT_TRANSACTIONS_OFF[IsSybase]); //set connection to autocommit mode - default
  411. if DatabaseName <> '' then
  412. CheckError( dbuse(FDBProc, PAnsiChar(DatabaseName)) );
  413. with TDBLibCursor.Create(Self) do
  414. begin
  415. try
  416. Prepare(format('SELECT cast(%s as varchar), @@version, user_name()', [VERSION_NUMBER[IsSybase]]), nil);
  417. Execute(nil);
  418. while Fetch do
  419. begin
  420. Put(1, FServerInfo.ServerVersion);
  421. Put(2, FServerInfo.ServerVersionString);
  422. Put(3, FServerInfo.UserName);
  423. end;
  424. except
  425. FServerInfo.ServerVersion:='';
  426. FServerInfo.ServerVersionString:='';
  427. FServerInfo.UserName:='';
  428. end;
  429. Free;
  430. end;
  431. end;
  432. procedure TMSSQLConnection.DoInternalDisconnect;
  433. begin
  434. inherited DoInternalDisconnect;
  435. dbclose(FDBProc);
  436. dbfreelogin(FDBLogin);
  437. ReleaseDBLib;
  438. end;
  439. function TMSSQLConnection.AllocateCursorHandle: TSQLCursor;
  440. begin
  441. Result:=TDBLibCursor.Create(Self);
  442. end;
  443. procedure TMSSQLConnection.DeAllocateCursorHandle(var cursor: TSQLCursor);
  444. begin
  445. FreeAndNil(cursor);
  446. end;
  447. function TMSSQLConnection.StrToStatementType(s: string): TStatementType;
  448. begin
  449. s:=LowerCase(s);
  450. if s = 'exec' then
  451. Result:=stExecProcedure
  452. else
  453. Result:=inherited StrToStatementType(s);
  454. end;
  455. function TMSSQLConnection.AllocateTransactionHandle: TSQLHandle;
  456. begin
  457. Result:=nil;
  458. end;
  459. function TMSSQLConnection.GetTransactionHandle(trans: TSQLHandle): pointer;
  460. begin
  461. Result:=nil;
  462. end;
  463. function TMSSQLConnection.StartDBTransaction(trans: TSQLHandle; AParams: string): boolean;
  464. begin
  465. Result := not AutoCommit;
  466. if Result then
  467. Execute(SBeginTransaction);
  468. end;
  469. function TMSSQLConnection.Commit(trans: TSQLHandle): boolean;
  470. begin
  471. Execute('COMMIT');
  472. Result:=true;
  473. end;
  474. function TMSSQLConnection.Rollback(trans: TSQLHandle): boolean;
  475. begin
  476. Execute('IF @@TRANCOUNT>0 ROLLBACK');
  477. Result:=true;
  478. end;
  479. procedure TMSSQLConnection.CommitRetaining(trans: TSQLHandle);
  480. begin
  481. if Commit(trans) then
  482. Execute(SBeginTransaction);
  483. end;
  484. procedure TMSSQLConnection.RollbackRetaining(trans: TSQLHandle);
  485. begin
  486. if Rollback(trans) then
  487. Execute(SBeginTransaction);
  488. end;
  489. function TMSSQLConnection.AutoCommit: boolean;
  490. begin
  491. Result := StrToBoolDef(Params.Values[SAutoCommit], False);
  492. end;
  493. procedure TMSSQLConnection.PrepareStatement(cursor: TSQLCursor;
  494. ATransaction: TSQLTransaction; buf: string; AParams: TParams);
  495. begin
  496. (cursor as TDBLibCursor).Prepare(buf, AParams);
  497. end;
  498. procedure TMSSQLConnection.UnPrepareStatement(cursor: TSQLCursor);
  499. begin
  500. CancelQuery;
  501. cursor.FPrepared := False;
  502. end;
  503. procedure TMSSQLConnection.Execute(const cmd: string);
  504. begin
  505. DBErrorStr:='';
  506. DBMsgStr :='';
  507. CheckError( dbcmd(FDBProc, PAnsiChar(cmd)) );
  508. CheckError( dbsqlexec(FDBProc) );
  509. CheckError( dbresults(FDBProc) );
  510. end;
  511. procedure TMSSQLConnection.Execute(cursor: TSQLCursor; ATransaction: TSQLTransaction; AParams: TParams);
  512. var c: TDBLibCursor;
  513. cmd: string;
  514. res: RETCODE;
  515. begin
  516. c:=cursor as TDBLibCursor;
  517. if LogEvent(detParamValue) then
  518. LogParams(AParams);
  519. cmd := c.ReplaceParams(AParams);
  520. if LogEvent(detActualSQL) then
  521. Log(detActualSQL,Cmd);
  522. Execute(cmd);
  523. res := SUCCEED;
  524. repeat
  525. c.FSelectable := dbcmdrow(FDBProc)=SUCCEED;
  526. c.FRowsAffected := dbcount(FDBProc);
  527. if assigned(dbiscount) and not dbiscount(FDBProc) then
  528. c.FRowsAffected := -1;
  529. if not c.FSelectable then //Sybase stored proc.
  530. begin
  531. repeat
  532. Fstatus := dbnextrow(FDBProc);
  533. until (Fstatus = NO_MORE_ROWS) or (Fstatus = FAIL);
  534. res := CheckError( dbresults(FDBProc) );
  535. // stored procedure information (return status and output parameters)
  536. // are available only after normal results are processed
  537. //if res = NO_MORE_RESULTS then GetParameters(cursor, AParams);
  538. end;
  539. until c.FSelectable or (res = NO_MORE_RESULTS) or (res = FAIL);
  540. if res = NO_MORE_RESULTS then
  541. Fstatus := NO_MORE_ROWS
  542. else
  543. Fstatus := MORE_ROWS;
  544. end;
  545. procedure TMSSQLConnection.GetParameters(cursor: TSQLCursor; AParams: TParams);
  546. var Param: TParam;
  547. begin
  548. // Microsoft SQL Server no more returns OUTPUT parameters as a special result row
  549. // so we can not use dbret*() functions, but instead we must use dbrpc*() functions
  550. // only procedure return status number is returned
  551. if dbhasretstat(FDBProc) = 1 then
  552. begin
  553. Param := AParams.FindParam('RETURN_STATUS');
  554. if not assigned(Param) then
  555. Param := AParams.CreateParam(ftInteger, 'RETURN_STATUS', ptOutput);
  556. Param.AsInteger := dbretstatus(FDBProc);
  557. end;
  558. end;
  559. function TMSSQLConnection.RowsAffected(cursor: TSQLCursor): TRowsCount;
  560. begin
  561. if assigned(cursor) then
  562. Result := (cursor as TDBLibCursor).FRowsAffected
  563. else
  564. Result := inherited RowsAffected(cursor);
  565. end;
  566. function TMSSQLConnection.RefreshLastInsertID(Query: TCustomSQLQuery; Field: TField): boolean;
  567. var Identity: int64;
  568. begin
  569. // global variable @@IDENTITY is NUMERIC(38,0)
  570. Result:=False;
  571. if dbcmd(FDBProc, 'SELECT @@IDENTITY') = FAIL then Exit;
  572. if dbsqlexec(FDBProc) = FAIL then Exit;
  573. if dbresults(FDBProc) = FAIL then Exit;
  574. if dbnextrow(FDBProc) = FAIL then Exit;
  575. if dbconvert(FDBProc, dbcoltype(FDBProc,1), dbdata(FDBProc,1), -1, SYBINT8, @Identity, sizeof(Identity)) = -1 then Exit;
  576. // by default identity columns are ReadOnly
  577. Field.AsLargeInt := Identity;
  578. Result:=True;
  579. end;
  580. function TMSSQLConnection.TranslateFldType(SQLDataType: integer): TFieldType;
  581. begin
  582. case SQLDataType of
  583. SQLCHAR: Result:=ftFixedChar;
  584. SQLVARCHAR: Result:=ftString;
  585. SQLINT1: Result:=ftWord;
  586. SQLINT2: Result:=ftSmallInt;
  587. SQLINT4, SQLINTN: Result:=ftInteger;
  588. SYBINT8: Result:=ftLargeInt;
  589. SQLFLT4, SQLFLT8,
  590. SQLFLTN: Result:=ftFloat;
  591. SQLMONEY4, SQLMONEY,
  592. SQLMONEYN: Result:=ftCurrency;
  593. SYBMSDATE: Result:=ftDate;
  594. SYBMSTIME: Result:=ftTime;
  595. SQLDATETIM4, SQLDATETIME,
  596. SQLDATETIMN,
  597. SYBMSDATETIME2,
  598. SYBMSDATETIMEOFFSET: Result:=ftDateTime;
  599. SYBMSXML,
  600. SQLTEXT: Result:=ftMemo;
  601. SQLIMAGE: Result:=ftBlob;
  602. SQLDECIMAL, SQLNUMERIC: Result:=ftBCD;
  603. SQLBIT: Result:=ftBoolean;
  604. SQLBINARY: Result:=ftBytes;
  605. SQLVARBINARY: Result:=ftVarBytes;
  606. SYBUNIQUE: Result:=ftGuid;
  607. SYBVARIANT: Result:=ftBlob;
  608. else
  609. DatabaseErrorFmt('Unsupported SQL DataType %d "%s"', [SQLDataType, dbprtype(SQLDataType)]);
  610. Result:=ftUnknown;
  611. end;
  612. end;
  613. procedure TMSSQLConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs: TFieldDefs);
  614. var i, FieldSize: integer;
  615. FieldName: string;
  616. FieldType: TFieldType;
  617. col: DBCOL;
  618. begin
  619. col.SizeOfStruct:=sizeof(col);
  620. for i:=1 to dbnumcols(FDBProc) do
  621. begin
  622. if dbtablecolinfo(FDBProc, i, @col) = FAIL then continue;
  623. FieldName := col.Name;
  624. FieldType := TranslateFldType(col.Typ);
  625. case FieldType of
  626. ftString, ftFixedChar:
  627. begin
  628. FieldSize := col.MaxLength;
  629. if FieldSize >= $3FFFFFFF then // varchar(max)
  630. FieldType := ftMemo;
  631. end;
  632. ftBytes, ftVarBytes:
  633. begin
  634. FieldSize := col.MaxLength;
  635. if FieldSize >= $3FFFFFFF then // varbinary(max)
  636. FieldType := ftBlob;
  637. end;
  638. ftBCD:
  639. begin
  640. FieldSize := col.Scale;
  641. if (FieldSize > MaxBCDScale) or (col.Precision-col.Scale > MaxBCDPrecision-MaxBCDScale) then
  642. FieldType := ftFmtBCD;
  643. end;
  644. ftGuid:
  645. FieldSize := 38;
  646. else
  647. FieldSize := 0;
  648. if col.Identity and (FieldType = ftInteger) then
  649. FieldType := ftAutoInc;
  650. end;
  651. // identity, timestamp and calculated column are not updatable
  652. AddFieldDef(FieldDefs, i, FieldName, FieldType, FieldSize, col.Precision, True, (col.Null=0) and (not col.Identity), col.Updatable=0);
  653. end;
  654. end;
  655. function TMSSQLConnection.Fetch(cursor: TSQLCursor): boolean;
  656. begin
  657. // Compute rows resulting from the COMPUTE clause are not processed
  658. repeat
  659. Fstatus := dbnextrow(FDBProc);
  660. // In case of network failure FAIL is returned
  661. // Use dbsettime() to specify query timeout, else on Windows TCP KeepAliveTime is used, which defaults to 2 hours
  662. Result := Fstatus=REG_ROW;
  663. until Result or (Fstatus = NO_MORE_ROWS) or (Fstatus = FAIL);
  664. if Fstatus = NO_MORE_ROWS then
  665. while dbresults(FDBProc) <> NO_MORE_RESULTS do // process remaining results if there are any
  666. repeat
  667. Fstatus := dbnextrow(FDBProc);
  668. until (Fstatus = NO_MORE_ROWS) or (Fstatus = FAIL);
  669. if Fstatus = FAIL then CheckError(FAIL);
  670. end;
  671. function TMSSQLConnection.LoadField(cursor: TSQLCursor; FieldDef: TFieldDef;
  672. buffer: pointer; out CreateBlob: boolean): boolean;
  673. var i: integer;
  674. data, dest: PByte;
  675. datalen, destlen: DBINT;
  676. srctype, desttype: INT;
  677. dbdt: DBDATETIME;
  678. dbdr: DBDATEREC;
  679. dbdta: DBDATETIMEALL;
  680. bcdstr: array[0..MaxFmtBCDFractionSize+2] of AnsiChar;
  681. begin
  682. CreateBlob:=false;
  683. i:=FieldDef.FieldNo;
  684. srctype:=dbcoltype(FDBProc,i);
  685. data:=dbdata(FDBProc,i);
  686. datalen:=dbdatlen(FDBProc,i);
  687. Result:=assigned(data) and (datalen>=0);
  688. if not Result then
  689. Exit;
  690. dest:=buffer;
  691. destlen:=FieldDef.Size;
  692. case FieldDef.DataType of
  693. ftString, ftFixedChar:
  694. begin
  695. desttype:=SQLCHAR;
  696. destlen:=FieldDef.Size*FieldDef.CharSize;
  697. end;
  698. ftBytes:
  699. desttype:=SQLBINARY;
  700. ftVarBytes:
  701. begin
  702. PWord(dest)^:=datalen;
  703. inc(dest, sizeof(Word));
  704. desttype:=SQLBINARY;
  705. end;
  706. ftSmallInt, ftWord:
  707. begin
  708. desttype:=SQLINT2;
  709. destlen:=sizeof(DBSMALLINT); //smallint
  710. end;
  711. ftAutoInc,
  712. ftInteger:
  713. begin
  714. desttype:=SQLINT4;
  715. destlen:=sizeof(DBINT); //integer
  716. end;
  717. ftLargeInt:
  718. begin
  719. desttype:=SYBINT8;
  720. destlen:=sizeof(int64);
  721. end;
  722. ftCurrency,
  723. ftFloat:
  724. begin
  725. desttype:=SQLFLT8;
  726. destlen:=sizeof(DBFLT8); //double
  727. end;
  728. ftDate, ftTime,
  729. ftDateTime:
  730. if srctype in [SYBMSDATE, SYBMSTIME, SYBMSDATETIME2, SYBMSDATETIMEOFFSET] then // dbwillconvert(srctype, SYBMSDATETIME2)
  731. begin
  732. dest:=@dbdta;
  733. desttype:=SYBMSDATETIME2;
  734. destlen:=sizeof(dbdta);
  735. end
  736. else
  737. begin
  738. dest:=@dbdt;
  739. desttype:=SQLDATETIME;
  740. destlen:=sizeof(dbdt);
  741. end;
  742. ftBCD:
  743. begin
  744. // FreeTDS 0.91 does not support converting from numeric to money
  745. //desttype:=SQLMONEY;
  746. desttype:=SQLFLT8;
  747. destlen:=sizeof(currency);
  748. end;
  749. ftFmtBCD:
  750. begin
  751. {
  752. dbnum.precision:=FieldDef.Precision;
  753. dbnum.scale :=FieldDef.Size;
  754. dest:=@dbnum;
  755. desttype:=SQLNUMERIC;
  756. destlen:=sizeof(dbnum);
  757. }
  758. dest:=@bcdstr[0];
  759. desttype:=SQLCHAR;
  760. destlen:=sizeof(bcdstr);
  761. fillchar(bcdstr, destlen, 0); //required when used ntwdblib.dll
  762. end;
  763. ftBoolean:
  764. begin
  765. desttype:=SQLBIT;
  766. destlen:=sizeof(WordBool);
  767. end;
  768. ftGuid:
  769. begin
  770. desttype:=SQLCHAR;
  771. dest[ 0]:=Ord('{');
  772. dest[37]:=Ord('}');
  773. dest[38]:=0; //strings must be null-terminated
  774. Inc(dest);
  775. destlen:=36;
  776. end;
  777. ftMemo,
  778. ftBlob:
  779. begin
  780. CreateBlob:=true;
  781. Exit;
  782. end
  783. else
  784. //DatabaseErrorFmt('Tried to load field of unsupported field type %s',[FieldTypeNames[FieldDef.DataType]]);
  785. Result:=false;
  786. end;
  787. dbconvert(FDBProc, srctype, data , datalen, desttype, dest, destlen);
  788. case FieldDef.DataType of
  789. ftString, ftFixedChar:
  790. dest[datalen] := 0; //strings must be null-terminated
  791. ftDate, ftTime, ftDateTime:
  792. if desttype = SYBMSDATETIME2 then
  793. PDateTime(buffer)^ := dbdatetimeallcrack(@dbdta)
  794. else
  795. begin
  796. //detect DBDATEREC version by pre-setting dbdr
  797. dbdr.millisecond := -1;
  798. if dbdatecrack(FDBProc, @dbdr, @dbdt) = SUCCEED then
  799. begin
  800. if dbdr.millisecond = -1 then
  801. PDateTime(buffer)^ := composedatetime(
  802. encodedate(dbdr.oldyear, dbdr.oldmonth, dbdr.oldday),
  803. encodetime(dbdr.oldhour, dbdr.oldminute, dbdr.oldsecond, dbdr.oldmillisecond))
  804. else
  805. PDateTime(buffer)^ := composedatetime(
  806. encodedate(dbdr.year, dbdr.month, dbdr.day),
  807. encodetime(dbdr.hour, dbdr.minute, dbdr.second, dbdr.millisecond));
  808. end;
  809. end;
  810. ftBCD:
  811. PCurrency(buffer)^ := FloatToCurr(PDouble(buffer)^); //PCurrency(buffer)^ := dbmoneytocurr(buffer);
  812. ftFmtBCD:
  813. PBCD(buffer)^:=StrToBCD(bcdstr, FSQLFormatSettings); //PBCD(buffer)^:=dbnumerictobcd(dbnum);
  814. end;
  815. end;
  816. procedure TMSSQLConnection.LoadBlobIntoBuffer(FieldDef: TFieldDef;
  817. ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction: TSQLTransaction);
  818. var data: PByte;
  819. datalen: DBINT;
  820. begin
  821. // see also LoadField
  822. data:=dbdata(FDBProc, FieldDef.FieldNo);
  823. datalen:=dbdatlen(FDBProc, FieldDef.FieldNo);
  824. ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer, datalen);
  825. Move(data^, ABlobBuf^.BlobBuffer^.Buffer^, datalen);
  826. ABlobBuf^.BlobBuffer^.Size := datalen;
  827. end;
  828. procedure TMSSQLConnection.FreeFldBuffers(cursor: TSQLCursor);
  829. begin
  830. CancelQuery;
  831. inherited;
  832. end;
  833. procedure TMSSQLConnection.UpdateIndexDefs(IndexDefs: TIndexDefs; TableName: string);
  834. const INDEXES_QUERY: array[boolean] of string=(
  835. //MS SQL Server; TODO: we can use "execute dbo.sp_helpindex 'TableName'" when Open on Execute will fully work
  836. 'select i.name, i.indid, c.name as col_name,'+
  837. 'indexproperty(i.id, i.name, ''IsUnique''),'+
  838. 'objectproperty(o.id, ''IsPrimaryKey'') '+
  839. 'from sysindexes i '+
  840. ' join sysindexkeys k on i.id=k.id and i.indid=k.indid '+
  841. ' join syscolumns c on k.id=c.id and k.colid=c.colid '+
  842. ' left join sysobjects o on i.name=o.name and i.id=o.parent_obj '+
  843. 'where i.id=object_id(''%s'') '+
  844. 'order by k.indid, k.keyno'
  845. ,
  846. //Sybase; http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.help.ase.15.7/title.htm
  847. 'select i.name, i.indid,' +
  848. 'index_col(object_name(i.id),i.indid,c.colid) as col_name,' +
  849. '(i.status & 2)/2 as IsUnique,' +
  850. '(i.status & 2048)/2048 as IsPrimaryKey ' +
  851. 'from sysindexes i '+
  852. ' join syscolumns c on c.id=i.id and c.colid<=i.keycnt-case i.indid when 1 then 0 else 1 end ' +
  853. 'where i.id=object_id(''%s'') '+
  854. ' and i.indid between 1 and 254 '+ // indid 0 is the table name, 255 is TEXT,IMAGE
  855. 'order by i.indid, c.colid'
  856. );
  857. var qry : TSQLQuery;
  858. begin
  859. //if not assigned(Transaction) then
  860. // DatabaseError(SErrConnTransactionnSet);
  861. qry := TSQLQuery.Create(nil);
  862. qry.Transaction := Transaction;
  863. qry.Database := Self;
  864. with qry do
  865. begin
  866. ReadOnly := True;
  867. SQL.Text := format(INDEXES_QUERY[IsSybase], [TableName]);
  868. Open;
  869. end;
  870. while not qry.Eof do with IndexDefs.AddIndexDef do
  871. begin
  872. Name := trim(qry.Fields[0].AsString);
  873. Fields := trim(qry.Fields[2].AsString);
  874. if qry.Fields[3].AsInteger=1 then Options := Options + [ixUnique];
  875. if qry.Fields[4].AsInteger=1 then Options := Options + [ixPrimary];
  876. qry.Next;
  877. while (Name = trim(qry.Fields[0].AsString)) and (not qry.Eof) do
  878. begin
  879. Fields := Fields + ';' + trim(qry.Fields[2].AsString);
  880. qry.Next;
  881. end;
  882. end;
  883. qry.Close;
  884. qry.Free;
  885. end;
  886. function TMSSQLConnection.GetSchemaInfoSQL(SchemaType: TSchemaType; SchemaObjectName, SchemaObjectPattern: string): string;
  887. const SCHEMA_QUERY='select id as RECNO, db_name() as CATALOG_NAME, user_name(uid) as SCHEMA_NAME, name as %s '+
  888. 'from sysobjects '+
  889. 'where type in (%s) '+
  890. 'order by name';
  891. begin
  892. // for simplicity are used only system tables and columns, common to both MS SQL Server and Sybase
  893. case SchemaType of
  894. stTables : Result := format(SCHEMA_QUERY, ['TABLE_NAME, 1 as TABLE_TYPE', '''U''']);
  895. stSysTables : Result := format(SCHEMA_QUERY, ['TABLE_NAME, 4 as TABLE_TYPE', '''S''']);
  896. stProcedures : Result := format(SCHEMA_QUERY, ['PROCEDURE_NAME , case type when ''P'' then 1 else 2 end as PROCEDURE_TYPE', '''P'',''FN'',''IF'',''TF''']);
  897. stColumns : Result := 'select colid as RECNO, db_name() as CATALOG_NAME, user_name(uid) as SCHEMA_NAME, o.name as TABLE_NAME,'+
  898. 'c.name as COLUMN_NAME,'+
  899. 'colid as COLUMN_POSITION,'+
  900. '0 as COLUMN_TYPE,'+
  901. 'c.type as COLUMN_DATATYPE,'+
  902. ''''' as COLUMN_TYPENAME,'+
  903. 'usertype as COLUMN_SUBTYPE,'+
  904. 'prec as COLUMN_PRECISION,'+
  905. 'scale as COLUMN_SCALE,'+
  906. 'length as COLUMN_LENGTH,'+
  907. 'case when c.status&8=8 then 1 else 0 end as COLUMN_NULLABLE '+
  908. 'from syscolumns c join sysobjects o on c.id=o.id '+
  909. 'where c.id=object_id(''' + SchemaObjectName + ''') '+
  910. 'order by colid';
  911. else Result := inherited;
  912. end;
  913. end;
  914. function TMSSQLConnection.GetConnectionInfo(InfoType: TConnInfoType): string;
  915. const
  916. SERVER_TYPE: array[boolean] of string = ('Microsoft SQL Server', 'ASE'); // product_name returned in TDS login token; same like ODBC SQL_DBMS_NAME
  917. begin
  918. Result:='';
  919. try
  920. InitialiseDBLib(DBLibLibraryName);
  921. case InfoType of
  922. citServerType:
  923. Result:=SERVER_TYPE[IsSybase];
  924. citServerVersion:
  925. if Connected then
  926. Result:=FServerInfo.ServerVersion;
  927. citServerVersionString:
  928. if Connected then
  929. Result:=FServerInfo.ServerVersionString;
  930. citClientName:
  931. Result:=TMSSQLConnectionDef.LoadedLibraryName;
  932. else
  933. Result:=inherited GetConnectionInfo(InfoType);
  934. end;
  935. finally
  936. ReleaseDBLib;
  937. end;
  938. end;
  939. { TMSSQLConnectionDef }
  940. class function TMSSQLConnectionDef.TypeName: String;
  941. begin
  942. Result:='MSSQLServer';
  943. end;
  944. class function TMSSQLConnectionDef.ConnectionClass: TSQLConnectionClass;
  945. begin
  946. Result:=TMSSQLConnection;
  947. end;
  948. class function TMSSQLConnectionDef.Description: String;
  949. begin
  950. Result:='Connect to MS SQL Server via Microsoft client library or via FreeTDS db-lib';
  951. end;
  952. class function TMSSQLConnectionDef.DefaultLibraryName: String;
  953. begin
  954. Result:=DBLibLibraryName;
  955. end;
  956. class function TMSSQLConnectionDef.LoadFunction: TLibraryLoadFunction;
  957. begin
  958. Result:=@InitialiseDBLib;
  959. end;
  960. class function TMSSQLConnectionDef.UnLoadFunction: TLibraryUnLoadFunction;
  961. begin
  962. Result:=@ReleaseDBLib;
  963. end;
  964. class function TMSSQLConnectionDef.LoadedLibraryName: string;
  965. begin
  966. Result:=DBLibLoadedLibrary;
  967. end;
  968. { TSybaseConnectionDef }
  969. class function TSybaseConnectionDef.TypeName: String;
  970. begin
  971. Result:='Sybase';
  972. end;
  973. class function TSybaseConnectionDef.ConnectionClass: TSQLConnectionClass;
  974. begin
  975. Result:=TSybaseConnection;
  976. end;
  977. class function TSybaseConnectionDef.Description: String;
  978. begin
  979. Result:='Connect to Sybase SQL Server via FreeTDS db-lib';;
  980. end;
  981. initialization
  982. RegisterConnection(TMSSQLConnectionDef);
  983. RegisterConnection(TSybaseConnectionDef);
  984. finalization
  985. UnRegisterConnection(TMSSQLConnectionDef);
  986. UnRegisterConnection(TSybaseConnectionDef);
  987. end.