sqldbpool.pp 31 KB

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