1{
2    This file is part of the Free Pascal run time library.
3    Copyright (c) 1999-2000 by Michael Van Canneyt, member of the
4    Free Pascal development team
5
6    TDatabase and related objects implementation
7
8    See the file COPYING.FPC, included in this distribution,
9    for details about the copyright.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 **********************************************************************}
16
17{ ---------------------------------------------------------------------
18    TDatabase
19  ---------------------------------------------------------------------}
20
21Procedure TDatabase.CheckConnected;
22
23begin
24  If Not Connected Then
25    DatabaseError(SNotConnected,Self);
26end;
27
28
29Procedure TDatabase.CheckDisConnected;
30begin
31  If Connected Then
32    DatabaseError(SConnected,Self);
33end;
34
35procedure TDatabase.DoConnect;
36begin
37  DoInternalConnect;
38  FConnected := True;
39end;
40
41procedure TDatabase.DoDisconnect;
42begin
43  CloseDatasets;
44  CloseTransactions;
45  DoInternalDisConnect;
46  if csLoading in ComponentState then
47    FOpenAfterRead := false;
48  FConnected := False;
49end;
50
51function TDatabase.GetConnected: boolean;
52begin
53  Result:= FConnected;
54end;
55
56constructor TDatabase.Create(AOwner: TComponent);
57
58begin
59  Inherited Create(AOwner);
60  FParams:=TStringlist.Create;
61  FDatasets:=TThreadList.Create;
62  FTransactions:=TThreadList.Create;
63  FConnected:=False;
64end;
65
66destructor TDatabase.Destroy;
67
68begin
69  Connected:=False;
70  RemoveDatasets;
71  RemoveTransactions;
72  FDatasets.Free;
73  FTransactions.Free;
74  FParams.Free;
75  Inherited Destroy;
76end;
77
78procedure TDatabase.CloseDataSets;
79
80Var
81  I : longint;
82  L : TList;
83
84begin
85  If Assigned(FDatasets) then
86    begin
87    L:=FDatasets.LockList;
88    try
89      For I:=L.Count-1 downto 0 do
90        TDataset(L[i]).Close;
91    finally
92      FDatasets.UnlockList;
93    end;
94    end;
95end;
96
97procedure TDatabase.CloseTransactions;
98
99Var
100  I : longint;
101  L : TList;
102
103begin
104  If Assigned(FTransactions) then
105    begin
106    L:=FTransactions.LockList;
107    try
108      For I:=L.Count-1 downto 0 do
109        try
110          TDBTransaction(L[i]).EndTransaction;
111        except
112          if not ForcedClose then
113            Raise;
114        end;
115    finally
116      FTransactions.UnlockList
117    end;
118    end;
119end;
120
121procedure TDatabase.RemoveDataSets;
122
123Var
124  I : longint;
125  L : TList;
126begin
127  If Assigned(FDatasets) then
128    begin
129    L:=FDatasets.LockList;
130    try
131      For I:=L.Count-1 downto 0 do
132        TDBDataset(L[i]).Database:=Nil;
133    finally
134      FDatasets.UnlockList;
135    end;
136    end;
137end;
138
139procedure TDatabase.RemoveTransactions;
140
141Var
142  I : longint;
143  L : TList;
144begin
145  If Assigned(FTransactions) then
146    begin
147    L:=FTransactions.LockList;
148    try
149      For I:=L.Count-1 downto 0 do
150        TDBTransaction(L[i]).Database:=Nil;
151    finally
152      FTransactions.UnlockList
153    end;
154    end;
155end;
156
157procedure TDatabase.SetParams(AValue: TStrings);
158begin
159  if AValue<>nil then
160    FParams.Assign(AValue);
161end;
162
163Function TDatabase.GetDataSetCount : Longint;
164
165Var
166  L : TList;
167
168begin
169  Result:=0;
170  If Assigned(FDatasets) Then
171    begin
172    L:=FDatasets.LockList;
173    try
174      Result:=L.Count;
175    finally
176      FDatasets.Unlocklist;
177    end;
178    end;
179end;
180
181Function TDatabase.GetTransactionCount : Longint;
182
183Var
184  L : TList;
185
186begin
187  Result:=0;
188  If Assigned(FTransactions) Then
189    begin
190    L:=FTransactions.LockList;
191    try
192      Result:=L.Count;
193    finally
194      FTransactions.UnlockList;
195    end;
196    end;
197end;
198
199Function TDatabase.GetDataset(Index : longint) : TDataset;
200
201Var
202  L : TList;
203
204begin
205  If Not Assigned(FDatasets) then
206    begin
207    result := nil;
208    DatabaseError(SNoDatasets);
209    end
210  else
211    begin
212    L:=FDatasets.LockList;
213    try
214      Result:=TDataset(L[Index])
215    finally
216      FDatasets.UnlockList;
217    end;
218    end;
219end;
220
221Function TDatabase.GetTransaction(Index : longint) : TDBtransaction;
222
223Var
224  L : TList;
225
226begin
227  If Not Assigned(FTransactions) then
228    begin
229    result := nil;
230    DatabaseError(SNoTransactions);
231    end
232  else
233    begin
234    L:=FTransactions.LockList;
235    try
236      Result:=TDBTransaction(L[Index])
237    finally
238      FTransactions.UnlockList;
239    end;
240    end;
241end;
242
243procedure TDatabase.RegisterDataset (DS : TDBDataset);
244
245Var
246  I : longint;
247  L : TList;
248begin
249  L:=FDatasets.LockList;
250  try
251    I:=L.IndexOf(DS);
252    If I=-1 then
253      L.Add(DS)
254    else
255      DatabaseErrorFmt(SDatasetRegistered,[DS.Name]);
256  finally
257    FDatasets.UnlockList;
258  end;
259end;
260
261procedure TDatabase.RegisterTransaction (TA : TDBTransaction);
262
263Var
264  I : longint;
265  L : TList;
266
267begin
268  L:=FTransactions.LockList;
269  try
270    I:=L.IndexOf(TA);
271    If I=-1 then
272      L.Add(TA)
273    else
274      DatabaseErrorFmt(STransactionRegistered,[TA.Name]);
275  finally
276    FTransactions.UnlockList;
277  end;
278end;
279
280procedure TDatabase.UnRegisterDataset (DS : TDBDataset);
281
282Var
283  I : longint;
284  L : TList;
285
286begin
287  L:=FDatasets.LockList;
288  try
289    I:=L.IndexOf(DS);
290    If I<>-1 then
291      L.Delete(I)
292    else
293      DatabaseErrorFmt(SNoDatasetRegistered,[DS.Name]);
294  finally
295    FDatasets.UnlockList;
296  end;
297end;
298
299procedure TDatabase.UnRegisterTransaction (TA : TDBTransaction);
300
301Var
302  I : longint;
303  L : TList;
304
305begin
306  L:=FTransactions.LockList;
307  try
308    I:=L.IndexOf(TA);
309    If I<>-1 then
310      L.Delete(I)
311    else
312      DatabaseErrorFmt(SNoTransactionRegistered,[TA.Name]);
313  finally
314    FTransactions.UnlockList;
315  end;
316end;
317
318
319{ ---------------------------------------------------------------------
320    TDBDataset
321  ---------------------------------------------------------------------}
322
323Procedure TDBDataset.SetDatabase (Value : TDatabase);
324
325begin
326  If Value<>FDatabase then
327    begin
328    CheckInactive;
329    If Assigned(FDatabase) then
330      FDatabase.UnregisterDataset(Self);
331    If Value<>Nil Then
332      Value.RegisterDataset(Self);
333    FDatabase:=Value;
334    end;
335end;
336
337Procedure TDBDataset.SetTransaction (Value : TDBTransaction);
338
339begin
340  CheckInactive;
341  If Value<>FTransaction then
342    begin
343    If Assigned(FTransaction) then
344      FTransaction.UnregisterDataset(Self);
345    If Value<>Nil Then
346      Value.RegisterDataset(Self);
347    FTransaction:=Value;
348    end;
349end;
350
351Procedure TDBDataset.CheckDatabase;
352
353begin
354  If (FDatabase=Nil) then
355    DatabaseError(SErrNoDatabaseAvailable,Self)
356end;
357
358Destructor TDBDataset.Destroy;
359
360begin
361  Database:=Nil;
362  Transaction:=Nil;
363  Inherited;
364end;
365
366{ ---------------------------------------------------------------------
367    TDBTransaction
368  ---------------------------------------------------------------------}
369procedure TDBTransaction.SetActive(Value : boolean);
370begin
371  if FActive and (not Value) then
372    EndTransaction
373  else if (not FActive) and Value then
374    if csLoading in ComponentState then
375      begin
376      FOpenAfterRead := true;
377      exit;
378      end
379    else
380      StartTransaction;
381end;
382
383procedure TDBTransaction.Loaded;
384
385begin
386  inherited;
387  try
388    if FOpenAfterRead then SetActive(true);
389  except
390    if csDesigning in Componentstate then
391      InternalHandleException
392    else
393      raise;
394  end;
395end;
396
397procedure TDBTransaction.InternalHandleException;
398
399begin
400  if assigned(classes.ApplicationHandleException) then
401    classes.ApplicationHandleException(self)
402  else
403    ShowException(ExceptObject,ExceptAddr);
404end;
405
406procedure TDBTransaction.CheckActive;
407
408begin
409  If not FActive Then
410    DatabaseError(STransNotActive,Self);
411end;
412
413procedure TDBTransaction.CheckInactive;
414
415begin
416  If FActive Then
417    DatabaseError(STransActive,Self);
418end;
419
420procedure TDBTransaction.Commit;
421begin
422  EndTransaction;
423end;
424
425procedure TDBTransaction.CommitRetaining;
426begin
427  Commit;
428  StartTransaction;
429end;
430
431procedure TDBTransaction.Rollback;
432begin
433  EndTransaction;
434end;
435
436procedure TDBTransaction.RollbackRetaining;
437begin
438  RollBack;
439  StartTransaction;
440end;
441
442procedure TDBTransaction.CloseTrans;
443
444begin
445  FActive := false;
446end;
447
448procedure TDBTransaction.OpenTrans;
449
450begin
451  FActive := true;
452end;
453
454procedure TDBTransaction.SetDatabase(Value: TDatabase);
455
456begin
457  If Value<>FDatabase then
458    begin
459    CheckInactive;
460    If Assigned(FDatabase) then
461      FDatabase.UnregisterTransaction(Self);
462    If Value<>Nil Then
463      Value.RegisterTransaction(Self);
464    FDatabase:=Value;
465    end;
466end;
467
468constructor TDBTransaction.Create(AOwner: TComponent);
469
470begin
471  inherited Create(AOwner);
472  FDatasets:=TThreadList.Create;
473end;
474
475procedure TDBTransaction.CheckDatabase;
476
477begin
478  If (FDatabase=Nil) then
479    DatabaseError(SErrNoDatabaseAvailable,Self)
480end;
481
482Function TDBTransaction.AllowClose(DS : TDBDataset) : Boolean;
483
484begin
485  Result:=Assigned(DS);
486end;
487
488procedure TDBTransaction.CloseDataSets;
489
490Var
491  I : longint;
492  L : TList;
493  DS : TDBDataset;
494
495begin
496  If Assigned(FDatasets) then
497    begin
498    L:=FDatasets.LockList;
499    try
500      For I:=L.Count-1 downto 0 do
501        begin
502        DS:=TDBDataset(L[i]);
503        If AllowClose(DS) then
504          DS.Close;
505        end;
506    finally
507      FDatasets.UnlockList;
508    end;
509    end;
510end;
511
512destructor TDBTransaction.Destroy;
513
514begin
515  Database:=Nil;
516  CloseDataSets;
517  RemoveDatasets;
518  FDatasets.Free;
519  Inherited;
520end;
521
522procedure TDBTransaction.RemoveDataSets;
523
524Var
525  I : longint;
526  L : TList;
527
528begin
529  If Not Assigned(FDatasets) then
530    exit;
531  L:=FDatasets.LockList;
532  try
533    For I:=L.Count-1 downto 0 do
534      TDBDataset(L[i]).Transaction:=Nil;
535  finally
536    FDatasets.unlockList;
537  end;
538end;
539
540function TDBTransaction.GetDataset(Index: longint): TDBDataset;
541
542Var
543  L : TList;
544
545
546begin
547  If Not Assigned(FDatasets) then
548    DatabaseError(SNoDatasets);
549  L:=FDatasets.LockList;
550  try
551    Result:=TDBDataset(L[Index])
552  finally
553    FDatasets.UnlockList;
554  end;
555end;
556
557function TDBTransaction.GetDataSetCount: Longint;
558
559Var
560  L : TList;
561
562begin
563  Result:=0;
564  If Not Assigned(FDatasets) Then
565    exit;
566  L:=FDatasets.lockList;
567  try
568    Result:=L.Count
569  finally
570    FDatasets.UnlockList;
571  end;
572end;
573
574procedure TDBTransaction.RegisterDataset (DS : TDBDataset);
575
576Var
577  I : longint;
578  L : TList;
579begin
580  L:=FDatasets.LockList;
581  try
582    I:=L.IndexOf(DS);
583    If I=-1 then
584      L.Add(DS)
585    else
586      DatabaseErrorFmt(SDatasetRegistered,[DS.Name]);
587  finally
588    FDatasets.UnlockList;
589  end;
590end;
591
592procedure TDBTransaction.UnRegisterDataset (DS : TDBDataset);
593
594Var
595  I : longint;
596  L : TList;
597
598begin
599  L:=FDatasets.LockList;
600  try
601    I:=L.IndexOf(DS);
602    If I<>-1 then
603      L.Delete(I)
604    else
605      DatabaseErrorFmt(SNoDatasetRegistered,[DS.Name]);
606  finally
607    FDatasets.UnlockList;
608  end;
609end;
610
611{ ---------------------------------------------------------------------
612    TCustomConnection
613  ---------------------------------------------------------------------}
614
615function TCustomConnection.GetDataSet(Index: Longint): TDataSet;
616begin
617  Result := nil;
618end;
619
620function TCustomConnection.GetDataSetCount: Longint;
621begin
622  Result := 0;
623end;
624
625procedure TCustomConnection.InternalHandleException;
626begin
627  if assigned(classes.ApplicationHandleException) then
628    classes.ApplicationHandleException(self)
629  else
630    ShowException(ExceptObject,ExceptAddr);
631end;
632
633procedure TCustomConnection.SetAfterConnect(const AValue: TNotifyEvent);
634begin
635  FAfterConnect:=AValue;
636end;
637
638procedure TCustomConnection.SetAfterDisconnect(const AValue: TNotifyEvent);
639begin
640  FAfterDisconnect:=AValue;
641end;
642
643procedure TCustomConnection.SetBeforeConnect(const AValue: TNotifyEvent);
644begin
645  FBeforeConnect:=AValue;
646end;
647
648procedure TCustomConnection.SetBeforeDisconnect(const AValue: TNotifyEvent);
649begin
650  FBeforeDisconnect:=AValue;
651end;
652
653procedure TCustomConnection.DoLoginPrompt;
654
655var
656  ADatabaseName, AUserName, APassword: string;
657
658begin
659  if FLoginPrompt then
660    begin
661    GetLoginParams(ADatabaseName, AUserName, APassword);
662    if Assigned(FOnLogin) then
663      FOnLogin(Self, AUserName, APassword) // by value
664    else if Assigned(LoginDialogExProc) then
665      begin
666      LoginDialogExProc(ADatabaseName, AUserName, APassword, False); // by reference
667      SetLoginParams(ADatabaseName, AUserName, APassword);
668      end;
669    end;
670end;
671
672procedure TCustomConnection.SetConnected(Value: boolean);
673
674begin
675  If Value<>Connected then
676    begin
677    If Value then
678      begin
679      if csReading in ComponentState then
680        begin
681        FStreamedConnected := true;
682        exit;
683        end
684      else
685        begin
686        if Assigned(BeforeConnect) then
687          BeforeConnect(self);
688        DoLoginPrompt;
689        DoConnect;
690        if Assigned(AfterConnect) then
691          AfterConnect(self);
692        end;
693      end
694    else
695      begin
696      if Assigned(BeforeDisconnect) then
697        BeforeDisconnect(self);
698      DoDisconnect;
699      if Assigned(AfterDisconnect) then
700        AfterDisconnect(self);
701      end;
702    end;
703end;
704
705procedure TCustomConnection.GetLoginParams(out ADatabaseName, AUserName, APassword: string);
706begin
707  if IsPublishedProp(Self,'DatabaseName') then
708    ADatabaseName := GetStrProp(Self,'DatabaseName');
709  if IsPublishedProp(Self,'UserName') then
710    AUserName := GetStrProp(Self,'UserName');
711  if IsPublishedProp(Self,'Password') then
712    APassword := GetStrProp(Self,'Password');
713end;
714
715procedure TCustomConnection.SetLoginParams(const ADatabaseName, AUserName, APassword: string);
716begin
717  if IsPublishedProp(Self,'DatabaseName') then
718    SetStrProp(Self,'DatabaseName',ADatabaseName);
719  if IsPublishedProp(Self,'UserName') then
720    SetStrProp(Self,'UserName',AUserName);
721  if IsPublishedProp(Self,'Password') then
722    SetStrProp(Self,'Password',APassword);
723end;
724
725procedure TCustomConnection.DoConnect;
726
727begin
728  // Do nothing yet
729end;
730
731procedure TCustomConnection.DoDisconnect;
732
733begin
734  // Do nothing yet
735end;
736
737function TCustomConnection.GetConnected: boolean;
738
739begin
740  Result := False;
741end;
742
743procedure TCustomConnection.Loaded;
744begin
745  inherited Loaded;
746  try
747    if FStreamedConnected then
748      SetConnected(true);
749  except
750    if csDesigning in Componentstate then
751      InternalHandleException
752    else
753      raise;
754  end;
755end;
756
757procedure TCustomConnection.Close(ForceClose : Boolean = False);
758begin
759  try
760    ForcedClose:=ForceClose;
761    Connected := False;
762  finally
763    ForcedClose:=false;
764  end;
765end;
766
767destructor TCustomConnection.Destroy;
768begin
769  Connected:=False;
770  Inherited Destroy;
771end;
772
773procedure TCustomConnection.Open;
774begin
775  Connected := True;
776end;
777
778