sqldbpool.pp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 1999-2000 by Free Pascal team.
  4. A sqldb connection pooling class framework
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit sqldbpool;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$mode objfpc}
  15. {$H+}
  16. interface
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. uses
  19. System.Classes, System.SysUtils, Data.Db, Data.Sqldb, Data.SqlDb.Pq, System.SyncObjs, System.Contnrs;
  20. {$ELSE FPC_DOTTEDUNITS}
  21. uses
  22. Classes, SysUtils, db, sqldb, pqconnection, syncobjs, contnrs;
  23. {$ENDIF FPC_DOTTEDUNITS}
  24. const
  25. DefaultDisconnectTimeOut = 10*60; // Number of seconds before connection is considered old and is discarded.
  26. type
  27. TPoolLogEvent = procedure(Sender : TObject; Const Msg : string) of object;
  28. ESQLDBPool = Class(EDatabaseError);
  29. { TSQLConnectionDef }
  30. { TSQLDBConnectionDef }
  31. TSQLDBConnectionDef = Class(TCollectionItem)
  32. private
  33. FConnectionClass: TSQLConnectionClass;
  34. FConnectionType: String;
  35. FDatabaseName: UTF8String;
  36. FEnabled: Boolean;
  37. FHostName: UTF8String;
  38. FName: UTF8String;
  39. FParams: TStrings;
  40. FPassword: UTF8string;
  41. FRole: UTF8String;
  42. FUserName: UTF8String;
  43. FKey : UTF8String;
  44. FCharSet : UTF8String;
  45. procedure DoChange(Sender: TObject);
  46. function GetPort: Word;
  47. procedure SetCharSet(AValue: UTF8String);
  48. procedure SetConnectionType(AValue: String);
  49. procedure SetDatabaseName(AValue: UTF8String);
  50. procedure SetHostName(AValue: UTF8String);
  51. procedure SetParams(AValue: TStrings);
  52. procedure SetPassword(AValue: UTF8string);
  53. procedure SetPort(AValue: Word);
  54. procedure SetRole(AValue: UTF8String);
  55. procedure SetUserName(AValue: UTF8String);
  56. Protected
  57. procedure AssignTo(Dest: TPersistent); override;
  58. procedure ClearKey;
  59. function GetName : UTF8String; virtual;
  60. Function GetDisplayName: string; override;
  61. function CreateKey : String; virtual;
  62. Public
  63. Constructor Create(ACollection: TCollection); override;
  64. Destructor Destroy; override;
  65. Procedure Assign(Source: TPersistent); override;
  66. Property ConnectionClass : TSQLConnectionClass Read FConnectionClass Write FConnectionClass;
  67. function GetDescription(Full: Boolean=False): string;
  68. Function ToString: string; override;
  69. Published
  70. // TSQLConnector type
  71. Property ConnectionType : String read FConnectionType write SetConnectionType;
  72. // Name for this connection
  73. Property Name : UTF8String read GetName write FName;
  74. // Database database name
  75. Property DatabaseName : UTF8String read FDatabaseName write SetDatabaseName;
  76. // Database hostname
  77. Property HostName : UTF8String read FHostName write SetHostName;
  78. // Database username
  79. Property UserName : UTF8String read FUserName write SetUserName;
  80. // Database role
  81. Property Role : UTF8String read FRole write SetRole;
  82. // Database user password
  83. Property Password : UTF8string read FPassword write SetPassword;
  84. // Other parameters
  85. Property Params : TStrings Read FParams Write SetParams;
  86. // Stored in Params.
  87. // Database character set
  88. Property CharSet : UTF8String Read FCharSet Write SetCharSet;
  89. // Port
  90. Property Port : Word Read GetPort Write SetPort;
  91. // Allow this connection to be used ?
  92. Property Enabled : Boolean Read FEnabled Write FEnabled default true;
  93. end;
  94. { TConnectionPoolData }
  95. TConnectionPoolData = Class(TObject)
  96. private
  97. FConnection: TSQLConnection;
  98. FLastUsed: TDateTime;
  99. FLocked: Boolean;
  100. Public
  101. Constructor Create(aConnection : TSQLConnection; aLocked : Boolean = true);
  102. Destructor Destroy; override;
  103. Procedure Lock;
  104. Procedure Unlock;
  105. Procedure FreeConnection;
  106. Property Connection : TSQLConnection Read FConnection;
  107. Property LastUsed : TDateTime Read FLastUsed Write FLastUsed;
  108. Property Locked : Boolean Read FLocked;
  109. end;
  110. { TSQLConnectionHelper }
  111. TSQLConnectionHelper = class helper for TSQLConnection
  112. Function GetDescription(Full : Boolean) : string;
  113. end;
  114. { TConnectionList }
  115. TConnectionList = Class (TFPObjectList)
  116. Private
  117. FonLog: TPoolLogEvent;
  118. FDisconnectTimeout: Integer;
  119. FLock : TCriticalSection;
  120. Protected
  121. Procedure Dolog(Const Msg : String);
  122. Procedure DoLog(Const Fmt : String; Args : Array of const);
  123. Function DoDisconnectOld(aTimeOut : Integer = -1) : Integer; virtual;
  124. function CreatePoolData(aConnection : TSQLConnection; aLocked : Boolean = True) : TConnectionPoolData;
  125. Public
  126. Constructor Create; reintroduce;
  127. Destructor Destroy; override;
  128. Procedure DisconnectAll;
  129. Function DisconnectOld(aTimeOut : Integer = -1) : Integer;
  130. function AddConnection (aConnection : TSQLConnection; aLocked : Boolean = True) : TConnectionPoolData;
  131. Function PopConnection : TSQLConnection;
  132. Function UnlockConnection(aConnection : TSQLConnection) : boolean;
  133. Property DisconnectTimeout : Integer Read FDisconnectTimeout Write FDisconnectTimeout;
  134. Property OnLog : TPoolLogEvent Read FonLog Write FOnLog;
  135. end;
  136. { TSQLDBConnectionPool }
  137. TSQLDBConnectionPool = class(TComponent)
  138. private
  139. FonLog: TPoolLogEvent;
  140. FPool : TFPObjectHashTable;
  141. FLock : TCriticalSection;
  142. procedure DisconnectAll;
  143. function GetCount: longword;
  144. protected
  145. Function CreateList : TConnectionList; virtual;
  146. Procedure Dolog(Const Msg : String);
  147. Procedure DoLog(Const Fmt : String; Args : Array of const);
  148. procedure Lock;
  149. procedure Unlock;
  150. function CreateKey(aDef : TSQLDBConnectionDef) : String; virtual;
  151. function CreateDef: TSQLDBConnectionDef;
  152. function DoFindConnection(const aConnectionDef: TSQLDBConnectionDef): TSQLConnection; virtual;
  153. procedure DoDisconnect(Item: TObject; const Key: ansistring; var Continue: Boolean);
  154. public
  155. Constructor Create(aOwner : TComponent); override;
  156. Destructor Destroy; override;
  157. function CountConnections(aClass : TSQLConnectionClass; const aDatabaseName,aHostName,aUserName,aPassword: string; aParams:TStrings = nil):Integer;
  158. function CountConnections(aInstance : TSQLConnection):Integer;
  159. function CountConnections(aDef : TSQLDBConnectionDef):Integer;
  160. Function CountAllConnections : Integer;
  161. function FindConnection(aClass : TSQLConnectionClass; const aDatabaseName,aHostName,aUserName,aPassword: string; aParams:TStrings = nil):TSQLConnection;
  162. function FindConnection(const aConnectionDef : TSQLDBConnectionDef):TSQLConnection;
  163. procedure AddConnection(aConnection: TSQLConnection; aLocked: Boolean=True);
  164. function ReleaseConnection(aConnection: TSQLConnection) : Boolean;
  165. Property OnLog : TPoolLogEvent Read FonLog Write FOnLog;
  166. end;
  167. { TTypedConnectionPool }
  168. Generic TTypedConnectionPool<T: TSQLConnection> = class(TSQLDBConnectionPool)
  169. public
  170. function FindConnection(const aDatabaseName:string; const aHostName:string; const aUserName:string; const aPassword:string; aParams:TStrings=nil):T; overload;
  171. end;
  172. { TSQLDBConnectionDefList }
  173. TSQLDBConnectionDefList = Class(TOwnedCollection)
  174. private
  175. function GetD(aIndex : Integer): TSQLDBConnectionDef;
  176. procedure SetD(aIndex : Integer; AValue: TSQLDBConnectionDef);
  177. Public
  178. Function IndexOf(const aName : UTF8String) : Integer;
  179. Function Find(const aName : UTF8String) : TSQLDBConnectionDef;
  180. Function Get(const aName : UTF8String) : TSQLDBConnectionDef;
  181. Property Definitions[aIndex : Integer] : TSQLDBConnectionDef Read GetD Write SetD; default;
  182. end;
  183. { TSQLDBConnectionmanager }
  184. TSQLDBConnectionmanager = Class(TComponent)
  185. private
  186. FConnectionOwner: TComponent;
  187. FDefinitions: TSQLDBConnectionDefList;
  188. FMaxDBConnections: Word;
  189. FMaxTotalConnections: Cardinal;
  190. FOnLog: TPoolLogEvent;
  191. FPool : TSQLDBConnectionPool;
  192. FMyPool : TSQLDBConnectionPool;
  193. FLogEvents : TDBEventTypes;
  194. procedure SetConnectionOwner(AValue: TComponent);
  195. procedure SetDefinitions(AValue: TSQLDBConnectionDefList);
  196. procedure SetOnLog(AValue: TPoolLogEvent);
  197. procedure SetPool(AValue: TSQLDBConnectionPool);
  198. Protected
  199. Procedure DoLog(const Msg : String);
  200. Procedure DoLog(const Fmt : String; const aArgs : Array of const);
  201. function NewConnectionAllowed(aDef: TSQLDBConnectionDef; out aReason: string): Boolean; virtual;
  202. Procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  203. Function CreatePool : TSQLDBConnectionPool; virtual;
  204. Function CreateDefinitionList : TSQLDBConnectionDefList; virtual;
  205. Public
  206. Constructor Create(aOwner : TComponent); override;
  207. Destructor Destroy; override;
  208. Function CreateConnection(const aDef : TSQLDBConnectionDef; addToPool : Boolean) : TSQLConnection;
  209. Function CreateConnection(const aName : string; addToPool : Boolean) : TSQLConnection;
  210. Function GetConnection(const aDef : TSQLDBConnectionDef) : TSQLConnection;
  211. Function GetConnection(const aName : string) : TSQLConnection;
  212. Function ReleaseConnection(aConnection : TSQLConnection) : Boolean;
  213. Published
  214. Property Pool : TSQLDBConnectionPool Read FPool Write SetPool;
  215. Property Definitions : TSQLDBConnectionDefList Read FDefinitions Write SetDefinitions;
  216. Property MaxDBConnections : Word Read FMaxDBConnections Write FMaxDBConnections;
  217. Property MaxTotalConnections : Cardinal Read FMaxTotalConnections Write FMaxTotalConnections;
  218. Property ConnectionOwner : TComponent Read FConnectionOwner Write SetConnectionOwner;
  219. Property OnLog : TPoolLogEvent Read FOnLog Write SetOnLog;
  220. Property LogEvents : TDBEventTypes Read FLogEvents Write FLogEvents;
  221. end;
  222. implementation
  223. {$IFDEF FPC_DOTTEDUNITS}
  224. uses System.TypInfo, System.DateUtils;
  225. {$ELSE FPC_DOTTEDUNITS}
  226. uses typinfo, dateutils;
  227. {$ENDIF FPC_DOTTEDUNITS}
  228. Resourcestring
  229. SFindingConnection = 'Finding Connection (%s)';
  230. SFoundConnection = 'Found Connection (%s) : %x';
  231. SNoSuchConnection = 'No such Connection (%s)';
  232. SErrorDisconnecting = 'Error %s disconnecting connections : %s';
  233. SCreatingNewConnection = 'Creating new connection for connection definition (%s)';
  234. STimeoutReached = 'Timeout (%d>%d) reached, freeing connection (%s)';
  235. SReleasingConnections = 'Releasing connections (%s) (Current count: %d)';
  236. SErrCannotCreateNewConnection = 'Cannot create new connection for (%s): %s';
  237. SErrMaxNumberOfDefConnections = 'Max number of connections (%d) for this connection (%s) is reached';
  238. SErrMaxTotalConnectionReached = 'Max total number of connections (%d) is reached';
  239. SErrFreeingConnection = 'Error %s freeing connection %d : %s';
  240. { TSQLConnectionHelper }
  241. function TSQLConnectionHelper.GetDescription(Full: Boolean): string;
  242. Procedure AddTo(const aName,aValue : String);
  243. begin
  244. if aValue='' then
  245. exit;
  246. if Result<>'' then
  247. Result:=Result+', ';
  248. Result:=Result+aName+': '+aValue;
  249. end;
  250. var
  251. aPort : integer;
  252. begin
  253. Result:='';
  254. AddTo('Name',Name);
  255. AddTo('Host',HostName);
  256. AddTo('Database',DatabaseName);
  257. AddTo('User',Username);
  258. AddTo('Charset',CharSet);
  259. if IsPublishedProp(Self,'Port') then
  260. if PropIsType(Self,'Port',tkInteger) then
  261. begin
  262. aPort:=GetOrdProp(Self,'Port');
  263. if aPort>0 then
  264. AddTo('Port',IntToStr(aPort));
  265. end;
  266. if Full then
  267. begin
  268. AddTo('Password',Password);
  269. if Params.Count>0 then
  270. AddTo('Params',Params.CommaText);
  271. end;
  272. end;
  273. { TSQLDBConnectionDefList }
  274. function TSQLDBConnectionDefList.GetD(aIndex : Integer): TSQLDBConnectionDef;
  275. begin
  276. Result:=TSQLDBConnectionDef(Items[aIndex])
  277. end;
  278. procedure TSQLDBConnectionDefList.SetD(aIndex : Integer; AValue: TSQLDBConnectionDef
  279. );
  280. begin
  281. Items[aIndex]:=aValue;
  282. end;
  283. function TSQLDBConnectionDefList.IndexOf(const aName: UTF8String): Integer;
  284. begin
  285. Result:=Count-1;
  286. While (Result>=0) and not SameText(aName,GetD(Result).Name) do
  287. Dec(Result);
  288. end;
  289. function TSQLDBConnectionDefList.Find(const aName: UTF8String): TSQLDBConnectionDef;
  290. Var
  291. Idx : Integer;
  292. begin
  293. Result:=Nil;
  294. Idx:=IndexOf(aName);
  295. if Idx<>-1 then
  296. Result:=GetD(Idx);
  297. end;
  298. function TSQLDBConnectionDefList.Get(const aName: UTF8String): TSQLDBConnectionDef;
  299. begin
  300. Result:=Find(aName);
  301. if Result=Nil then
  302. end;
  303. { TSQLDBConnectionDef }
  304. procedure TSQLDBConnectionDef.DoChange(Sender: TObject);
  305. begin
  306. ClearKey;
  307. end;
  308. procedure TSQLDBConnectionDef.ClearKey;
  309. begin
  310. FKey:='';
  311. end;
  312. function TSQLDBConnectionDef.GetName: UTF8String;
  313. begin
  314. Result:=FName;
  315. end;
  316. function TSQLDBConnectionDef.GetPort: Word;
  317. begin
  318. Result:=StrToIntDef(FParams.Values['port'],0);
  319. end;
  320. procedure TSQLDBConnectionDef.SetCharSet(AValue: UTF8String);
  321. begin
  322. FCharSet :=aValue;
  323. ClearKey;
  324. end;
  325. procedure TSQLDBConnectionDef.SetConnectionType(AValue: String);
  326. Var
  327. Def : TConnectionDef;
  328. begin
  329. if FConnectionType=AValue then Exit;
  330. FConnectionType:=AValue;
  331. if FConnectionType<>'' then
  332. begin
  333. Def:=GetConnectionDef(aValue);
  334. if Def<>Nil then
  335. ConnectionClass:=Def.ConnectionClass
  336. else
  337. ConnectionClass:=TSQLConnector;
  338. end
  339. else
  340. ConnectionClass:=Nil;
  341. end;
  342. procedure TSQLDBConnectionDef.SetDatabaseName(AValue: UTF8String);
  343. begin
  344. if FDatabaseName=AValue then Exit;
  345. FDatabaseName:=AValue;
  346. ClearKey;
  347. end;
  348. procedure TSQLDBConnectionDef.SetHostName(AValue: UTF8String);
  349. begin
  350. if FHostName=AValue then Exit;
  351. FHostName:=AValue;
  352. ClearKey;
  353. end;
  354. procedure TSQLDBConnectionDef.SetParams(AValue: TStrings);
  355. begin
  356. FParams.Assign(aValue);
  357. ClearKey;
  358. end;
  359. procedure TSQLDBConnectionDef.SetPassword(AValue: UTF8string);
  360. begin
  361. if FPassword=AValue then Exit;
  362. FPassword:=AValue;
  363. ClearKey;
  364. end;
  365. procedure TSQLDBConnectionDef.SetPort(AValue: Word);
  366. begin
  367. if aValue=0 then
  368. FParams.Values['port']:=''
  369. else
  370. FParams.Values['port']:=IntToStr(aValue)
  371. end;
  372. procedure TSQLDBConnectionDef.SetRole(AValue: UTF8String);
  373. begin
  374. if FRole=AValue then Exit;
  375. FRole:=AValue;
  376. ClearKey;
  377. end;
  378. procedure TSQLDBConnectionDef.SetUserName(AValue: UTF8String);
  379. begin
  380. if FUserName=AValue then Exit;
  381. FUserName:=AValue;
  382. ClearKey;
  383. end;
  384. procedure TSQLDBConnectionDef.AssignTo(Dest: TPersistent);
  385. var
  386. Conn : TSQLConnection absolute Dest;
  387. begin
  388. if Dest is TSQLDBConnectionDef then
  389. Dest.Assign(Self)
  390. else if Dest is TSQLConnection then
  391. begin
  392. Conn.DatabaseName := FDatabaseName;
  393. Conn.HostName := FHostName;
  394. Conn.Password := FPassword;
  395. Conn.UserName := FUserName;
  396. Conn.Role:=FRole;
  397. Conn.CharSet:=FCharSet;
  398. Conn.Params.Assign(Self.Params);
  399. if Conn is TSQLConnector then
  400. TSQLConnector(Conn).ConnectorType:=Self.ConnectionType;
  401. end
  402. else
  403. inherited AssignTo(Dest);
  404. end;
  405. function TSQLDBConnectionDef.GetDisplayName: string;
  406. begin
  407. Result:=Name;
  408. end;
  409. function TSQLDBConnectionDef.CreateKey: String;
  410. Var
  411. S : TStringList;
  412. N : String;
  413. begin
  414. if FKey<>'' then
  415. Exit(FKey);
  416. if Assigned(ConnectionClass) then
  417. N:=ConnectionClass.ClassName
  418. else
  419. N:=TSQLConnector.ClassName+'.'+ConnectionType;
  420. Result:=N
  421. +'#@'+HostName
  422. +'#@'+DatabaseName
  423. +'#@'+UserName
  424. +'#@'+Password
  425. +'#@'+Role
  426. +'#@'+CharSet;
  427. If Assigned(Params) then
  428. begin
  429. // Canonicalize
  430. S:=TStringList.Create;
  431. try
  432. S.Sorted:=true;
  433. S.AddStrings(Params);
  434. Result:=Result+'#@'+S.Text;
  435. finally
  436. S.Free;
  437. end;
  438. end;
  439. FKey:=Result;
  440. end;
  441. constructor TSQLDBConnectionDef.Create(ACollection: TCollection);
  442. begin
  443. inherited Create(ACollection);
  444. FParams:=TStringList.Create;
  445. TStringList(FParams).OnChange:=@DoChange;
  446. FEnabled:=True;
  447. end;
  448. destructor TSQLDBConnectionDef.Destroy;
  449. begin
  450. FParams.Free;
  451. inherited Destroy;
  452. end;
  453. procedure TSQLDBConnectionDef.Assign(Source: TPersistent);
  454. Var
  455. Def : TSQLDBConnectionDef absolute source;
  456. Conn : TSQLConnection absolute source;
  457. begin
  458. if Source is TSQLDBConnectionDef then
  459. begin
  460. FConnectionType:=Def.ConnectionType;
  461. FDatabaseName:=Def.DatabaseName;
  462. FHostName:=Def.HostName;
  463. FPassword:=Def.Password;
  464. FUserName:=Def.UserName;
  465. FName:=Def.Name;
  466. FCharSet:=Def.CharSet;
  467. FParams.Assign(Def.Params);
  468. FEnabled:=Def.Enabled;
  469. ClearKey;
  470. end
  471. else if Source is TSQLConnection then
  472. begin
  473. if Conn is TSQLConnector then
  474. FConnectionType:=TSQLConnector(Conn).ConnectorType
  475. else
  476. FConnectionClass:=TSQLConnectionClass(Conn.ClassType);
  477. FDatabaseName:=Conn.DatabaseName;
  478. FHostName:=Conn.HostName;
  479. FPassword:=Conn.Password;
  480. FUserName:=Conn.UserName;
  481. FName:='';
  482. FCharSet:=Conn.CharSet;
  483. FParams.Assign(Conn.Params);
  484. FEnabled:=Def.Enabled;
  485. ClearKey;
  486. end
  487. else
  488. inherited Assign(Source);
  489. end;
  490. function TSQLDBConnectionDef.GetDescription(Full : Boolean = False) : string;
  491. Procedure AddTo(const aName,aValue : String);
  492. begin
  493. if aValue='' then
  494. exit;
  495. if Result<>'' then
  496. Result:=Result+', ';
  497. Result:=Result+aName+': '+aValue;
  498. end;
  499. begin
  500. Result:='';
  501. AddTo('Name',Name);
  502. AddTo('Host',HostName);
  503. AddTo('Database',DatabaseName);
  504. AddTo('User',Username);
  505. AddTo('Charset',CharSet);
  506. if Port>0 then
  507. AddTo('Port',IntToStr(Port));
  508. if Full then
  509. begin
  510. AddTo('Password',Password);
  511. if Params.Count>0 then
  512. AddTo('Params',Params.CommaText);
  513. end;
  514. end;
  515. function TSQLDBConnectionDef.ToString: string;
  516. begin
  517. Result:=GetDescription;
  518. end;
  519. { TSQLDBConnectionmanager }
  520. procedure TSQLDBConnectionmanager.SetConnectionOwner(AValue: TComponent);
  521. begin
  522. if FConnectionOwner=AValue then Exit;
  523. if Assigned(FConnectionOwner) then
  524. FConnectionOwner.RemoveFreeNotification(Self);
  525. FConnectionOwner:=AValue;
  526. if Assigned(FConnectionOwner) then
  527. FConnectionOwner.FreeNotification(Self);
  528. end;
  529. procedure TSQLDBConnectionmanager.SetDefinitions(AValue: TSQLDBConnectionDefList);
  530. begin
  531. if FDefinitions=AValue then
  532. Exit;
  533. FDefinitions.Assign(AValue);
  534. end;
  535. procedure TSQLDBConnectionmanager.SetOnLog(AValue: TPoolLogEvent);
  536. begin
  537. if FOnLog=AValue then Exit;
  538. FOnLog:=AValue;
  539. if Assigned(FMyPool) then
  540. FMyPool.OnLog:=aValue;
  541. end;
  542. procedure TSQLDBConnectionmanager.SetPool(AValue: TSQLDBConnectionPool);
  543. begin
  544. if FPool=AValue then Exit;
  545. FPool:=AValue;
  546. if (FPool=Nil) then
  547. FPool:=FMyPool;
  548. end;
  549. procedure TSQLDBConnectionmanager.DoLog(const Msg: String);
  550. begin
  551. if Assigned(OnLog) then
  552. OnLog(Self,Msg);
  553. end;
  554. procedure TSQLDBConnectionmanager.DoLog(const Fmt: String;
  555. const aArgs: array of const);
  556. begin
  557. DoLog(Format(Fmt,aArgs));
  558. end;
  559. procedure TSQLDBConnectionmanager.Notification(AComponent: TComponent;
  560. Operation: TOperation);
  561. begin
  562. if (Operation=opRemove) then
  563. if (aComponent=FConnectionOwner) then
  564. FConnectionOwner:=Nil
  565. else
  566. begin
  567. if (aComponent=FMyPool) then
  568. FMyPool:=Nil;
  569. if (aComponent=FPool) then
  570. FPool:=Nil;
  571. end;
  572. inherited Notification(AComponent, Operation);
  573. end;
  574. constructor TSQLDBConnectionmanager.Create(aOwner: TComponent);
  575. begin
  576. inherited Create(aOwner);
  577. FMyPool:=CreatePool;
  578. FMyPool.SetSubComponent(True);
  579. FDefinitions:=CreateDefinitionList;
  580. FPool:=FMyPool;
  581. FLogEvents:=LogAllEvents;
  582. end;
  583. destructor TSQLDBConnectionmanager.Destroy;
  584. begin
  585. FreeAndNil(FPool);
  586. FreeAndNil(FDefinitions);
  587. inherited Destroy;
  588. end;
  589. function TSQLDBConnectionmanager.CreateConnection(const aDef: TSQLDBConnectionDef; addToPool: Boolean): TSQLConnection;
  590. var
  591. C : TSQLConnectionClass;
  592. begin
  593. C:=aDef.ConnectionClass;
  594. if (C=Nil) and (aDef.ConnectionType<>'') then
  595. C:=TSQLConnector;
  596. With aDef do
  597. DoLog(SCreatingNewConnection, [GetDescription]);
  598. Result:=C.Create(Self.ConnectionOwner);
  599. try
  600. aDef.AssignTo(Result);
  601. Result.LogEvents:=Self.LogEvents;
  602. Result.Transaction:=TSQLTransaction.Create(Result);
  603. except
  604. Result.Free;
  605. Raise;
  606. end;
  607. if AddToPool then
  608. Pool.AddConnection(Result);
  609. end;
  610. function TSQLDBConnectionmanager.CreatePool : TSQLDBConnectionPool;
  611. begin
  612. Result:=TSQLDBConnectionPool.Create(Self);
  613. end;
  614. function TSQLDBConnectionmanager.CreateDefinitionList: TSQLDBConnectionDefList;
  615. begin
  616. Result:=TSQLDBConnectionDefList.Create(Self,TSQLDBConnectionDef);
  617. end;
  618. function TSQLDBConnectionmanager.CreateConnection(const aName: string;
  619. addToPool: Boolean): TSQLConnection;
  620. begin
  621. Result:=CreateConnection(Definitions.Get(aName),addToPool);
  622. end;
  623. function TSQLDBConnectionmanager.NewConnectionAllowed(aDef: TSQLDBConnectionDef; out aReason: string): Boolean;
  624. Var
  625. N: Integer;
  626. begin
  627. Result:=True;
  628. if (MaxDBConnections>0) then
  629. begin
  630. N:=FPool.CountConnections(aDef);
  631. if (N>MaxDBConnections) then
  632. AReason:=Format(SErrMaxNumberOfDefConnections, [MaxDBConnections, aDef.GetDescription(False)]);
  633. end;
  634. if (MaxTotalConnections>0) then
  635. begin
  636. N:=FPool.CountAllConnections;
  637. if (N>MaxDBConnections) then
  638. aReason:=Format(SErrMaxTotalConnectionReached, [MaxDBConnections]);
  639. end;
  640. Result:=aReason='';
  641. end;
  642. function TSQLDBConnectionmanager.GetConnection(const aDef: TSQLDBConnectionDef ): TSQLConnection;
  643. Var
  644. aReason,aErr : String;
  645. begin
  646. Result:=FPool.FindConnection(aDef);
  647. if Result=Nil then
  648. begin
  649. if Not NewConnectionAllowed(aDef,aReason) then
  650. begin
  651. aErr:=Format(SErrCannotCreateNewConnection, [aDef.GetDescription, aReason]);
  652. DoLog(aErr);
  653. Raise ESQLDBPool.Create(aErr);
  654. end;
  655. Result:=CreateConnection(aDef,True);
  656. end;
  657. end;
  658. function TSQLDBConnectionmanager.GetConnection(const aName: string
  659. ): TSQLConnection;
  660. begin
  661. Result:=GetConnection(Definitions.Get(aName));
  662. end;
  663. function TSQLDBConnectionmanager.ReleaseConnection(aConnection: TSQLConnection
  664. ): Boolean;
  665. begin
  666. Result:=FPool.ReleaseConnection(aConnection);
  667. end;
  668. { TConnectionPoolData }
  669. constructor TConnectionPoolData.Create(aConnection: TSQLConnection; aLocked : Boolean = true);
  670. begin
  671. FConnection:=aConnection;
  672. LastUsed:=Now;
  673. Flocked:=aLocked;
  674. end;
  675. destructor TConnectionPoolData.Destroy;
  676. begin
  677. inherited Destroy;
  678. end;
  679. procedure TConnectionPoolData.Lock;
  680. begin
  681. FLocked:=True;
  682. FLastUsed:=Now;
  683. end;
  684. procedure TConnectionPoolData.Unlock;
  685. begin
  686. FLocked:=False;
  687. FLastUsed:=Now;
  688. end;
  689. procedure TConnectionPoolData.FreeConnection;
  690. Var
  691. TR : TSQLTransaction;
  692. begin
  693. try
  694. TR:=Connection.Transaction;
  695. Connection.Transaction:=Nil;
  696. TR.Free;
  697. Connection.Connected:=False;
  698. finally
  699. FreeAndNil(FConnection);
  700. end;
  701. end;
  702. { TTypedConnectionPool }
  703. function TTypedConnectionPool.FindConnection(const aDatabaseName: string;
  704. const aHostName: string; const aUserName: string; const aPassword: string;
  705. aParams: TStrings): T;
  706. begin
  707. Result:=T(Inherited FindConnection(T,aDatabaseName,aHostName,aUserName,aPassword,aParams));
  708. end;
  709. { TPQConnPool }
  710. (*
  711. generic function TTypedConnectionPool<T : TSQLConnection>.FindConnection(const aDatabaseName: string; const aHostName: string;
  712. const aUserName: string; const aPassword: string; aParams: TStrings = Nil): T;
  713. begin
  714. result:=T(FindConnection(T,aDatabaseName,aHostName,aPassword,aUserName,aParams));
  715. end;
  716. *)
  717. { TConnectionList }
  718. constructor TConnectionList.Create;
  719. begin
  720. Inherited Create;
  721. FLock:=TCriticalSection.Create;
  722. FDisconnectTimeout:=DefaultDisconnectTimeout;
  723. end;
  724. destructor TConnectionList.Destroy;
  725. begin
  726. FreeAndNil(FLock);
  727. inherited Destroy;
  728. end;
  729. procedure TConnectionList.DisconnectAll;
  730. Var
  731. I : integer;
  732. CD : TConnectionPoolData;
  733. begin
  734. FLock.Enter;
  735. try
  736. For I:=Count-1 downto 0 do
  737. begin
  738. CD:=TConnectionPoolData(Items[i]);
  739. if (not CD.Locked) then
  740. begin
  741. CD.FreeConnection;
  742. Delete(I);
  743. end;
  744. end;
  745. finally
  746. FLock.Leave;
  747. end;
  748. end;
  749. procedure TConnectionList.Dolog(const Msg: String);
  750. begin
  751. If Assigned(OnLog) then
  752. OnLog(Self,Msg);
  753. end;
  754. procedure TConnectionList.DoLog(const Fmt: String; Args: array of const);
  755. begin
  756. DoLog(Format(Fmt,args));
  757. end;
  758. function TConnectionList.DoDisconnectOld(aTimeOut: Integer = -1): Integer;
  759. Var
  760. secs,I : integer;
  761. CD : TConnectionPoolData;
  762. N : TDateTime;
  763. begin
  764. Result:=0;
  765. N:=Now;
  766. if aTimeout<0 then
  767. aTimeout:=FDisconnectTimeout;
  768. for I:=Count-1 downto 0 do
  769. begin
  770. CD:=TConnectionPoolData(Items[i]);
  771. Secs:=SecondsBetween(N,CD.LastUsed);
  772. if (not CD.Locked) and (Secs>aTimeout) then
  773. begin
  774. With CD.Connection do
  775. DoLog(STimeoutReached, [Secs, aTimeout, GetDescription(False)]);
  776. try
  777. CD.FreeConnection;
  778. except
  779. on E : Exception do
  780. DoLog(SErrFreeingConnection, [E.ClassName, I, E.Message]);
  781. end;
  782. Delete(I);
  783. Inc(Result);
  784. end;
  785. end;
  786. end;
  787. function TConnectionList.CreatePoolData(aConnection: TSQLConnection;
  788. aLocked: Boolean): TConnectionPoolData;
  789. begin
  790. Result:=TConnectionPoolData.Create(aConnection,aLocked);
  791. end;
  792. function TConnectionList.DisconnectOld(aTimeOut: Integer): Integer;
  793. begin
  794. FLock.Enter;
  795. try
  796. Result:=DoDisconnectOld(aTimeout);
  797. finally
  798. FLock.Leave;
  799. end;
  800. end;
  801. function TConnectionList.AddConnection(aConnection: TSQLConnection; aLocked: Boolean
  802. ): TConnectionPoolData;
  803. begin
  804. FLock.Enter;
  805. try
  806. Result:=CreatePoolData(aConnection,aLocked);
  807. Add(Result);
  808. finally
  809. FLock.Leave;
  810. end;
  811. end;
  812. function TConnectionList.PopConnection: TSQLConnection;
  813. Var
  814. i : integer;
  815. CD : TConnectionPoolData;
  816. begin
  817. Result:=nil;
  818. FLock.Enter;
  819. try
  820. DoDisconnectOld;
  821. I:=0;
  822. While (Result=Nil) and (I<Count) do
  823. begin
  824. CD:=TConnectionPoolData(Items[i]);
  825. if not CD.Locked then
  826. begin
  827. CD.Lock;
  828. Result:=CD.Connection;
  829. end;
  830. Inc(I);
  831. end;
  832. finally
  833. Flock.Leave;
  834. end;
  835. end;
  836. function TConnectionList.UnlockConnection(aConnection: TSQLConnection): boolean;
  837. Var
  838. I : Integer;
  839. Data : TConnectionPoolData;
  840. begin
  841. Result:=False;
  842. FLock.Enter;
  843. try
  844. I:=Count-1;
  845. Data:=Nil;
  846. While (Data=Nil) and (I>=0) do
  847. begin
  848. Data:=TConnectionPoolData(Items[i]);
  849. if Data.Connection<>aConnection then
  850. Data:=Nil;
  851. Dec(i);
  852. end;
  853. if Assigned(Data) then
  854. begin
  855. Data.Unlock;
  856. Result:=True;
  857. end;
  858. finally
  859. FLock.Leave;
  860. end;
  861. end;
  862. { TSQLDBConnectionPool }
  863. function TSQLDBConnectionPool.GetCount: longword;
  864. begin
  865. Result:=FPool.Count;
  866. end;
  867. function TSQLDBConnectionPool.CreateList: TConnectionList;
  868. begin
  869. Result:=TConnectionList.Create;
  870. end;
  871. procedure TSQLDBConnectionPool.Dolog(const Msg: String);
  872. begin
  873. If Assigned(OnLog) then
  874. OnLog(Self,Msg);
  875. end;
  876. procedure TSQLDBConnectionPool.DoLog(const Fmt: String; Args: array of const);
  877. begin
  878. DoLog(Format(Fmt,args));
  879. end;
  880. procedure TSQLDBConnectionPool.Lock;
  881. begin
  882. Flock.Enter;
  883. end;
  884. procedure TSQLDBConnectionPool.Unlock;
  885. begin
  886. Flock.Leave;
  887. end;
  888. function TSQLDBConnectionPool.CreateKey(aDef: TSQLDBConnectionDef): String;
  889. begin
  890. Result:=aDef.CreateKey;
  891. end;
  892. function TSQLDBConnectionPool.CreateDef : TSQLDBConnectionDef;
  893. begin
  894. Result:=TSQLDBConnectionDef.Create(Nil);
  895. end;
  896. function TSQLDBConnectionPool.FindConnection(aClass : TSQLConnectionClass; const aDatabaseName, aHostName,
  897. aUserName, aPassword: string; aParams: TStrings): TSQLConnection;
  898. Var
  899. Def : TSQLDBConnectionDef;
  900. begin
  901. Result:=nil;
  902. Def:=CreateDef;
  903. try
  904. Def.ConnectionClass:=aClass;
  905. Def.DatabaseName:=aDatabaseName;
  906. Def.HostName:=aHostName;
  907. Def.UserName:=aUserName;
  908. Def.Password:=aPassword;
  909. if Assigned(aParams) then
  910. Def.Params:=aParams;
  911. Result:=FindConnection(Def);
  912. finally
  913. Def.Free;
  914. end;
  915. end;
  916. function TSQLDBConnectionPool.FindConnection(const aConnectionDef: TSQLDBConnectionDef): TSQLConnection;
  917. Var
  918. N : String;
  919. begin
  920. Result:=nil;
  921. with aConnectionDef do
  922. begin
  923. N:=ConnectionType;
  924. if (N='') and Assigned(ConnectionClass) then
  925. N:=ConnectionClass.ClassName;
  926. DoLog(SFindingConnection,[GetDescription]);
  927. Result:=DoFindConnection(aConnectionDef);
  928. If (Result=Nil) then
  929. DoLog(SNoSuchConnection,[GetDescription])
  930. else
  931. DoLog(SFoundConnection,[GetDescription, PtrInt(Result)])
  932. end;
  933. end;
  934. function TSQLDBConnectionPool.DoFindConnection(const aConnectionDef: TSQLDBConnectionDef): TSQLConnection;
  935. Var
  936. Key : String;
  937. L : TConnectionList;
  938. begin
  939. Result:=Nil;
  940. Key:=CreateKey(aConnectionDef);
  941. Lock;
  942. try
  943. L:=TConnectionList(FPool.Items[Key]);
  944. if L=Nil then
  945. Exit;
  946. Result:=L.PopConnection;
  947. finally
  948. Unlock;
  949. end;
  950. end;
  951. (*
  952. result:=TSQLConnection(FPool[key]);
  953. if result=nil then
  954. begin
  955. result:=CreateConn(AOwner);
  956. result.HostName:=GetFirstNonNull(sHostName,FHostName);
  957. // Force local connection
  958. if result.HostName=MyServerName then
  959. Result.HostName:='';
  960. result.DatabaseName:=GetFirstNonNull(sDatabaseName,FDatabaseName);
  961. result.UserName:=GetFirstNonNull(sUserName,FUserName);
  962. result.Password:=GetFirstNonNull(sPassword,FPassword);
  963. result.Params:=GetFirstNonNull(ssParams,FParams);
  964. result.CharSet:='UTF8';
  965. if not CreateDisconnected then
  966. Result.Open;
  967. FPool.Add(key,result);
  968. end;
  969. end;
  970. *)
  971. procedure TSQLDBConnectionPool.DoDisconnect(Item: TObject; const Key: ansistring;
  972. var Continue: Boolean);
  973. Var
  974. L : TConnectionList absolute item;
  975. begin
  976. Continue:=True;
  977. try
  978. L.DisconnectOld();
  979. except
  980. on E : Exception do
  981. DoLog(SErrorDisconnecting,[E.ClassName,E.Message]);
  982. end;
  983. end;
  984. procedure TSQLDBConnectionPool.DisconnectAll;
  985. begin
  986. Lock;
  987. try
  988. FPool.Iterate(@DoDisconnect);
  989. finally
  990. UnLock;
  991. end;
  992. end;
  993. destructor TSQLDBConnectionPool.Destroy;
  994. begin
  995. FLock.Free;
  996. FPool.Destroy;
  997. inherited Destroy;
  998. end;
  999. function TSQLDBConnectionPool.CountConnections(aClass: TSQLConnectionClass;
  1000. const aDatabaseName, aHostName, aUserName, aPassword: string;
  1001. aParams: TStrings): Integer;
  1002. Var
  1003. Def : TSQLDBConnectionDef;
  1004. begin
  1005. Result:=0;
  1006. Def:=CreateDef;
  1007. try
  1008. Def.ConnectionClass:=aClass;
  1009. Def.DatabaseName:=aDatabaseName;
  1010. Def.HostName:=aHostName;
  1011. Def.UserName:=aUserName;
  1012. Def.Password:=aPassword;
  1013. if Assigned(aParams) then
  1014. Def.Params:=aParams;
  1015. Result:=CountConnections(Def);
  1016. finally
  1017. Def.Free;
  1018. end;
  1019. end;
  1020. function TSQLDBConnectionPool.CountConnections(aInstance: TSQLConnection): Integer;
  1021. begin
  1022. With aInstance do
  1023. Result:=CountConnections(TSQLConnectionClass(ClassType),DatabaseName,HostName,UserName,Password,Params);
  1024. end;
  1025. function TSQLDBConnectionPool.CountConnections(aDef: TSQLDBConnectionDef): Integer;
  1026. Var
  1027. Key : String;
  1028. L : TConnectionList;
  1029. begin
  1030. Result:=0;
  1031. Key:=CreateKey(aDef);
  1032. Lock;
  1033. try
  1034. L:=TConnectionList(FPool.Items[Key]);
  1035. if L<>Nil then
  1036. Result:=L.Count;
  1037. finally
  1038. UnLock;
  1039. end;
  1040. end;
  1041. Type
  1042. { TConnectionCounter }
  1043. TConnectionCounter = Class(TObject)
  1044. private
  1045. FCount : Integer;
  1046. Public
  1047. Procedure DoCount(Item: TObject; const Key: ansistring; var Continue: Boolean);
  1048. Property Count : Integer Read FCount;
  1049. end;
  1050. { TConnectionCounter }
  1051. procedure TConnectionCounter.DoCount(Item: TObject; const Key: Ansistring; var Continue: Boolean);
  1052. begin
  1053. FCount:=FCount+(Item as TConnectionList).Count;
  1054. Continue:=True;
  1055. end;
  1056. function TSQLDBConnectionPool.CountAllConnections: Integer;
  1057. var
  1058. Counter : TConnectionCounter;
  1059. begin
  1060. Counter:=Nil;
  1061. Lock;
  1062. try
  1063. Counter:=TConnectionCounter.Create;
  1064. FPool.Iterate(@Counter.DoCount);
  1065. Result:=Counter.Count;
  1066. finally
  1067. Unlock;
  1068. Counter.Free;
  1069. end;
  1070. end;
  1071. procedure TSQLDBConnectionPool.AddConnection(aConnection: TSQLConnection; aLocked : Boolean = True);
  1072. Var
  1073. Key : String;
  1074. L : TConnectionList;
  1075. aDef: TSQLDBConnectionDef;
  1076. begin
  1077. aDef:=Nil;
  1078. Lock;
  1079. try
  1080. aDef:=CreateDef;
  1081. aDef.Assign(aConnection);
  1082. Key:=CreateKey(aDef);
  1083. L:=TConnectionList(FPool.Items[Key]);
  1084. if L=Nil then
  1085. begin
  1086. L:=CreateList;
  1087. L.FonLog:=Self.OnLog;
  1088. FPool.Add(Key,L);
  1089. end;
  1090. L.AddConnection(aConnection,aLocked);
  1091. finally
  1092. Unlock;
  1093. aDef.Free;
  1094. end;
  1095. end;
  1096. function TSQLDBConnectionPool.ReleaseConnection(aConnection: TSQLConnection): Boolean;
  1097. Var
  1098. Key : String;
  1099. L : TConnectionList;
  1100. aDef: TSQLDBConnectionDef;
  1101. begin
  1102. Result:=False;
  1103. aDef:=Nil;
  1104. Lock;
  1105. try
  1106. aDef:=CreateDef;
  1107. aDef.Assign(aConnection);
  1108. Key:=CreateKey(aDef);
  1109. L:=TConnectionList(FPool.Items[Key]);
  1110. if Assigned(L) then
  1111. begin
  1112. With aConnection do
  1113. DoLog(SReleasingConnections, [GetDescription(False), L.Count]);
  1114. Result:=L.UnlockConnection(aConnection);
  1115. end;
  1116. finally
  1117. Unlock;
  1118. aDef.Free;
  1119. end;
  1120. end;
  1121. constructor TSQLDBConnectionPool.Create(aOwner: TComponent);
  1122. begin
  1123. FPool:=TFPObjectHashTable.Create(True);
  1124. FLock:=TCriticalSection.Create;
  1125. end;
  1126. end.