-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathCcConf.pas
More file actions
2123 lines (1913 loc) · 75.6 KB
/
CcConf.pas
File metadata and controls
2123 lines (1913 loc) · 75.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//CopyCat replication suite<p/>
//Copyright (c) 2014 Microtec Communications<p/>
//For any questions or technical support, contact us at contact@copycat.fr
unit CcConf;
{$I CC.INC}
interface
uses Classes, DB, CcDB, CCat, CcProviders, CcKeys;
type
TConfirmEvent = procedure(Sender:TObject; var CanContinue:Boolean) of object;
TCcScriptEvent = procedure(Sender: TObject; Script: TStrings) of object;
//Summary:
//\Parameters of a stored procedure.
//Description:
//TCcProcParams represents the parameters of a stored procedure. It is only used in
//conjunction with the TCcConfig component, when generating primary key
//synchronization SQL based on a stored procedure.<p/>
//<p/>
//The most common way to use TCcProcParams (for holding input parameters) is as
//follows:
// 1. Create an instance of TCcProcParams.
// 2. Pass it as a parameter to the TCcConfig.GetProcParams method, in order to
// fill it with the list of parameters of a given stored procedure, along with the
// data-type and field length of each one.
// 3. Fill in the values of the parameters, using the PARAM_VALUE field.
// 4. Call TCcConfig.GetProcGenerator, passing the TCcProcParams object in order
// to provide input parameter values.
//
//TCcProcParams is a TDataSet descendant, that defines the following fields:
//<table 25c%, 15c%>
//Field name Data type \Description
//=============== =============== -----------------------------
//PARAM_NAME <align center> Name of the stored procedure
// String parameter.
// </align>
//<align center> String Value of the parameter.
// PARAM_VALUE
// </align>
//FIELD_TYPE <align center> Data type.
// Integer
// </align>
//FIELD_LENGTH Integer Field size.
//</table>
TCcProcParams = class(TCcMemoryTable)
protected
procedure LoadFields;
procedure Loaded;override;
public
constructor Create(AOwner: TComponent); override;
end;
TCcStringList = class(TStringList)
private
function GetSQLCommaText: String;
procedure SetSQLCommaText(const Value: String);
public
property SQLCommaText : String read GetSQLCommaText write SetSQLCommaText;
end;
TCcConfigTable = class(TCollectionItem)
private
FTableName: String;
FFieldsIncluded: TStringList;
FFieldsExcluded: TStringList;
FCondition: TStringList;
FDeleteCondition: TStringList;
FUpdateCondition: TStringList;
FInsertCondition: TStringList;
FPriority: Integer;
FPKSyncStatements: TCcStringList;
FSyncStatements: TCcStringList;
FSyncFieldNames: TCcStringList;
procedure SetFieldsExcluded(const Value: TStringList);
procedure SetFieldsIncluded(const Value: TStringList);
procedure SetCondition(const Value: TStringList);
procedure SetDeleteCondition(const Value: TStringList);
procedure SetInsertCondition(const Value: TStringList);
procedure SetUpdateCondition(const Value: TStringList);
procedure SetPKSyncStatements(const Value: TCcStringList);
procedure SetSyncFieldNames(const Value: TCcStringList);
procedure SetSyncStatements(const Value: TCcStringList);
protected
function GetDisplayName: string; override;
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
published
property Priority: Integer read FPriority write FPriority;
property TableName: String read FTableName write FTableName;
//SQL statements for synchronizing primary key values (one row per statement, one statement for each field of the PK)
property PKSyncStatements: TCcStringList read FPKSyncStatements write SetPKSyncStatements;
//Names of fields to be sychronized using the SQL statements in SyncStatements (one row per field)
property SyncFieldNames: TCcStringList read FSyncFieldNames write SetSyncFieldNames;
//SQL statements for synchronizing key values that aren't part of the PK
//Description: The list of fields to synchronise is in the SyncFieldNames property.
//Put one row per statement, corresponding to the fields listed in SyncFieldNames
property SyncStatements: TCcStringList read FSyncStatements write SetSyncStatements;
//SQL condition to be used in the replication triggers
property Condition: TStringList read FCondition write SetCondition;
//SQL condition to be used in the update replication triggers
property UpdateCondition: TStringList read FUpdateCondition write SetUpdateCondition;
//SQL condition to be used in the insert replication triggers
property InsertCondition: TStringList read FInsertCondition write SetInsertCondition;
//SQL condition to be used in the delete replication triggers
property DeleteCondition: TStringList read FDeleteCondition write SetDeleteCondition;
//Fields to be taken into account for replicating this table.
//Description: If nothing is set, the default behaviour is to always replicate any row that is changed,
//without comparing the field values. Therefore, even if none if the fields actually changed at all,
//the row will be replicated because the triggers were fired.<p/>
//If you explicitly set a list of fields to include in replication, then CopyCat will check those fields
//in the triggers and will only fire replication for the row if at least one of the included fields was changed.
//That means that by default, any new field will be ignored unlesss it's added to this list.
property FieldsIncluded: TStringList read FFieldsIncluded write SetFieldsIncluded;
//Fields to be ignored in the replication triggers.
//Description: If you set fields in the FieldsExcluded property, CopyCat will put the list of all fields
//except the excluded ones into the FieldsIncluded property. Thus, if you add a field in your table
//and don't want to exclude it), you need to call TCcConfig.GenerateConfig again in order to refresh the
//FieldsIncluded property and update the triggers.
property FieldsExcluded: TStringList read FFieldsExcluded write SetFieldsExcluded;
end;
TCcConfigTables = class (TCollection)
private
FOwner: TPersistent;
function GetItem(Index: Integer): TCcConfigTable;
protected
function GetOwner: TPersistent; override;
public
constructor Create(AOwner: TPersistent);
function FindTable(tableName: String): TCcConfigTable;
function Add: TCcConfigTable;
property Item[Index: Integer]: TCcConfigTable read GetItem; default;
end;
//Summary:
//Component for generating replication meta-data.
//Description:
//Before any replication can take place, the appropriate meta-data must be created
//in both databases. TCcConfig allows you to generate this meta-data (based on
//settings specific to each setup), and apply it to the database.<p/>
//<p/>
//Here's the simplest way to configure your databases :<p/>
// 1. Setup the list of tables that you want to replicate using the Tables property.<p/>
// 2. Fill out the list of nodes in the Nodes property. You need to set one node name
// for every replication node towards which you want to send the selected database's data.<p/>
// 3. Set ConfigName to a short, descriptive name. This allows you to have several different
// configurations, with different lists of tables and nodes, and different conditions and options.
// If you give each a different configuration name, they will be able to fit together seamlessly
// in the same database.<p/>
// 4. Call GenerateConfig to apply the above setting to the database. The settings are also stored in
// the database, so they can be checked, and the triggers will only be recreated if the options have
// changed.
//
//Note: If you ever need to force CopyCat to recreate a trigger, for whatever reason, simply set the CREATED field
//of RPL$TABLES_CONFIG to 'N' for the row corresponding to the table and configuration that you need to update. This
//setting will force CopyCat to recreate the trigger next time you call GenerateConfig.
//[ComponentPlatformsAttribute( PidWin32 Or PidiOSDevice Or PidiOSSimulator )]
{ TCcConfig }
TCcConfig = class(TComponent)
private
FPrevDestroyQueries: TNotifyEvent;
FQueriesInitiated: Boolean;
FOnQueryReady :TCcScriptEvent;
Query: TStringList;
// FTerminator: String;
FScript: TStrings;
FFieldNames: TStringList;
FOnScriptReady: TCcNotifyEvent;
qProcedure: TCcQuery;
FConnection: TCcConnection;
qTable: TCcQuery;
qTableConfig: TCcQuery;
FPrevExecQuery: TNotifyEvent;
FTables: TCcConfigTables;
FConfigName: String;
FNodes: TStringList;
FFailIfNoPK: Boolean;
FTrackFieldChanges: Boolean;
FOnProgress :TNotifyEvent;
procedure AddUser(cUserName: String);
function GetConnection :TCcConnection;
procedure QueryReady;
procedure QueryExecuted(Sender: TObject);
procedure DestroyQueries(Sender: TObject);
procedure RemoveUser(cUserName: String);
procedure SetConnection(const Value: TCcConnection);
function FindTable(TableName: String) :Boolean;
procedure GetFieldNames(TableName: String);
procedure CheckConnected;
procedure SetScript(const Value: TStrings);
procedure ScriptReady;
procedure CheckMetaData;
procedure SetTables(const Value: TCcConfigTables);
procedure SetNodes(const Value: TStringList);
procedure SetConfigName(const Value: String);
procedure DropTable(cTableName: String);
function ExistsInList(str: String; list: TStringList): Boolean;
function GetVersion: String;
procedure SetVersion(const Value: String);
procedure RefreshDisplay;
function ListRPLTables: String;
function ListRPLTablesConfig: String;
function CalcTriggerName(cTableName: String): String;
protected
procedure FillTables;virtual;
procedure Notification(AComponent: TComponent; Operation: TOperation);override;
public
procedure CheckUsers;
procedure DropAllTriggers;
procedure ConnectAndRemoveConfig;
function TableNameWithConfig(cTableName: String): String;
function ListRPLTablesConfigWithTriggerNames: String;
{*********************************************************************************
Summary:
Connect to the database.
Description:
Call Connect to open a connection to the database, using the parameters specified
in the ConnectParams property. The Tables and Procedures properties must be
assigned before calling Connect.<p/>
<p/>
After connecting, TCcConfig loads the list of tables and procedures into the
Tables and Procedures properties, and then checks that the necessary replication
meta-data exists in the database. See OnCreateMetaData for more information.
See Also:
Disconnect
*********************************************************************************}
procedure Connect;
{********************************************************
Summary:
Disconnect from the database.
Description:
Call Disconnect to close the connection to the database.
See Also:
Connect
********************************************************}
procedure Disconnect;
{********************************************************************************
Summary:
Build SQL statement for synchronizing a primary key or generator field, based on
a generator.
Parameters:
GenName : Name of the generator to use.
Increment : Amount to increment the generator by.
Returns:
The generated SQL statement, in a format suitable to be put in a 'PKn_GEN' or
'GENn_VALUE' field of Tables.
See Also:
GetProcGenerator, Tables
Note:
This function does not alter the Script property, it merely returns the SQL
statement.
********************************************************************************}
function GetGenerator(GenName: String; Increment: Integer): String;
//Summary:
//Get the list of parameters for a stored procedure.
//Parameters:
//ProcName : Name of the stored procedure.
//Params : Empty TCcProcParams object to fill.
//InputParam : Fill Params with input or output parameters of the procedure?
//See Also:
//GetProcGenerator
// procedure GetProcParams(ProcName: String; Params: TCcProcParams; InputParam: Boolean = True);
{********************************************************************************
Summary:
Build SQL statement for synchronizing a primary key or generator field of the
selected table, based on a stored procedure.
Returns:
The generated SQL statement, in a format suitable to be put in a 'PKn_GEN' or
'GENn_VALUE' field of Tables.
Parameters:
ProcName : Name of the stored procedure to select from.
Params : \Input parameters for the procedure, with their values filled in.
OutputParam : \Output parameter to be selected from the procedure.
See Also:
GetGenerator, Tables
Note:
This function does not alter the Script property, it merely returns the SQL
statement.
********************************************************************************}
// function GetProcGenerator(TableName, ProcName: String; Params: TCcProcParams; OutputParam: String): String;
{*****************************************************************************
Summary:
Generate the replication triggers for the selected table.
Description:
Call this method to create the necessary replication triggers for the current
table, based on the options set in Tables and Procedures.
See Also:
RemoveTriggers
*****************************************************************************}
procedure GenerateTriggers(TableName:String);
(*
{********************************************************************************
Summary:
Generate the replication meta-data for the selected procedure.
Description:
Use GenerateProcedure to generate the SQL necessary for creating the replication
meta-data for the current procedure.
See Also:
RemoveProcedure
********************************************************************************}
procedure GenerateProcedure(ProcName:String);
*)
{*****************************************************************************
Summary:
Remove replication triggers from the selected table.
Description:
Use RemoveTriggers to generate the SQL necessary for removing the replication
triggers from the "TableName" table.
See Also:
GenerateTriggers
Parameters:
TableName : Name of the table from which to remove triggers
*****************************************************************************}
procedure RemoveTriggers(TableName:String);
(*
{*******************************************************************************
Summary:
Remove the replication meta-data for the selected procedure.
Description:
Use RemoveProcedure to generate the SQL necessary for removing the replication
meta-data for the selected procedure. The procedure itself is not removed, only
the meta-data necessary for replicating it.
Parameters:
ProcName : Name of the stored procedure to remove
See Also:
GenerateProcedure
*******************************************************************************}
procedure RemoveProcedure(ProcName:String);
*)
constructor Create(AOwner: TComponent);override;
destructor Destroy;override;
// procedure PrepareBatchImport(tableName: String; sqlCondition: String; destinationNode: String);
{*******************************************************************************
Summary:
Creates triggers and / or replication nodes according to the configuration
Description:
Call GenerateConfig to create all the replication configuration based on the Nodes and Tables properties
If the list of tables to replicate is given, we automatically create triggers for all the tables.
If any table missing from the list (Tables property) its triggers are deleted.
If a list of nodes is given in the Nodes property, it is used to fill the RPL$USERS table.
*******************************************************************************}
procedure GenerateConfig(lForceRecreateTriggers: Boolean = False; lCheckUsers: Boolean = True);
published
//Summary: Set TrackFieldChanges to true in order to track the values of fields changed to RPL$LOG_VALUES
//Description: This option must be set if you plan to use TCcReplicator.ReplicateOnlyChangedFields
property TrackFieldChanges: Boolean read FTrackFieldChanges write FTrackFieldChanges;
//Indicates whether tables with no primary key are accepted or not
//If FailIfNoPK is true (the default value), then an error will be raised
//if a table is detected with no primary key.
//If FailIfNoPK is false, the full list of field values (excluding blobs and string fields over 50 chars)
//will be used instead
//ONLY USE THIS OPTION FOR ONE-WAY REPLICATION, otherwise, it could cause conflicts.
property FailIfNoPK: Boolean read FFailIfNoPK write FFailIfNoPK;
//Name of the current configuration.
property ConfigName: String read FConfigName write SetConfigName;
//The list of replication nodes (databases) towards which the data in the current database should be sent,
//for the specified configuration. Since each configuration can have a different list of nodes, you could
//very well send different tables to different nodes, or perhaps send part of one table to all nodes and the
//whole table to some other node.
property Nodes: TStringList read FNodes write SetNodes;
{***************************************************************************
Summary:
Terminator character.
Description:
Terminator is the character used for separating statements in the meta-data
script.
See Also:
Script
***************************************************************************}
// property Terminator: String read FTerminator write FTerminator;
{******************************************************************************
Summary:
Last meta-data script generated.
Description:
Script is filled (and overwritten) every time GenerateTriggers,
GenerateProcedure, RemoveTriggers or RemoveProcedure are called. It represents
the last generated Script.
See Also:
Terminator
******************************************************************************}
property Script: TStrings read FScript write SetScript;
{********************************************************************************
Summary:
Database connection parameters.
Description:
Connection defines the necessary parameters for connecting to the database to
be configured.
********************************************************************************}
property Connection: TCcConnection read FConnection write SetConnection;
{********************************************************************************
Summary:
Fired when an entire meta-data Script has been prepared.
Description:
When an script has been generated by TCcConfig, it is placed in the Script
property, and the OnScriptReady event is fired to give the application the
opportunity to either execute it, or stores it. The individual statements in the
script are separated using the Terminator character.
See Also:
OnQueryReady
********************************************************************************}
property OnScriptReady: TCcNotifyEvent read FOnScriptReady write FOnScriptReady;
{*********************************************************************************
Summary:
Fired for every individual meta-data query.
Description:
The purpose of OnQueryReady is to give the application the possibility to execute
the meta-data statements created by TCcConfig, one statement at a time.
See Also:
OnScriptReady
*********************************************************************************}
property OnQueryReady :TCcScriptEvent read FOnQueryReady write FOnQueryReady;
property OnProgress :TNotifyEvent read FOnProgress write FOnProgress;
//The Tables property hold the list of to be replicated for the current configuration
//You can also set various options.
//See also: TCcConfigTables
property Tables: TCcConfigTables read FTables write SetTables;
property Version: String read GetVersion write SetVersion;
end;
implementation
uses SysUtils;
function TCcConfig.TableNameWithConfig(cTableName: String): String;
var
test: String;
begin
//test := concat('_', PChar(cTableName));
//Result := test;
if (ConfigName <> '') then
// Result := '_' + cTableName
Result := concat(PChar(FConfigName), '_', PChar(cTableName))
else
Result := Trim(cTableName);
end;
procedure TCcConfig.RemoveTriggers(TableName: String);
var
cSQL: String;
begin
if not FindTable(TableName) then Exit;
if (ConfigName = '') then begin
FConnection.DBAdaptor.RemoveTriggers(qTable);
cSQL := 'update RPL$TABLES set created = ''N'' where %upper_case(table_name) = %upper_case(:table_name)';
end else begin
FConnection.DBAdaptor.RemoveTriggers(qTableConfig);
cSQL := 'update RPL$TABLES_CONFIG set created = ''N'' where %upper_case(table_name) = %upper_case(:table_name) and config_name = ' + QuotedStr(ConfigName);
end;
with GetConnection.UpdateQuery['TCcConfig_RemoveTriggers'] do begin
Close;
SQL.Text := cSQL;
if GetConnection.DBAdaptor.QuoteMetadata then
Macro['upper_case'].Value := ''
else
Macro['upper_case'].Value := 'upper';
Param['table_name'].Value := TableName;
Exec;
end;
end;
function TCcConfig.FindTable(TableName: String): Boolean;
begin
qTable := GetConnection.SelectQuery['TCcConfig_qTable'];
with qTable do begin
Close;
SQL.Text := 'select t.* from RPL$TABLES t where %upper_case(t.table_name) = %upper_case(:table_name)';
if GetConnection.DBAdaptor.QuoteMetadata then
Macro['upper_case'].Value := ''
else
Macro['upper_case'].Value := 'upper';
Param['table_name'].AsString := TableName;
Exec;
Result := (RecordCount > 0);
end;
if (ConfigName <> '') then begin
qTableConfig := GetConnection.SelectQuery['TCcConfig_qTableConfig'];
with qTableConfig do begin
Close;
SQL.Text := 'select tc.* from RPL$TABLES_CONFIG tc where %upper_case(tc.table_name) = %upper_case(:table_name) and upper(config_name) = ' + QuotedStr(ConfigName);
if GetConnection.DBAdaptor.QuoteMetadata then
Macro['upper_case'].Value := ''
else
Macro['upper_case'].Value := 'upper';
Param['table_name'].AsString := TableName;
Exec;
end;
end else
qTableConfig := nil;
end;
procedure TCcConfig.ScriptReady;
begin
if Assigned(FOnScriptReady) then
FOnScriptReady(Self);
end;
procedure TCcConfig.QueryExecuted(Sender: TObject);
var
qQuery: TCcQuery;
begin
if Assigned(FPrevExecQuery) then
FPrevExecQuery(Sender);
qQuery := (Sender as TCcQuery);
if qQuery.Name = 'qConfigQuery' then
begin
Query.Text := qQuery.SQL.Text;
Script.Add(Query.Text);
// Script.Add(FTerminator);
try
GetConnection.CommitRetaining;
if Assigned(FOnQueryReady) then
FOnQueryReady(Self, Query);
finally
Query.Clear;
end;
end;
end;
procedure TCcConfig.QueryReady;
begin
try
with GetConnection.UpdateQuery['TCcConfig_qQuery'] do begin
Close;
SQL.Text := Query.Text;
Exec;
end;
if Assigned(FOnQueryReady) then begin
// Query.Add(FTerminator);
FOnQueryReady(Self, Query);
end;
finally
Query.Clear;
end;
RefreshDisplay;
end;
(*
procedure TCcConfig.RemoveProcedure(ProcName:String);
begin
if not FindProcedure(ProcName) then Exit;
if GetConnection.DBAdaptor.ProcedureExists(qProcedure.Field['NEW_PROCEDURE_NAME'].AsString) then begin
Query.Add('DROP PROCEDURE ' + GetConnection.DBAdaptor.MetaQuote(qProcedure.Field['NEW_PROCEDURE_NAME'].AsString));
QueryReady;
Query.Add('update RPL$PROCEDURES set created = ''N'' where procedure_name = ' + QuotedStr(ProcName));
QueryReady;
ScriptReady;
end;
end;
*)
{procedure TCcConfig.GenerateProcedure(ProcName:String);
var
Params: TCcProcParams;
NewProcName: String;
begin
if not FindProcedure(ProcName) then Exit;
NewProcName := qProcedure.Field['NEW_PROCEDURE_NAME'].AsString;
if Trim(NewProcName) = '' then
raise Exception.Create('You must give the name for the new procedure you want to create!'#13#10'This new procedure will allow you to replicate all calls to the selected stored procedure.');
if qProcedure.Field['PRIORITY'].Value <= 0 then
raise Exception.Create('You must provide the priority of this procedure in the replication process.');
//Check if procedure already exists, and if so, remove it
RemoveProcedure(ProcName);
if not GetConnection.DBAdaptor.GenDeclared(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_' + copy(NewProcName, 1, 27))) then
GetConnection.DBAdaptor.DeclareGenerator(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_' + copy(NewProcName, 1, 27)));
Params := TCcProcParams.Create(Self);
GetProcParams(ProcName, Params);
GetConnection.DBAdaptor.GenerateProcedure(qProcedure, Params);
Query.Add('update RPL$PROCEDURES set created = ''Y'' where procedure_name = ' + QuotedStr(ProcName));
QueryReady;
ScriptReady;
end; }
{
//This procedure checks a trigger exists, and if so, removes it.
//RemoveTrigger is called internally by RemoveTriggers and GenerateTriggers,
//in order to avoid duplicate trigger creation or deletion.
//Parameters:
//cTriggerName: Name of the trigger to remove
procedure TCcConfig.RemoveTrigger(cTriggerName:String);
begin
if GetConnection.DBAdaptor.TriggerExists(cTriggerName) then begin
Query.Add('DROP TRIGGER ' + GetConnection.DBAdaptor.MetaQuote(cTriggerName));
QueryReady;
end;
end; }
procedure TCcConfig.GenerateTriggers(TableName: String);
var
cTableName, cQuotedTableName: String;
nNumberTriggers: Integer;
begin
cTableName := Trim(TableName);
cQuotedTableName := GetConnection.DBAdaptor.MetaQuote(cTableName);
if not FindTable(cTableName) then Exit;
//TriggerName := Trim(qTable.Field['TRIG_BASE_NAME'].AsString);
//If primary or unique key synchronization is configured, create the RPL$LOCAL field in the table
if ((Trim(qTable.Field['PRIMARY_KEY_SYNC'].AsString) <> '') or (Trim(qTable.Field['UNIQUE_KEY_SYNC'].AsString) <> ''))
and not GetConnection.DBAdaptor.FieldExists(cTableName, GetConnection.DBAdaptor.UnQuotedIdentifier('RPL$LOCAL')) then begin
Query.Add('alter table ' + cQuotedTableName + ' add RPL$LOCAL char(1) default ''N''');
QueryReady;
end;
if Assigned(qTableConfig) then
FConnection.DBAdaptor.RemoveTriggers(qTableConfig)
else
FConnection.DBAdaptor.RemoveTriggers(qTable);
// GetConnection.CommitRetaining;
nNumberTriggers := GetConnection.DBAdaptor.GenerateTriggers(qTable, qTableConfig, FailIfNoPK, TrackFieldChanges);
if (ConfigName <> '') then begin
Query.Add('update RPL$TABLES_CONFIG set created = ''Y'', number_of_triggers = ' + IntToStr(nNumberTriggers) + ' where table_name = ' + QuotedStr(cTableName) + ' and config_name = ' + QuotedStr(ConfigName));
QueryReady;
end;
Query.Add('update RPL$TABLES set created = ''Y'' where table_name = ' + QuotedStr(cTableName));
QueryReady;
GetConnection.CommitRetaining;
ScriptReady;
end;
function TCcConfig.GetGenerator(GenName: String;
Increment: Integer): String;
begin
Result := GetConnection.DBAdaptor.GetGenerator(Trim(GenName), Increment);
end;
function TCcConfig.GetVersion: String;
begin
Result := VersionNumber;
end;
(*
function TCcConfig.GetProcGenerator(TableName, ProcName: String; Params: TCcProcParams; OutputParam: String): String;
begin
if not FindTable(TableName) then Exit;
GetFieldNames(qTable.Field['TABLE_NAME'].AsString);
Result := GetConnection.DBAdaptor.GetProcGenerator(ProcName, Params, OutputParam, FFieldNames);
end;
*)
procedure TCcConfig.GetFieldNames(TableName: String);
begin
CheckConnected;
FFieldNames.Assign(FConnection.ListTableFields(TableName));
{ with GetConnection.MetaQuery[sqlTableFields] do begin
Close();
Param['table_name'].AsString := TableName;
Exec;
FFieldNames.Clear();
while (not Eof) do begin
FFieldNames.Add(Trim(Field['field_name'].AsString));
Next;
end;
end;}
end;
function TCcConfig.GetConnection: TCcConnection;
begin
if Assigned(FConnection) then
Result := FConnection
else
raise Exception.Create('Database connection not assigned!');
end;
(*procedure TCcConfig.GetProcParams(ProcName: String; Params: TCcProcParams; InputParam: Boolean = True);
begin
CheckConnected;
GetConnection.DBAdaptor.GetProcParams(ProcName, Params, InputParam);
end;
*)
constructor TCcConfig.Create(AOwner: TComponent);
begin
inherited;
FTables := TCcConfigTables.Create(Self);
FQueriesInitiated := False;
FNodes := TStringList.Create;
Query := TStringList.Create;
FFieldNames := TStringList.Create;
FScript := TStringList.Create;
// Terminator := '§';
end;
destructor TCcConfig.Destroy;
begin
Query.Free;
FNodes.Free;
FTables.Free;
FFieldNames.Free;
FScript.Free;
inherited;
end;
procedure TCcConfig.CheckConnected;
begin
if not GetConnection.Connected then
Connect;
end;
procedure TCcConfig.SetScript(const Value: TStrings);
begin
FScript.Assign(Value);
end;
{
procedure TCcConfig.LoadConfig;
begin
if not Assigned(ConfigStorage) then Exit;
if Assigned(Connection) then
Connection.Disconnect;
if DatabaseNode = dnLocal then
Connection := ConfigStorage.LocalDB.Connection
else
Connection := ConfigStorage.RemoteDB.Connection;
end;
}
procedure TCcConfig.Notification(AComponent: TComponent; Operation: TOperation);
begin
if Operation = opRemove then begin
if AComponent = FConnection then
SetConnection(nil);
end;
inherited;
end;
{
procedure TCcConfig.SetConfigStorage(const Value: TCcConfigStorage);
begin
inherited;
end;}
procedure TCcConfig.SetConnection(const Value: TCcConnection);
begin
if FConnection = Value then Exit;
if FConnection <> nil then begin
FConnection.OnDestroyQueries := FPrevDestroyQueries;
FConnection.OnQueryExecute := FPrevExecQuery;
FConnection.RemoveFreeNotification(Self);
end;
FConnection := Value;
if FConnection <> nil then begin
FPrevDestroyQueries := FConnection.OnDestroyQueries;
FConnection.OnDestroyQueries := DestroyQueries;
FPrevExecQuery := FConnection.OnQueryExecute;
FConnection.OnQueryExecute := QueryExecuted;
FConnection.FreeNotification(Self);
end
else
DestroyQueries(nil);
end;
constructor TCcProcParams.Create(AOwner: TComponent);
begin
inherited;
if not (csLoading in ComponentState) then
LoadFields;
end;
procedure TCcConfig.Connect;
begin
GetConnection.Connect;
RefreshDisplay;
CheckMetaData;
RefreshDisplay;
GetConnection.Disconnect;
GetConnection.Connect;
FillTables;
RefreshDisplay;
RefreshDisplay;
end;
procedure TCcConfig.DropTable(cTableName:String);
begin
if GetConnection.DBAdaptor.TableExists(GetConnection.DBAdaptor.UnQuotedIdentifier(cTableName)) then begin
Query.Clear;
Query.Add('DROP TABLE ' + cTableName);
QueryReady;
GetConnection.CommitRetaining;
end;
end;
procedure TCcConfig.DropAllTriggers;
var
slTriggers: TStringList;
I: Integer;
cTriggerName: string;
begin
slTriggers := GetConnection.ListTriggers;
for I:=0 to slTriggers.Count-1 do begin
cTriggerName := Trim(slTriggers[I]);
if Uppercase(Copy(cTriggerName, 1, 4)) = 'RPL$' then
GetConnection.ExecQuery('DROP TRIGGER ' + GetConnection.DBAdaptor.MetaQuote(cTriggerName));
end;
end;
procedure TCcConfig.ConnectAndRemoveConfig;
begin
try
GetConnection.Connect;
DropAllTriggers;
if GetConnection.DBAdaptor.TableExists(GetConnection.DBAdaptor.UnQuotedIdentifier('RPL$TABLES_CONFIG')) then
GetConnection.ExecQuery('UPDATE RPL$TABLES_CONFIG SET CREATED = ''N''');
GetConnection.CommitRetaining;
GetConnection.DBAdaptor.RemoveExtraCustomMetadata;
GetConnection.CommitRetaining;
GetConnection.DBAdaptor.DropProcedures;
GetConnection.CommitRetaining;
DropTable('RPL$TABLES');
DropTable('RPL$TABLES_CONFIG');
DropTable('RPL$ERRORS');
DropTable('RPL$PROCEDURES');
DropTable('RPL$LOG');
DropTable('RPL$CONFLICTS');
DropTable('RPL$TRACE');
DropTable('RPL$USERS');
DropTable('RPL$LOG_VALUES');
GetConnection.DBAdaptor.DropGenerator(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_RPL$LOG'));
GetConnection.DBAdaptor.DropGenerator(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_RPL$LOG_VALUES'));
GetConnection.DBAdaptor.DropGenerator(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_RPL$CONFLICTS'));
GetConnection.DBAdaptor.DropGenerator(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_RPL$ERRORS'));
GetConnection.DBAdaptor.DropGenerator(GetConnection.DBAdaptor.UnQuotedIdentifier('GEN_RPL$TRACE'));
GetConnection.DBAdaptor.RemoveCustomMetadata;
GetConnection.CommitRetaining;
finally
if GetConnection.InTransaction then
GetConnection.RollbackRetaining;
end;
end;
procedure TCcConfig.Disconnect;
begin
if Assigned(FConnection) then
GetConnection.Disconnect;
end;
procedure TCcConfig.RefreshDisplay;
begin
if Assigned(FOnProgress) then
FOnProgress(Self);
end;
(*
procedure TCcConfig.FillProcedures;
var
// qProcedures: TCcQuery;
slProcs, slRPLProcs: TStringList;
I: Integer;
begin
if GetConnection.Connected then
begin
slProcs := GetConnection.ListProcedures;
slRPLProcs := TStringList.Create;
try
slRPLProcs.CommaText := ListRPLProcedures;
for I:=0 to slProcs.Count-1 do begin
if slRPLProcs.IndexOf(slProcs[i]) = -1 then
with GetConnection.UpdateQuery['TCcConfig_qFillProcedures'] do begin
Close;
if SQL.Text = '' then
SQL.Text := 'insert into RPL$PROCEDURES (procedure_name, new_procedure_name, priority) values (:procedure_name, :new_procedure_name, :priority)';
Param['PROCEDURE_NAME'].AsString := Copy(Trim(slProcs[i]), 1, 50);
Param['NEW_PROCEDURE_NAME'].AsString := Copy('RPL$' + slProcs[i], 1, 29);
Param['PRIORITY'].Value := 0;
Exec;
end;
RefreshDisplay;
end;
finally
slRPLProcs.Free;
end;
{ qProcedures := GetConnection.MetaQuery[sqlProcedures];
qProcedures.Close;
try
qProcedures.Exec;
except
Exit;
end;
while not qProcedures.Eof do
begin
if not FindProcedure(qProcedures.Field['PROCEDURE_NAME'].AsString) then
with GetConnection.Query['TCcConfig_qFillProcedures'] do begin
Close;
if SQL.Text = '' then
SQL.Text := 'insert into RPL$PROCEDURES (procedure_name, new_procedure_name, priority) values (:procedure_name, :new_procedure_name, :priority)';
Param['PROCEDURE_NAME'].AsString := Trim(qProcedures.Field['PROCEDURE_NAME'].AsString);
Param['NEW_PROCEDURE_NAME'].AsString := Copy('RPL$' + Param['PROCEDURE_NAME'].AsString, 1, 29);
Param['PRIORITY'].Value := 0;
Exec;
end;
qProcedures.Next;
end;}
GetConnection.CommitRetaining;
end;
end; *)
function TCcConfig.ListRPLTables: String;
begin
Result := '';
qTable := GetConnection.SelectQuery['TCcConfig_qRPLTables'];
with qTable do begin
Close;
SQL.Text := 'select t.TABLE_NAME from RPL$TABLES t';
Exec;
while not Eof do begin
if Result <> '' then
Result := Result + ',';
Result := Result + Field['TABLE_NAME'].AsString;
Next;
end;
end;
end;
{function TCcConfig.ListRPLProcedures: String;
begin
qTable := GetConnection.SelectQuery['TCcConfig_qRPLProcedures'];
with qTable do begin
Close;
SQL.Text := 'select p.procedure_name from RPL$PROCEDURES P';
Exec;
while not Eof do begin
if Result <> '' then
Result := Result + ',';
Result := Result + Field['procedure_name'].AsString;
Next;
end;
end;
end;
}