zeoslib  UNKNOWN
 All Files
ZDbcPooled.pas
Go to the documentation of this file.
1 {*********************************************************}
2 { }
3 { Zeos Database Objects }
4 { }
5 {*********************************************************}
6 
7 {@********************************************************}
8 { Copyright (c) 1999-2012 Zeos Development Group }
9 { }
10 { License Agreement: }
11 { }
12 { This library is distributed in the hope that it will be }
13 { useful, but WITHOUT ANY WARRANTY; without even the }
14 { implied warranty of MERCHANTABILITY or FITNESS FOR }
15 { A PARTICULAR PURPOSE. See the GNU Lesser General }
16 { Public License for more details. }
17 { }
18 { The source code of the ZEOS Libraries and packages are }
19 { distributed under the Library GNU General Public }
20 { License (see the file COPYING / COPYING.ZEOS) }
21 { with the following modification: }
22 { As a special exception, the copyright holders of this }
23 { library give you permission to link this library with }
24 { independent modules to produce an executable, }
25 { regardless of the license terms of these independent }
26 { modules, and to copy and distribute the resulting }
27 { executable under terms of your choice, provided that }
28 { you also meet, for each linked independent module, }
29 { the terms and conditions of the license of that module. }
30 { An independent module is a module which is not derived }
31 { from or based on this library. If you modify this }
32 { library, you may extend this exception to your version }
33 { of the library, but you are not obligated to do so. }
34 { If you do not wish to do so, delete this exception }
35 { statement from your version. }
36 { }
37 { }
38 { The project web site is located on: }
39 { http://zeos.firmos.at (FORUM) }
40 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
41 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
42 { }
43 { http://www.sourceforge.net/projects/zeoslib. }
44 { }
45 { }
46 { Zeos Development Group. }
47 {********************************************************@}
48 
49 unit ZDbcPooled;
50 
51 interface
52 
53 {$I ZDbc.inc}
54 
55 implementation
56 
57 uses
58  Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} Contnrs, DateUtils, SysUtils, Types,
59  SyncObjs,
60  ZCompatibility, ZClasses, ZURL, ZDbcConnection, ZDbcIntfs, ZPlainDriver,
61  ZMessages, ZVariant;
62 
63 type
64  TConnectionPool = class;
65 
66  { This class searchs for timed out connections in a pool and destroy them.
67  Maybe it would be nice to have a global instance to check all pools. This
68  way, we could avoid creating many threads. }
69  TConnectionTimeoutThread = class(TThread)
70  private
71  FConnectionPool: TConnectionPool;
72  protected
73  procedure Execute; override;
74  public
75  constructor Create(const ConnectionPool: TConnectionPool);
76  end;
77 
78  { This class keeps a pool of connections which shares the same URL.
79  When a new connection is needed, it looks first if there is an available
80  connection in the pool, and returns it. If there is no connection available,
81  it creates a new one. Each created connection will be returned automatically
82  to the pool when it is not used anymore. }
83  TConnectionPool = class
84  private
85  FConnections: array of IZConnection;
86  FConnectionsReturnTimes: array of TDateTime;
87  FSlotsInUse: TBits;
88  FConnectionTimeout: Integer;
89  FConnectionTimeoutThread: TConnectionTimeoutThread;
90  FCriticalSection: TCriticalSection;
91  FCriticalSectionDriverManager: TCriticalSection;
92  FDefaultAutoCommit: Boolean;
93  FDefaultTransactIsolationLevel: TZTransactIsolationLevel;
94  FMaxConnections: Integer;
95  FURL: string;
96  FWait: Boolean;
97  public
98  { URL
99  The connection URL
100  ConnectionTimeout
101  How many time a pooled connection will be kept in the pool. Zero = infinite
102  MaxConnections
103  The maximum numbers of connections this pool will hold. Zero = infinite
104  Wait
105  True - When a pool reach its maximum number of connections and someone
106  tries to acquire a new one, it waits until a connection is
107  returned to the pool
108  False - Raises an exception instead of wait }
109  constructor Create(const URL: string; const ConnectionTimeout: Integer = 0; const MaxConnections: Integer = 0; const Wait: Boolean = True);
110  destructor Destroy; override;
111  function Acquire: IZConnection;
112  procedure ReturnToPool(const Connection: IZConnection);
113  end;
114 
115  { This class embedds a real connection and redirects all methods to it.
116  When it is droped or closed, it returns the real connection to the pool. }
117 
118  { TZDbcPooledConnection }
119 
120  TZDbcPooledConnection = class(TZCodePagedObject, IZConnection)
121  private
122  FConnection: IZConnection;
123  FConnectionPool: TConnectionPool;
124  FAutoEncodeStrings: Boolean;
125  FUseMetadata: Boolean;
126  {$IFDEF ZEOS_TEST_ONLY}
127  FTestMode: Byte;
128  {$ENDIF}
129  function GetConnection: IZConnection;
130  protected // IZConnection
131  FClientCodePage: String;
132  procedure CheckCharEncoding(CharSet: String;
133  const DoArrange: Boolean = False);
134  function GetClientCodePageInformations: PZCodePage; //EgonHugeist
135  function GetAutoEncodeStrings: Boolean; //EgonHugeist
136  procedure SetAutoEncodeStrings(const Value: Boolean);
137  function CreateStatement: IZStatement;
138  function PrepareStatement(const SQL: string): IZPreparedStatement;
139  function PrepareCall(const SQL: string): IZCallableStatement;
140  function CreateStatementWithParams(Info: TStrings): IZStatement;
141  function PrepareStatementWithParams(const SQL: string; Info: TStrings): IZPreparedStatement;
142  function PrepareCallWithParams(const SQL: string; Info: TStrings): IZCallableStatement;
143  function CreateNotification(const Event: string): IZNotification;
144  function CreateSequence(const Sequence: string; BlockSize: Integer): IZSequence;
145  function NativeSQL(const SQL: string): string;
146  procedure SetAutoCommit(Value: Boolean);
147  function GetAutoCommit: Boolean;
148  procedure Commit;
149  procedure Rollback;
150  procedure PrepareTransaction(const transactionid: string);
151  procedure CommitPrepared(const transactionid: string);
152  procedure RollbackPrepared(const transactionid: string);
153  function PingServer: Integer;
154  function EscapeString(Value : RawByteString) : RawByteString;
155  procedure Open;
156  procedure Close;
157  function IsClosed: Boolean;
158  function GetDriver: IZDriver;
159  function GetIZPlainDriver: IZPlainDriver;
160  function GetMetadata: IZDatabaseMetadata;
161  function GetParameters: TStrings;
162  function GetClientVersion: Integer;
163  function GetHostVersion: Integer;
164  procedure SetReadOnly(Value: Boolean);
165  function IsReadOnly: Boolean;
166  procedure SetCatalog(const Value: string);
167  function GetCatalog: string;
168  procedure SetTransactionIsolation(Value: TZTransactIsolationLevel);
169  function GetTransactionIsolation: TZTransactIsolationLevel;
170  function GetWarnings: EZSQLWarning;
171  procedure ClearWarnings;
172  function UseMetadata: boolean;
173  procedure SetUseMetadata(Value: Boolean);
174  public
175  constructor Create(const ConnectionPool: TConnectionPool);
176  destructor Destroy; override;
177  function GetBinaryEscapeString(const Value: RawByteString): String; overload;
178  function GetBinaryEscapeString(const Value: TByteDynArray): String; overload;
179  function GetEscapeString(const Value: ZWideString): ZWideString; overload; virtual;
180  function GetEscapeString(const Value: RawByteString): RawByteString; overload; virtual;
181  function GetEncoding: TZCharEncoding;
182  function GetConSettings: PZConSettings;
183  {$IFDEF ZEOS_TEST_ONLY}
184  function GetTestMode : Byte;
185  procedure SetTestMode(Mode: Byte);
186  {$ENDIF}
187  end;
188 
189  {$WARNINGS OFF}
190  TZDbcPooledConnectionDriver = class(TZAbstractDriver)
191  private
192  PoolList: TObjectList;
193  URLList: TStringList;
194  function GetEmbeddedURL(const URL: String): String;
195  public
196  //function GetSupportedProtocols: TStringDynArray; override;
197  function Connect(const URL: TZURL): IZConnection; override;
198  function GetClientVersion(const URL: string): Integer; override;
199  function AcceptsURL(const URL: string): Boolean; override;
200  function GetPropertyInfo(const URL: string; Info: TStrings): TStrings; override;
201  function GetMajorVersion: Integer; override;
202  function GetMinorVersion: Integer; override;
203  function GetSubVersion: Integer; override;
204  public
205  constructor Create; override;
206  destructor Destroy; override;
207  end;
208  {$WARNINGS ON}
209 
210 { TConnectionPool }
211 
212 constructor TConnectionPool.Create(const URL: string; const ConnectionTimeout: Integer = 0; const MaxConnections: Integer = 0; const Wait: Boolean = True);
213 begin
214  FURL := URL;
215  FWait := Wait;
216 
217  FSlotsInUse := TBits.Create;
218  FCriticalSection := TCriticalSection.Create;
219  FCriticalSectionDriverManager := TCriticalSection.Create;
220 
221  FMaxConnections := MaxConnections;
222  if FMaxConnections = 0 then
223  begin
224  SetLength(FConnections, 10);
225  SetLength(FConnectionsReturnTimes, 10);
226  FSlotsInUse.Size := 10;
227  end
228  else
229  begin
230  SetLength(FConnections, FMaxConnections);
231  SetLength(FConnectionsReturnTimes, FMaxConnections);
232  FSlotsInUse.Size := FMaxConnections;
233  end;
234 
235  //
236  // If there is a connection timeout, an instance of TConnectionTimeoutThread
237  // will monitor all unused connections and drop them when they timeout.
238  //
239  FConnectionTimeout := ConnectionTimeout;
240  if FConnectionTimeout <> 0 then
241  FConnectionTimeoutThread := TConnectionTimeoutThread.Create(Self);
242 end;
243 
244 destructor TConnectionPool.Destroy;
245 begin
246  if FConnectionTimeoutThread <> nil then
247  begin
248  FConnectionTimeoutThread.Terminate;
249  FConnectionTimeoutThread.WaitFor;
250  FConnectionTimeoutThread.Free;
251  end;
252 
253  SetLength(FConnections, 0);
254  FSlotsInUse.Free;
255  FCriticalSection.Free;
256  FCriticalSectionDriverManager.Free;
257 
258  inherited;
259 end;
260 
261 function TConnectionPool.Acquire: IZConnection;
262 var
263  I: Integer;
264 begin
265  Result := nil;
266  I := 0;
267 
268  while True do
269  begin
270  FCriticalSection.Enter;
271  try
272  // Try to get an existing connection
273  I := 0;
274  while I < FSlotsInUse.Size do
275  begin
276  if (FConnections[I] <> nil) and (not FSlotsInUse[I]) then
277  begin
278  try
279  // Test for dead connections
280  FConnections[I].Rollback; // PingServer did not work (tested with FB)
281  FSlotsInUse[I] := True;
282  Break;
283  except
284  // An exception can be raised when the dead connection is dropped
285  try
286  FConnections[I] := nil;
287  except
288  end;
289  Inc(I);
290  end;
291  end
292  else
293  Inc(I);
294  end;
295 
296  // Try to get a free slot if there is no existing connection available
297  if I = FSlotsInUse.Size then
298  begin
299  I := 0;
300  while I < FSlotsInUse.Size do
301  begin
302  if (FConnections[I] = nil) and (not FSlotsInUse[I]) then
303  begin
304  FSlotsInUse[I] := True;
305  Break;
306  end;
307  Inc(I);
308  end;
309  end;
310 
311  // Increase the pool if there is no free slot in the pool
312  if I = FSlotsInUse.Size then
313  begin
314  if FMaxConnections = 0 then
315  begin
316  SetLength(FConnections, Length(FConnections) + 10);
317  SetLength(FConnectionsReturnTimes, Length(FConnectionsReturnTimes) + 10);
318  FSlotsInUse.Size := FSlotsInUse.Size + 10;
319  FSlotsInUse[I] := True;
320  end;
321  end;
322  finally
323  FCriticalSection.Leave;
324  end;
325 
326  if I < FSlotsInUse.Size then
327  Break;
328 
329  // No connection available. Wait and try again later
330  if FWait then
331  Sleep(100)
332  else
333  raise Exception.Create(ClassName + '.Acquire'+LineEnding+'O pool de conexatingiu o limite maximo');
334  //2013-10-13 mse: please replace non ASCII characters (>127) by the
335  //#nnn notation in order to have encoding independent sources
336  end;
337 
338  //
339  // If there is no connection in the pool, create a new one.
340  // This block is separated from the block above because there could be some
341  // delay to create a connection, and it would not be nice to keep the critical
342  // section locked during this delay.
343  //
344  if FConnections[I] = nil then
345  begin
346  try
347  // I had a strong feeling that DriverManager is not thread-safe, because i
348  // had random access violations on high load operations at this point.
349  // For now, i will serialize DriverManager access, until further
350  // investigation (maybe the problem is in the pool driver, as
351  // DriverManager should be thread-safe in essence.
352  FCriticalSectionDriverManager.Enter;
353  try
354  FConnections[I] := DriverManager.GetConnection(FURL);
355  finally
356  FCriticalSectionDriverManager.Leave;
357  end;
358  FConnections[I].Open;
359  except
360  on E: Exception do
361  begin
362  FCriticalSection.Enter;
363  try
364  FSlotsInUse[I] := False;
365  FConnections[I] := nil;
366  finally
367  FCriticalSection.Leave;
368  raise Exception.Create(ClassName + '.Acquire'+LineEnding+'Error while trying to acquire a new connection'+LineEnding+LineEnding+E.Message);
369  end;
370  end;
371  end;
372  FDefaultAutoCommit := FConnections[I].GetAutoCommit;
373  FDefaultTransactIsolationLevel := FConnections[I].GetTransactionIsolation;
374  end;
375 
376  Result := IZConnection(FConnections[I]);
377  Result.SetAutoCommit(True);
378  Result.SetTransactionIsolation(tiReadCommitted);
379 end;
380 
381 procedure TConnectionPool.ReturnToPool(const Connection: IZConnection);
382 var
383  I: Integer;
384 begin
385  //
386  // Return the connection to the pool.
387  //
388  FCriticalSection.Enter;
389  try
390  for I := 0 to Length(FConnections) - 1 do
391  begin
392  if FConnections[I] = Connection then
393  begin
394  //
395  // If there is some problem with the connection, a RollBack will raise
396  // an exception, and the connection will be dropped.
397  //
398  try
399  FSlotsInUse[I] := False;
400  FConnectionsReturnTimes[I] := Now;
401  FConnections[I].Rollback;
402  except
403  try
404  FConnections[I] := nil;
405  except
406  end;
407  end;
408  Break;
409  end;
410  end;
411  finally
412  FCriticalSection.Leave;
413  end;
414 end;
415 
416 { TZDbcPooledConnection }
417 
418 constructor TZDbcPooledConnection.Create(const ConnectionPool: TConnectionPool);
419 begin
420  FConnectionPool := ConnectionPool;
421  {$IFDEF ZEOS_TEST_ONLY}
422  FTestMode := 0;
423  {$ENDIF}
424 end;
425 
426 destructor TZDbcPooledConnection.Destroy;
427 begin
428  if FConnection <> nil then
429  begin
430  FConnectionPool.ReturnToPool(FConnection);
431  FConnection := nil;
432  end;
433 
434  FConnectionPool := nil;
435 
436  inherited;
437 end;
438 
439 function TZDbcPooledConnection.GetConnection: IZConnection;
440 begin
441  if FConnection = nil then
442  FConnection := FConnectionPool.Acquire;
443  Result := FConnection;
444 end;
445 
446 procedure TZDbcPooledConnection.ClearWarnings;
447 begin
448  GetConnection.ClearWarnings;
449 end;
450 
451 function TZDbcPooledConnection.UseMetadata: boolean;
452 begin
453  result := FUseMetadata;
454 end;
455 
456 procedure TZDbcPooledConnection.SetUseMetadata(Value: Boolean);
457 begin
458  FUseMetadata := Value;
459 end;
460 
461 procedure TZDbcPooledConnection.Close;
462 begin
463  if FConnection <> nil then
464  begin
465  FConnectionPool.ReturnToPool(FConnection);
466  FConnection := nil;
467  end;
468 end;
469 
470 procedure TZDbcPooledConnection.Commit;
471 begin
472  GetConnection.Commit;
473 end;
474 
475 procedure TZDbcPooledConnection.CommitPrepared(const transactionid: string);
476 begin
477  GetConnection.CommitPrepared(transactionid);
478 end;
479 
480 function TZDbcPooledConnection.CreateNotification(const Event: string): IZNotification;
481 begin
482  Result := GetConnection.CreateNotification(Event);
483 end;
484 
485 function TZDbcPooledConnection.CreateSequence(const Sequence: string; BlockSize: Integer): IZSequence;
486 begin
487  Result := GetConnection.CreateSequence(Sequence, BlockSize);
488 end;
489 
490 function TZDbcPooledConnection.CreateStatement: IZStatement;
491 begin
492  Result := GetConnection.CreateStatement;
493 end;
494 
495 
496 function TZDbcPooledConnection.CreateStatementWithParams(Info: TStrings): IZStatement;
497 begin
498  Result := GetConnection.CreateStatementWithParams(Info);
499 end;
500 
501 function TZDbcPooledConnection.EscapeString(Value: RawByteString): RawByteString;
502 begin
503  Result := GetConnection.EscapeString(Value);
504 end;
505 
506 function TZDbcPooledConnection.GetAutoCommit: Boolean;
507 begin
508  Result := GetConnection.GetAutoCommit;
509 end;
510 
511 function TZDbcPooledConnection.GetCatalog: string;
512 begin
513  Result := GetConnection.GetCatalog;
514 end;
515 
516 function TZDbcPooledConnection.GetClientVersion: Integer;
517 begin
518  Result := GetConnection.GetClientVersion;
519 end;
520 
521 function TZDbcPooledConnection.GetDriver: IZDriver;
522 begin
523  Result := GetConnection.GetDriver;
524 end;
525 
526 function TZDbcPooledConnection.GetIZPlainDriver: IZPlainDriver;
527 begin
528  Result := GetConnection.GetIZPlainDriver;
529 end;
530 
531 function TZDbcPooledConnection.GetHostVersion: Integer;
532 begin
533  Result := GetConnection.GetHostVersion;
534 end;
535 
536 function TZDbcPooledConnection.GetMetadata: IZDatabaseMetadata;
537 begin
538  Result := GetConnection.GetMetadata;
539 end;
540 
541 function TZDbcPooledConnection.GetParameters: TStrings;
542 begin
543  Result := GetConnection.GetParameters;
544 end;
545 
546 function TZDbcPooledConnection.GetTransactionIsolation: TZTransactIsolationLevel;
547 begin
548  Result := GetConnection.GetTransactionIsolation;
549 end;
550 
551 function TZDbcPooledConnection.GetWarnings: EZSQLWarning;
552 begin
553  Result := GetConnection.GetWarnings;
554 end;
555 
556 function TZDbcPooledConnection.IsClosed: Boolean;
557 begin
558  Result := (FConnection = nil) or FConnection.IsClosed;
559 end;
560 
561 function TZDbcPooledConnection.IsReadOnly: Boolean;
562 begin
563  Result := GetConnection.IsReadOnly;
564 end;
565 
566 function TZDbcPooledConnection.NativeSQL(const SQL: string): string;
567 begin
568  Result := GetConnection.NativeSQL(SQL);
569 end;
570 
571 procedure TZDbcPooledConnection.Open;
572 begin
573  GetConnection.Open;
574 end;
575 
576 function TZDbcPooledConnection.PingServer: Integer;
577 begin
578  Result := GetConnection.PingServer;
579 end;
580 
581 function TZDbcPooledConnection.PrepareCall(const SQL: string): IZCallableStatement;
582 begin
583  Result := GetConnection.PrepareCall(SQL);
584 end;
585 
586 function TZDbcPooledConnection.PrepareCallWithParams(const SQL: string; Info: TStrings): IZCallableStatement;
587 begin
588  Result := GetConnection.PrepareCallWithParams(SQL, Info);
589 end;
590 
591 function TZDbcPooledConnection.PrepareStatement(const SQL: string): IZPreparedStatement;
592 begin
593  Result := GetConnection.PrepareStatement(SQL);
594 end;
595 
596 function TZDbcPooledConnection.PrepareStatementWithParams(const SQL: string; Info: TStrings): IZPreparedStatement;
597 begin
598  Result := GetConnection.PrepareStatementWithParams(SQL, Info);
599 end;
600 
601 procedure TZDbcPooledConnection.PrepareTransaction(const transactionid: string);
602 begin
603  GetConnection.PrepareTransaction(transactionid);
604 end;
605 
606 procedure TZDbcPooledConnection.Rollback;
607 begin
608  GetConnection.Rollback;
609 end;
610 
611 procedure TZDbcPooledConnection.RollbackPrepared(const transactionid: string);
612 begin
613  GetConnection.RollbackPrepared(transactionid);
614 end;
615 
616 procedure TZDbcPooledConnection.SetAutoCommit(Value: Boolean);
617 begin
618  GetConnection.SetAutoCommit(Value);
619 end;
620 
621 procedure TZDbcPooledConnection.SetCatalog(const Value: string);
622 begin
623  GetConnection.SetCatalog(Value);
624 end;
625 
626 procedure TZDbcPooledConnection.SetReadOnly(Value: Boolean);
627 begin
628  GetConnection.SetReadOnly(Value);
629 end;
630 
631 procedure TZDbcPooledConnection.SetTransactionIsolation(Value: TZTransactIsolationLevel);
632 begin
633  GetConnection.SetTransactionIsolation(Value);
634 end;
635 
636 {**
637  EgonHugeist: Check if the given Charset for Compiler/Database-Support!!
638  Not supported means if there is a pissible String-DataLoss.
639  So it raises an Exception if case of settings. This handling
640  is an improofment to inform Zeos-Users about the troubles the given
641  CharacterSet may have.
642  @param CharSet the CharacterSet which has to be proofed
643  @param DoArrange represents a switch to check and set a aternative ZAlias as
644  default. This means it ignores the choosen Client-CharacterSet and sets a
645  "more" Zeos-Compatible Client-CharacterSet if known.
646 }
647 procedure TZDbcPooledConnection.CheckCharEncoding(CharSet: String;
648  const DoArrange: Boolean = False);
649 begin
650  Self.GetConSettings.ClientCodePage := GetIZPlainDriver.ValidateCharEncoding(CharSet, DoArrange);
651  FClientCodePage := ConSettings.ClientCodePage^.Name; //resets the developer choosen ClientCodePage
652 end;
653 
654 
655 {**
656  EgonHugeist: this is a compatibility-Option for exiting Applictions.
657  Zeos is now able to preprepare direct insered SQL-Statements.
658  Means do the UTF8-preparation if the CharacterSet was choosen.
659  So we do not need to do the SQLString + UTF8Encode(Edit1.Test) for example.
660  @result True if coAutoEncodeStrings was choosen in the TZAbstractConnection
661 }
662 function TZDbcPooledConnection.GetAutoEncodeStrings: Boolean;
663 begin
664  Result := FAutoEncodeStrings;
665 end;
666 
667 procedure TZDbcPooledConnection.SetAutoEncodeStrings(const Value: Boolean);
668 begin
669  FAutoEncodeStrings := Value;
670 end;
671 
672 {**
673  EgonHugeist:
674  Returns the BinaryString in a Tokenizer-detectable kind
675  If the Tokenizer don't need to predetect it Result = BinaryString
676  @param Value represents the Binary-String
677  @param EscapeMarkSequence represents a Tokenizer detectable EscapeSequence (Len >= 3)
678  @result the detectable Binary String
679 }
680 function TZDbcPooledConnection.GetBinaryEscapeString(const Value: RawByteString): String;
681 begin
682  Result := GetConnection.GetBinaryEscapeString(Value);
683 end;
684 
685 function TZDbcPooledConnection.GetBinaryEscapeString(const Value: TByteDynArray): String;
686 begin
687  Result := GetConnection.GetBinaryEscapeString(Value);
688 end;
689 
690 function TZDbcPooledConnection.GetEscapeString(const Value: ZWideString): ZWideString;
691 begin
692  Result := GetConnection.GetEscapeString(Value);
693 end;
694 
695 function TZDbcPooledConnection.GetEscapeString(const Value: RawByteString): RawByteString;
696 begin
697  Result := GetConnection.GetEscapeString(Value);
698 end;
699 
700 function TZDbcPooledConnection.GetEncoding: TZCharEncoding;
701 begin
702  Result := ConSettings.ClientCodePage^.Encoding;
703 end;
704 
705 function TZDbcPooledConnection.GetConSettings: PZConSettings;
706 begin
707  Result := @ConSettings;
708 end;
709 
710 {$IFDEF ZEOS_TEST_ONLY}
711 function TZDbcPooledConnection.GetTestMode: Byte;
712 begin
713  Result := FTestMode;
714 end;
715 
716 procedure TZDbcPooledConnection.SetTestMode(Mode: Byte);
717 begin
718  FTestMode := Mode;
719 end;
720 {$ENDIF}
721 
722 {**
723  Result 100% Compiler-Compatible
724  And sets it Result to ClientCodePage by calling the
725  PlainDriver.GetClientCodePageInformations function
726 
727  @param ClientCharacterSet the CharacterSet which has to be checked
728  @result PZCodePage see ZCompatible.pas
729 }
730 function TZDbcPooledConnection.GetClientCodePageInformations: PZCodePage; //EgonHugeist
731 begin
732  Result := ConSettings.ClientCodePage
733 end;
734 
735 { TZDbcPooledConnectionDriver }
736 
737 constructor TZDbcPooledConnectionDriver.Create;
738 begin
739  inherited Create;
740 
741  PoolList := TObjectList.Create(True);
742  URLList := TStringList.Create;
743  AddSupportedProtocol(PooledPrefix + '*');
744 end;
745 
746 destructor TZDbcPooledConnectionDriver.Destroy;
747 begin
748  PoolList.Free;
749  URLList.Free;
750 
751  inherited;
752 end;
753 
754 function TZDbcPooledConnectionDriver.AcceptsURL(const URL: string): Boolean;
755 begin
756  Result := Copy(URL, 1, 5 + Length(PooledPrefix)) = 'zdbc:' + PooledPrefix;
757 end;
758 
759 function TZDbcPooledConnectionDriver.Connect(const URL: TZURL): IZConnection;
760 var
761  TempURL: TZURL;
762  I: Integer;
763  ConnectionPool: TConnectionPool;
764  ConnetionTimeout: Integer;
765  MaxConnections: Integer;
766  Wait: Boolean;
767 begin
768  Result := nil;
769 
770  TempURL := TZURL.Create;
771  try
772  TempURL.URL := GetEmbeddedURL(URL.URL);
773  TempURL.Properties.Text := URL.Properties.Text;
774 
775  ConnectionPool := nil;
776 
777 { TODO
778  - Read and process connection properties 'timeout', 'poolsize' and 'wait' }
779 
780  //
781  // Search for an existing pool for the URL.
782  // There is room to improve the algorithm used to decide when a pool is
783  // compatible with a given URL. For now, i am just comparing the URL strings.
784  //
785  for I := 0 to PoolList.Count - 1 do
786  if URLList[I] = TempURL.URL then
787  begin
788  ConnectionPool := TConnectionPool(PoolList[I]);
789  Break;
790  end;
791 
792  //
793  // Create a new pool if needed.
794  //
795  if ConnectionPool = nil then
796  begin
797  ConnetionTimeout := StrToIntDef(TempURL.Properties.Values['ConnectionTimeout'], 0);
798  MaxConnections := StrToIntDef(TempURL.Properties.Values['MaxConnections'], 0);
799  Wait := StrToBoolDef(TempURL.Properties.Values['Wait'], True);
800  ConnectionPool := TConnectionPool.Create(TempURL.URL, ConnetionTimeout, MaxConnections, Wait);
801  PoolList.Add(ConnectionPool);
802  URLList.Add(TempURL.URL);
803  end;
804 
805  Result := TZDbcPooledConnection.Create(ConnectionPool);
806  finally
807  TempURL.Free;
808  end;
809 end;
810 
811 function TZDbcPooledConnectionDriver.GetClientVersion(const URL: string): Integer;
812 begin
813  Result := DriverManager.GetDriver(GetEmbeddedURL(URL)).GetClientVersion(GetEmbeddedURL(URL));
814 end;
815 
816 function TZDbcPooledConnectionDriver.GetMajorVersion: Integer;
817 begin
818  Result := 1;
819 end;
820 
821 function TZDbcPooledConnectionDriver.GetMinorVersion: Integer;
822 begin
823  Result := 0;
824 end;
825 
826 function TZDbcPooledConnectionDriver.GetPropertyInfo(const URL: string; Info: TStrings): TStrings;
827 begin
828  Result := DriverManager.GetDriver(GetEmbeddedURL(URL)).GetPropertyInfo(GetEmbeddedURL(URL), Info);
829  if Result = nil then
830  Result := TStringList.Create;
831  Result.Values['ConnectionTimeout'] := '0';
832  Result.Values['MaxConnections'] := '0';
833  Result.Values['Wait'] := 'True';
834 end;
835 
836 function TZDbcPooledConnectionDriver.GetSubVersion: Integer;
837 begin
838  Result := 0;
839 end;
840 
841 {function TZDbcPooledConnectionDriver.GetSupportedProtocols: TStringDynArray;
842 begin
843  SetLength(Result, 1);
844  Result[0] := PooledPrefix + '*';
845 end;}
846 
847 function TZDbcPooledConnectionDriver.GetEmbeddedURL(const URL: String): String;
848 begin
849  if Copy(URL, 1, 5 + Length(PooledPrefix)) = 'zdbc:' + PooledPrefix then
850  Result := 'zdbc:' + Copy(URL, 5 + Length(PooledPrefix) + 1, Length(URL))
851  else
852  raise Exception.Create('TZDbcPooledConnectionDriver.GetRealURL - URL must start with ''zdbc:' + PooledPrefix+ '''');
853 end;
854 
855 var
856  _Driver: IZDriver;
857 
858 { TConnectionTimeoutThread }
859 
860 constructor TConnectionTimeoutThread.Create(const ConnectionPool: TConnectionPool);
861 begin
862  inherited Create(False);
863 
864  FConnectionPool := ConnectionPool;
865  FreeOnTerminate := False;
866 end;
867 
868 procedure TConnectionTimeoutThread.Execute;
869 var
870  I: Integer;
871 begin
872  while not Terminated do
873  begin
874  Sleep(1000);
875 
876  //
877  // Check if there are timed out connections and releases them
878  //
879  FConnectionPool.FCriticalSection.Enter;
880  try
881  for I := 0 to Length(FConnectionPool.FConnections) - 1 do
882  if (FConnectionPool.FConnections[I] <> nil) and
883  (not FConnectionPool.FSlotsInUse[I]) and
884  (FConnectionPool.FConnectionsReturnTimes[I] <> 0) and
885  (MilliSecondsBetween(FConnectionPool.FConnectionsReturnTimes[I], Now) > FConnectionPool.FConnectionTimeout * 1000) then
886  FConnectionPool.FConnections[I] := nil;
887  finally
888  FConnectionPool.FCriticalSection.Leave;
889  end;
890  end;
891 end;
892 
893 initialization
894  _Driver := TZDbcPooledConnectionDriver.Create;
895  DriverManager.RegisterDriver(_Driver);
896 
897 finalization
898  DriverManager.DeregisterDriver(_Driver);
899 
900 end.
901