mysqlconn.inc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. {$mode objfpc}{$H+}
  2. {$MACRO on}
  3. interface
  4. uses
  5. Classes, SysUtils,sqldb,db,
  6. {$IfDef mysql41}
  7. mysql41dyn;
  8. {$DEFINE TConnectionName:=TMySQL41Connection}
  9. {$DEFINE TTransactionName:=TMySQL41Transaction}
  10. {$DEFINE TCursorName:=TMySQL41Cursor}
  11. {$ELSE}
  12. {$IFDEF mysql4} // temporary backwards compatibility for Lazarus
  13. mysql40dyn;
  14. {$DEFINE TConnectionName:=TMySQLConnection}
  15. {$DEFINE TTransactionName:=TMySQLTransaction}
  16. {$DEFINE TCursorName:=TMySQLCursor}
  17. {$ELSE}
  18. mysql40dyn;
  19. {$DEFINE TConnectionName:=TMySQL40Connection}
  20. {$DEFINE TTransactionName:=TMySQL40Transaction}
  21. {$DEFINE TCursorName:=TMySQL40Cursor}
  22. {$EndIf}
  23. {$EndIf}
  24. Type
  25. TTransactionName = Class(TSQLHandle)
  26. protected
  27. end;
  28. TCursorName = Class(TSQLCursor)
  29. protected
  30. FQMySQL : PMySQL;
  31. FRes: PMYSQL_RES; { Record pointer }
  32. FNeedData : Boolean;
  33. FStatement : String;
  34. Row : MYSQL_ROW;
  35. RowsAffected : QWord;
  36. LastInsertID : QWord;
  37. end;
  38. TConnectionName = class (TSQLConnection)
  39. private
  40. FDialect: integer;
  41. FHostInfo: String;
  42. FServerInfo: String;
  43. FMySQL : PMySQL;
  44. function GetClientInfo: string;
  45. function GetServerStatus: String;
  46. procedure ConnectMySQL(var HMySQL : PMySQL;H,U,P : pchar);
  47. protected
  48. function StrToStatementType(s : string) : TStatementType; override;
  49. Procedure ConnectToServer; virtual;
  50. Procedure SelectDatabase; virtual;
  51. function MySQLDataType(AType: enum_field_types; ASize: Integer; var NewType: TFieldType; var NewSize: Integer): Boolean;
  52. function MySQLWriteData(AType: enum_field_types; ASize: Integer; Source, Dest: PChar): Integer;
  53. // SQLConnection methods
  54. procedure DoInternalConnect; override;
  55. procedure DoInternalDisconnect; override;
  56. function GetHandle : pointer; override;
  57. Function AllocateCursorHandle : TSQLCursor; override;
  58. Procedure DeAllocateCursorHandle(var cursor : TSQLCursor); override;
  59. Function AllocateTransactionHandle : TSQLHandle; override;
  60. procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
  61. procedure UnPrepareStatement(cursor:TSQLCursor); override;
  62. procedure FreeFldBuffers(cursor : TSQLCursor); override;
  63. procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams); override;
  64. procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
  65. function Fetch(cursor : TSQLCursor) : boolean; override;
  66. function LoadField(cursor : TSQLCursor;FieldDef : TfieldDef;buffer : pointer) : boolean; override;
  67. function GetTransactionHandle(trans : TSQLHandle): pointer; override;
  68. function Commit(trans : TSQLHandle) : boolean; override;
  69. function RollBack(trans : TSQLHandle) : boolean; override;
  70. function StartdbTransaction(trans : TSQLHandle; AParams : string) : boolean; override;
  71. procedure CommitRetaining(trans : TSQLHandle); override;
  72. procedure RollBackRetaining(trans : TSQLHandle); override;
  73. procedure UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string); override;
  74. Public
  75. Property ServerInfo : String Read FServerInfo;
  76. Property HostInfo : String Read FHostInfo;
  77. property ClientInfo: string read GetClientInfo;
  78. property ServerStatus : String read GetServerStatus;
  79. published
  80. property Dialect : integer read FDialect write FDialect;
  81. property DatabaseName;
  82. property HostName;
  83. property KeepConnection;
  84. property LoginPrompt;
  85. property Params;
  86. property OnLogin;
  87. end;
  88. EMySQLError = Class(Exception);
  89. implementation
  90. uses dbconst;
  91. { TConnectionName }
  92. Resourcestring
  93. SErrServerConnectFailed = 'Server connect failed.';
  94. SErrDatabaseSelectFailed = 'failed to select database: %s';
  95. SErrDatabaseCreate = 'Failed to create database: %s';
  96. SErrDatabaseDrop = 'Failed to drop database: %s';
  97. SErrNoData = 'No data for record';
  98. SErrExecuting = 'Error executing query: %s';
  99. SErrFetchingdata = 'Error fetching row data: %s';
  100. SErrGettingResult = 'Error getting result set: %s';
  101. SErrNoQueryResult = 'No result from query.';
  102. SErrNotversion41 = 'TMySQL41Connection can not work with the installed MySQL client version (%s).';
  103. SErrNotversion40 = 'TMySQL40Connection can not work with the installed MySQL client version (%s).';
  104. Procedure MySQlError(R : PMySQL;Msg: String;Comp : TComponent);
  105. Var
  106. MySQLMsg : String;
  107. begin
  108. If (R<>Nil) then
  109. begin
  110. MySQLMsg:=Strpas(mysql_error(R));
  111. DatabaseErrorFmt(Msg,[MySQLMsg],Comp);
  112. end
  113. else
  114. DatabaseError(Msg,Comp);
  115. end;
  116. function TConnectionName.StrToStatementType(s : string) : TStatementType;
  117. begin
  118. S:=Lowercase(s);
  119. if s = 'show' then exit(stSelect);
  120. result := inherited StrToStatementType(s);
  121. end;
  122. function TConnectionName.GetClientInfo: string;
  123. begin
  124. // To make it possible to call this if there's no connection yet
  125. InitialiseMysql4;
  126. Result:=strpas(mysql_get_client_info());
  127. ReleaseMysql4;
  128. end;
  129. function TConnectionName.GetServerStatus: String;
  130. begin
  131. CheckConnected;
  132. Result := mysql_stat(FMYSQL);
  133. end;
  134. procedure TConnectionName.ConnectMySQL(var HMySQL : PMySQL;H,U,P : pchar);
  135. begin
  136. HMySQL := mysql_init(HMySQL);
  137. HMySQL:=mysql_real_connect(HMySQL,PChar(H),PChar(U),Pchar(P),Nil,0,Nil,0);
  138. If (HMySQL=Nil) then
  139. MySQlError(Nil,SErrServerConnectFailed,Self);
  140. end;
  141. procedure TConnectionName.ConnectToServer;
  142. Var
  143. H,U,P : String;
  144. begin
  145. H:=HostName;
  146. U:=UserName;
  147. P:=Password;
  148. ConnectMySQL(FMySQL,pchar(H),pchar(U),pchar(P));
  149. FServerInfo := strpas(mysql_get_server_info(FMYSQL));
  150. FHostInfo := strpas(mysql_get_host_info(FMYSQL));
  151. end;
  152. procedure TConnectionName.SelectDatabase;
  153. begin
  154. if mysql_select_db(FMySQL,pchar(DatabaseName))<>0 then
  155. MySQLError(FMySQL,SErrDatabaseSelectFailed,Self);
  156. end;
  157. procedure TConnectionName.DoInternalConnect;
  158. begin
  159. InitialiseMysql4;
  160. {$IFDEF mysql41}
  161. if copy(strpas(mysql_get_client_info()),1,3)<>'4.1' then
  162. Raise EInOutError.CreateFmt(SErrNotversion41,[strpas(mysql_get_client_info())]);
  163. {$ELSE}
  164. if copy(strpas(mysql_get_client_info()),1,3)<>'4.0' then
  165. Raise EInOutError.CreateFmt(SErrNotversion40,[strpas(mysql_get_client_info())]);
  166. {$ENDIF}
  167. inherited DoInternalConnect;
  168. ConnectToServer;
  169. SelectDatabase;
  170. end;
  171. procedure TConnectionName.DoInternalDisconnect;
  172. begin
  173. inherited DoInternalDisconnect;
  174. mysql_close(FMySQL);
  175. FMySQL:=Nil;
  176. ReleaseMysql4;
  177. end;
  178. function TConnectionName.GetHandle: pointer;
  179. begin
  180. Result:=FMySQL;
  181. end;
  182. function TConnectionName.AllocateCursorHandle: TSQLCursor;
  183. begin
  184. Result:=TCursorName.Create;
  185. end;
  186. Procedure TConnectionName.DeAllocateCursorHandle(var cursor : TSQLCursor);
  187. begin
  188. FreeAndNil(cursor);
  189. end;
  190. function TConnectionName.AllocateTransactionHandle: TSQLHandle;
  191. begin
  192. // Result:=TTransactionName.Create;
  193. Result := nil;
  194. end;
  195. procedure TConnectionName.PrepareStatement(cursor: TSQLCursor;
  196. ATransaction: TSQLTransaction; buf: string;AParams : TParams);
  197. begin
  198. if assigned(AParams) and (AParams.count > 0) then
  199. DatabaseError('Parameters (not) yet supported for the MySQL SqlDB connection.',self);
  200. With Cursor as TCursorName do
  201. begin
  202. FStatement:=Buf;
  203. if FStatementType=stSelect then
  204. FNeedData:=True;
  205. ConnectMySQL(FQMySQL,FMySQL^.host,FMySQL^.user,FMySQL^.passwd);
  206. if mysql_select_db(FQMySQL,pchar(DatabaseName))<>0 then
  207. MySQLError(FQMySQL,SErrDatabaseSelectFailed,Self);
  208. end
  209. end;
  210. procedure TConnectionName.UnPrepareStatement(cursor: TSQLCursor);
  211. begin
  212. // not necessary
  213. end;
  214. procedure TConnectionName.FreeFldBuffers(cursor: TSQLCursor);
  215. Var
  216. C : TCursorName;
  217. begin
  218. C:=Cursor as TCursorName;
  219. if c.FStatementType=stSelect then
  220. c.FNeedData:=False;
  221. If (C.FRes<>Nil) then
  222. begin
  223. C.FRes:=Nil;
  224. end;
  225. if (c.FQMySQL <> Nil) then
  226. begin
  227. mysql_close(c.FQMySQL);
  228. c.FQMySQL:=Nil;
  229. end;
  230. If (C.FRes<>Nil) then
  231. begin
  232. Mysql_free_result(C.FRes);
  233. C.FRes:=Nil;
  234. end;
  235. end;
  236. procedure TConnectionName.Execute(cursor: TSQLCursor;
  237. atransaction: tSQLtransaction;AParams : TParams);
  238. Var
  239. C : TCursorName;
  240. begin
  241. C:=Cursor as TCursorName;
  242. If (C.FRes=Nil) then
  243. begin
  244. if mysql_query(c.FQMySQL,Pchar(C.FStatement))<>0 then
  245. MySQLError(c.FQMYSQL,Format(SErrExecuting,[StrPas(mysql_error(c.FQMySQL))]),Self)
  246. else
  247. begin
  248. C.RowsAffected := mysql_affected_rows(c.FQMYSQL);
  249. C.LastInsertID := mysql_insert_id(c.FQMYSQL);
  250. if C.FNeedData then
  251. C.FRes:=mysql_use_result(c.FQMySQL);
  252. end;
  253. end;
  254. end;
  255. function TConnectionName.MySQLDataType(AType: enum_field_types; ASize: Integer;
  256. var NewType: TFieldType; var NewSize: Integer): Boolean;
  257. begin
  258. Result := True;
  259. case AType of
  260. FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
  261. FIELD_TYPE_INT24:
  262. begin
  263. NewType := ftInteger;
  264. NewSize := 0;
  265. end;
  266. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  267. begin
  268. NewType := ftFloat;
  269. NewSize := 0;
  270. end;
  271. FIELD_TYPE_TIMESTAMP, FIELD_TYPE_DATETIME:
  272. begin
  273. NewType := ftDateTime;
  274. NewSize := 0;
  275. end;
  276. FIELD_TYPE_DATE:
  277. begin
  278. NewType := ftDate;
  279. NewSize := 0;
  280. end;
  281. FIELD_TYPE_TIME:
  282. begin
  283. NewType := ftTime;
  284. NewSize := 0;
  285. end;
  286. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  287. begin
  288. NewType := ftString;
  289. NewSize := ASize;
  290. end;
  291. else
  292. Result := False;
  293. end;
  294. end;
  295. procedure TConnectionName.AddFieldDefs(cursor: TSQLCursor;
  296. FieldDefs: TfieldDefs);
  297. var
  298. C : TCursorName;
  299. I, FC: Integer;
  300. field: PMYSQL_FIELD;
  301. DFT: TFieldType;
  302. DFS: Integer;
  303. begin
  304. // Writeln('MySQL: Adding fielddefs');
  305. C:=(Cursor as TCursorName);
  306. If (C.FRes=Nil) then
  307. begin
  308. // Writeln('res is nil');
  309. MySQLError(c.FQMySQL,SErrNoQueryResult,Self);
  310. end;
  311. // Writeln('MySQL: have result');
  312. FC:=mysql_num_fields(C.FRes);
  313. For I:= 0 to FC-1 do
  314. begin
  315. field := mysql_fetch_field_direct(C.FRES, I);
  316. // Writeln('MySQL: creating fielddef ',I+1);
  317. if MySQLDataType(field^.ftype, field^.length, DFT, DFS) then
  318. TFieldDef.Create(FieldDefs, field^.name, DFT, DFS, False, I+1);
  319. end;
  320. // Writeln('MySQL: Finished adding fielddefs');
  321. end;
  322. function TConnectionName.Fetch(cursor: TSQLCursor): boolean;
  323. Var
  324. C : TCursorName;
  325. begin
  326. C:=Cursor as TCursorName;
  327. C.Row:=MySQL_Fetch_row(C.FRes);
  328. Result:=(C.Row<>Nil);
  329. end;
  330. function TConnectionName.LoadField(cursor : TSQLCursor;
  331. FieldDef : TfieldDef;buffer : pointer) : boolean;
  332. var
  333. I, FC, CT: Integer;
  334. field: PMYSQL_FIELD;
  335. row : MYSQL_ROW;
  336. C : TCursorName;
  337. begin
  338. // Writeln('LoadFieldsFromBuffer');
  339. C:=Cursor as TCursorName;
  340. if C.Row=nil then
  341. begin
  342. // Writeln('LoadFieldsFromBuffer: row=nil');
  343. MySQLError(c.FQMySQL,SErrFetchingData,Self);
  344. end;
  345. Row:=C.Row;
  346. FC := mysql_num_fields(C.FRES);
  347. for I := 0 to FC-1 do
  348. begin
  349. field := mysql_fetch_field_direct(C.FRES, I);
  350. if field^.name=FieldDef.name then break;
  351. Inc(Row);
  352. end;
  353. CT := MySQLWriteData(field^.ftype, field^.length, Row^, Buffer);
  354. result := true;
  355. end;
  356. function InternalStrToFloat(S: string): Extended;
  357. var
  358. I: Integer;
  359. Tmp: string;
  360. begin
  361. Tmp := '';
  362. for I := 1 to Length(S) do
  363. begin
  364. if not (S[I] in ['0'..'9', '+', '-', 'E', 'e']) then
  365. Tmp := Tmp + DecimalSeparator
  366. else
  367. Tmp := Tmp + S[I];
  368. end;
  369. Result := StrToFloat(Tmp);
  370. end;
  371. function InternalStrToDate(S: string): TDateTime;
  372. var
  373. EY, EM, ED: Word;
  374. begin
  375. EY := StrToInt(Copy(S,1,4));
  376. EM := StrToInt(Copy(S,6,2));
  377. ED := StrToInt(Copy(S,9,2));
  378. if (EY = 0) or (EM = 0) or (ED = 0) then
  379. Result:=0
  380. else
  381. Result:=EncodeDate(EY, EM, ED);
  382. end;
  383. function InternalStrToDateTime(S: string): TDateTime;
  384. var
  385. EY, EM, ED: Word;
  386. EH, EN, ES: Word;
  387. begin
  388. EY := StrToInt(Copy(S, 1, 4));
  389. EM := StrToInt(Copy(S, 6, 2));
  390. ED := StrToInt(Copy(S, 9, 2));
  391. EH := StrToInt(Copy(S, 11, 2));
  392. EN := StrToInt(Copy(S, 14, 2));
  393. ES := StrToInt(Copy(S, 17, 2));
  394. if (EY = 0) or (EM = 0) or (ED = 0) then
  395. Result := 0
  396. else
  397. Result := EncodeDate(EY, EM, ED);
  398. Result := Result + EncodeTime(EH, EN, ES, 0);
  399. end;
  400. function InternalStrToTime(S: string): TDateTime;
  401. var
  402. EH, EM, ES: Word;
  403. begin
  404. EH := StrToInt(Copy(S, 1, 2));
  405. EM := StrToInt(Copy(S, 4, 2));
  406. ES := StrToInt(Copy(S, 7, 2));
  407. Result := EncodeTime(EH, EM, ES, 0);
  408. end;
  409. function InternalStrToTimeStamp(S: string): TDateTime;
  410. var
  411. EY, EM, ED: Word;
  412. EH, EN, ES: Word;
  413. begin
  414. EY := StrToInt(Copy(S, 1, 4));
  415. EM := StrToInt(Copy(S, 5, 2));
  416. ED := StrToInt(Copy(S, 7, 2));
  417. EH := StrToInt(Copy(S, 9, 2));
  418. EN := StrToInt(Copy(S, 11, 2));
  419. ES := StrToInt(Copy(S, 13, 2));
  420. if (EY = 0) or (EM = 0) or (ED = 0) then
  421. Result := 0
  422. else
  423. Result := EncodeDate(EY, EM, ED);
  424. Result := Result + EncodeTime(EH, EN, ES, 0);;
  425. end;
  426. function TConnectionName.MySQLWriteData(AType: enum_field_types;ASize: Integer; Source, Dest: PChar): Integer;
  427. var
  428. VI: Integer;
  429. VF: Double;
  430. VD: TDateTime;
  431. Src : String;
  432. begin
  433. Result := 0;
  434. If (Source<>Nil) Then
  435. Src:=StrPas(Source)
  436. else
  437. Src:='';
  438. case AType of
  439. FIELD_TYPE_TINY, FIELD_TYPE_SHORT, FIELD_TYPE_LONG, FIELD_TYPE_LONGLONG,
  440. FIELD_TYPE_INT24:
  441. begin
  442. Result:=SizeOf(Integer);
  443. if (Src<>'') then
  444. VI := StrToInt(Src)
  445. else
  446. VI := 0;
  447. Move(VI, Dest^, Result);
  448. end;
  449. FIELD_TYPE_DECIMAL, FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE:
  450. begin
  451. Result := SizeOf(Double);
  452. if Src <> '' then
  453. VF := InternalStrToFloat(Src)
  454. else
  455. VF := 0;
  456. Move(VF, Dest^, Result);
  457. end;
  458. FIELD_TYPE_TIMESTAMP:
  459. begin
  460. Result := SizeOf(TDateTime);
  461. if Src <> '' then
  462. VD := InternalStrToTimeStamp(Src)
  463. else
  464. VD := 0;
  465. Move(VD, Dest^, Result);
  466. end;
  467. FIELD_TYPE_DATETIME:
  468. begin
  469. Result := SizeOf(TDateTime);
  470. if Src <> '' then
  471. VD := InternalStrToDateTime(Src)
  472. else
  473. VD := 0;
  474. Move(VD, Dest^, Result);
  475. end;
  476. FIELD_TYPE_DATE:
  477. begin
  478. Result := SizeOf(TDateTime);
  479. if Src <> '' then
  480. VD := InternalStrToDate(Src)
  481. else
  482. VD := 0;
  483. Move(VD, Dest^, Result);
  484. end;
  485. FIELD_TYPE_TIME:
  486. begin
  487. Result := SizeOf(TDateTime);
  488. if Src <> '' then
  489. VD := InternalStrToTime(Src)
  490. else
  491. VD := 0;
  492. Move(VD, Dest^, Result);
  493. end;
  494. FIELD_TYPE_VAR_STRING, FIELD_TYPE_STRING, FIELD_TYPE_ENUM, FIELD_TYPE_SET:
  495. begin
  496. Result := ASize;
  497. { Write('Moving string of size ',asize,' : ');
  498. P:=Source;
  499. If (P<>nil) then
  500. While P[0]<>#0 do
  501. begin
  502. Write(p[0]);
  503. inc(p);
  504. end;
  505. Writeln;
  506. } if Src<> '' then
  507. Move(Source^, Dest^, Result)
  508. else
  509. Dest^ := #0;
  510. end;
  511. end;
  512. end;
  513. procedure TConnectionName.UpdateIndexDefs(var IndexDefs : TIndexDefs;TableName : string);
  514. var qry : TSQLQuery;
  515. begin
  516. if not assigned(Transaction) then
  517. DatabaseError(SErrConnTransactionnSet);
  518. qry := tsqlquery.Create(nil);
  519. qry.transaction := Transaction;
  520. qry.database := Self;
  521. with qry do
  522. begin
  523. ReadOnly := True;
  524. sql.clear;
  525. sql.add('show index from ' + TableName);
  526. open;
  527. end;
  528. while not qry.eof do with IndexDefs.AddIndexDef do
  529. begin
  530. Name := trim(qry.fieldbyname('Key_name').asstring);
  531. Fields := trim(qry.fieldbyname('Column_name').asstring);
  532. If Name = 'PRIMARY' then options := options + [ixPrimary];
  533. If qry.fieldbyname('Non_unique').asinteger = 0 then options := options + [ixUnique];
  534. qry.next;
  535. { while (name = qry.fields[0].asstring) and (not qry.eof) do
  536. begin
  537. Fields := Fields + ';' + trim(qry.Fields[2].asstring);
  538. qry.next;
  539. end;}
  540. end;
  541. qry.close;
  542. qry.free;
  543. end;
  544. function TConnectionName.GetTransactionHandle(trans: TSQLHandle): pointer;
  545. begin
  546. Result:=Nil;
  547. end;
  548. function TConnectionName.Commit(trans: TSQLHandle): boolean;
  549. begin
  550. // Do nothing.
  551. end;
  552. function TConnectionName.RollBack(trans: TSQLHandle): boolean;
  553. begin
  554. // Do nothing
  555. end;
  556. function TConnectionName.StartdbTransaction(trans: TSQLHandle; AParams : string): boolean;
  557. begin
  558. // Do nothing
  559. end;
  560. procedure TConnectionName.CommitRetaining(trans: TSQLHandle);
  561. begin
  562. // Do nothing
  563. end;
  564. procedure TConnectionName.RollBackRetaining(trans: TSQLHandle);
  565. begin
  566. // Do nothing
  567. end;
  568. end.