-
Notifications
You must be signed in to change notification settings - Fork 2
/
gendbcx.prg
2092 lines (1928 loc) · 93.1 KB
/
gendbcx.prg
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
** For best results, view this file with: Courier New / Regular / 10
** TabWidth set to: 4
*******************************************************************************
** Program Name : GenDBCX.PRG (nee GENDBC.PRG)
** Version : 1.13
** Creation Dates: 94.12.01 (GENDBC.PRG) 2022.06.12 (GenDBCX.PRG)
**
** Purpose:
** To take an existing FoxPro 3.0/5.0 database and generate an output
** program that can be used to "re-create" that database.
**
** Parameters:
** tcOutFile - The name of the output file. This can contain path
** information and an extension.
** If no extension is supplied, one will be provided.
**
** tcClassName - The name of the class to create. If not supplied,
** the default 'GenDBCX' will be used.
**
** tlSeparateSPObjCode
** - A flag that tells the program whether to append the
** Stored Procedures object code to the .krt file that
** holds the SP source code (the default), or write it
** to a separate .kro file.
**
** Modification History:
** (KRT = Somebody at MS; SEA = Steve Arnott, SEA Drive;
** SAS = Steve Sawyer; DH Doug Hennig; SF Lutz Scheffler)
**
** GENDBC:
** 1994.12.01 KRT Created program, runs on Build 329 of VFP
** 1994.12.02 KRT Added GetView function and cleaned up all code
** 1994.12.05 KRT Modified some areas and verified run on Build 335
** 1994.12.06 KRT Added things for international conversion
** 1994.12.08 KRT Added function ADBOBJECTS() to code
** 1994.12.12 KRT Added commands COPY PROCEDURES TO
** 1995.01.04 KRT Added connection properties
** 1995.01.05 KRT Added support for long filenames - thierryp
** 1995.01.05 KRT Added support for RI
** 1995.01.06 KRT Added support for MS-DOS short filenames (NAME clause)
** 1995.01.08 KRT Added status bar line
** 1995.01.26 KRT Fixed a few file bugs and localization bug
** 1995.02.19 KRT Took advantage of AFIELDS() command
** 1995.02.22 KRT Fixed AUSED() bug
** 1995.03.01 KRT Removed ON ERROR problem
** 1995.03.20 KRT Fixed "Exclusive" Error / Procedures created before
** Tables
** 1995.03.22 KRT Allowed user to open database if one is not set current
** / Set SAFETY OFF in resulting code to prevent errors
** when validating rules/triggers that don't exist
** 1995.04.07 KRT Put any database procedures into a seperate file to
** prevent "Program Too Large" errors
** 1995.04.20 KRT Only change SAFETY when appending procedures
**
** 3.0b - Changes/Fixes
** 1995.09.15 KRT Changed ADBOBJECTS() to DBF() in GETTABLE procedure.
** This allows for "real" table names instead of alias'
** 1995.09.15 KRT Take into account CR+LF in comments for fields
** 1995.09.15 KRT Store Source and Object code into external file
** So the code can be executed from the run-time version
** 1995.10.23 KRT Changed DBF(cTableName ) to DBF(ALIAS() ) because
** VFP will automatically add underscores where spaces
** should be and I need to know what it did to the alias
** [Regress]
** 1995.10.25 KRT Added OVERWRITE to append memo's [Regress]
** 1995.10.25 KRT Added support for CR+LF in comments for Table
** 1995.10.25 KRT Close all tables in generated code before adding
** Source and Object code from external file[Regress]
** 1995.10.25 KRT Added warning about filter indices on Unique and
** Candidate keys (Not supported via language )
** 1995.12.20 KRT Better lookup for adding RI information to database
** 1995.12.20 KRT Added support for filter expressions on CANDIDATE keys
**
** 5.0 - Changes/Fixes
** 1996.03.27 KRT Added "exact match" support for Locate command
** 1996.04.12 KRT Added new properties for Views, Fields and Connections
** 1996.05.14 KRT Adjusted for some logical properties return spaces
** 1996.05.16 KRT Added even more new properties for Views
** 1996.05.16 KRT Add M. in front of all memory variables to prevent
** confusion with table fields and names
** 1996.06.01 KRT Added support for Collate sequence on index files
** 1996.06.26 KRT Added support for ParameterList in Views
** 1996.07.19 KRT Added support for Offline Views
** 1996.08.07 KRT Added support for comments and default values in views
**
** GenDBCX:
** 1997.02.01 SEA Changed "Get Relations" code to build output in a
** memory variable to avoid creating 100+MB memo files
** when working with large databases
** 1997.02.24 SEA Added code to avoid the dreaded "Program too large"
** error message by making (almost) everything in the
** database re-creation program a procedure
** 1997.02.24 SEA Replaced usage of "Program" memo field with writing
** directly to a temporary file to prevent "Out of disk
** space" error caused by memo file bloat
** 1997.02.25 SEA Changed format of CREATE TABLE output code to improve
** (I hope) its readability
** 1997.03.23 SEA Added code to create the DBC regeneration program as a
** class, so that you can recreate specific tables, views
** and connections by calling the output class' methods.
** To recreate the entire database, you either run the
** program, or call the GenerateAll method.
** 1997.03.24 SEA Added code to create a class Init routine that checks
** for the existence of the target database container and,
** if it finds it, loads the names of its tables into an
** array that the table creation methods search. If the
** table name exists in the dbc, it's deleted and recreated.
** 1997.04.04 SEA Added code to allow user to pass the name of the method
** to call as a parameter, rather than forcing the user to
** instantiate a GenDBCX object.
** 1997.05.01 SAS Added code to recreate the dbc in two steps, with a hook
** in the middle that allows the user to append existing data
** to the new database, or do anything else that the user might
** require upon creation of the new database.
** 1997.07.29 SEA Fixed some minor formatting errors.
** Fixed the RI "error" message line so that the number of the
** line in the regeneration program is displayed, rather than
** the number of the line in this program.
** 1997.09.05 SEA Fixed procedure GetView to handle 5.0-specific properties
** when running under VFP 3.0. Thanks to Nancy Folsom for
** catching this!
** 1997.09.05 SEA Added code to handle table, view and connection names that
** contain spaces.
** 1997.10.01 SEA Added parameter tlSeparateSPObjCode to allow the user to
** specify that the Stored Procedures object code is to be
** output to a separate .kro file, rather than appended to
** the .krt file that holds the SP source code. Apparently,
** the inclusion of object code in the .krt prevents the file
** from being compared to a previous version in Visual
** SourceSafe projects.
** 1997.10.15 SEA Added code to allow the user to sort the arrays that hold
** the table, view, connection and relation names alphabetically,
** so that they appear in alphabetical order in the output code.
** 1997.11.01 SEA Added ability to control the appearance of the output code
** through the use of constants that control code indentation
** and the separation of field names from their data types
** in CREATE TABLE commands.
** 1997.11.01 SEA Added code in procedure GetTable to handle "Program line too
** long" error which occurs when a table has a large number
** of fields. Thanks to Ken Lee for reporting this!
** 1997.11.07 SEA Added code to place each type of database "object" in a
** separate procedure/method, and added a constant to allow
** the user to specify that indices be created in a pass
** separate from the one that creates the table.
** 1997.12.07 SEA Added code to initialize local variables for each parameter
** in a parameterized view. Note that this only works if you
** have added each parameter to the view's parameters list,
** either through the View Designer (Query/View Parameters) or
** with DBSetProp(). Thanks to Todd Arnold for telling me how
** to create the parameters list!
** 1997.12.07 SEA Added code to display a dialog, warning the user that
** his/her dbc and/or tables are about to be overwritten, and
** requesting confirmation to continue.
** 1997.12.14 SEA Set EXACT ON in generated program so that the ASCAN() in
** RemoveTable() won't find "TABLE10" when it's looking for
** "TABLE1". Thanks to Jeff Clauss and Ken Lee for catching
** this!
** 1997.12.29 SEA Added code to break up the RI regeneration code into
** multiple procedures when it exceeds 64K. Thanks to Arnon
** Gal-Oz for letting me know that it was time to do this!
** 1998.07.20 SEA Fixed bug that created a procedure without a name if the
** database has no tables.
** 1998.07.20 SEA UPPER()'d and ALLTRIM()'d the dbc's ObjectName field when
** searching for relations in procedure GetRI. Thanks to
** Patrick Godfrey for catching this!
** 2022.06.11 SF Included changes by DH from GenDBC.prg for additional file types
** Modified:
** PROCEDURE GetTable
** 2004.03.19 DH Added support for Blob, VarChar, and VarBinary fields
** 2004.05.05 DH Added support for binary indexe
** PROCEDURE GetView
** 2004.04.14 DH Added support for AllowSimultaneousFetch,
** RuleExpression, and RuleText properties
** 2002.06.28 DH Removed duplicate output of FetchSize property
** 2004.09.22 DH Handled case where UpdateName has quotes (e.g. with
** calculated fields like "Test" AS SOMEFIELD)
** PROCEDURE GetConn
** 2004.03.19 DH Added support for PacketSize
** added code to support WaitTime (it was retrieved but not written)
** /SF
** 2022.06.11 SF PROCEDURE GetRI
** The code to rewrite RIINFO might find the wrong record
*******************************************************************************
LPARAMETERS tcOutFile, tcClassName, tlSeparateSPObjCode
#IF FILE("custproc.h")
#INCLUDE custproc.h
#ENDIF
PRIVATE ALL EXCEPT g_*
**************************************************************************
** Constants
**************************************************************************
#DEFINE CRLF CHR(13) + CHR(10)
#DEFINE TAB CHR(9)
*! SEA: CREATE TABLE command can exceed max command line length when
*! table has 180+ fields in 5.0, or 45+ fields in 3.0. I'll try to
*! alleviate the problem by allowing you to strip out the default
*! formatting tabs and spaces. If that's not enough, procedure
*! GetTable will truncate the CREATE TABLE command and add as many
*! ALTER TABLE commands as necessary to add the remaining fields.
#IFNDEF GDX_INDENT
#DEFINE GDX_INDENT TAB
#ENDIF
#IFNDEF GDX_PRCMARGIN
#DEFINE GDX_PRCMARGIN GDX_INDENT
#ENDIF
#IFNDEF GDX_CMDMARGIN
#DEFINE GDX_CMDMARGIN GDX_PRCMARGIN + GDX_INDENT
#ENDIF
#IFNDEF GDX_FLDNAMEPAD
#DEFINE GDX_FLDNAMEPAD 20
#ENDIF
#IFNDEF ALERT_USING_ALTER_TABLE
#DEFINE ALERT_USING_ALTER_TABLE .T.
#ENDIF
#IFNDEF ALTER_TABLE_NOVALIDATE
#DEFINE ALTER_TABLE_NOVALIDATE .F.
#ENDIF
*! SEA: If you want to separate the index-creation routine from that which
*! creates the related table, then #DEFINE MAKE_INDEX_PASS .T. in the
*! CustProc.h header file.
#IFNDEF MAKE_INDEX_PASS
#DEFINE MAKE_INDEX_PASS .F.
#ENDIF
*! SEA: If you want to add field-level properties, such as CHECK, ERROR
*! and DEFAULT separately from the code that creates the table, then
*! #DEFINE CREATE_TABLE_SET_PROPS .F. in the CustProc.h header file.
#IFNDEF CREATE_TABLE_SET_PROPS
#DEFINE CREATE_TABLE_SET_PROPS .T.
#ENDIF
*! SEA: If you want your tables, views, connections and relations to be
*! created in alphabetical order (rather than the order that they
*! were created in the database container), then #DEFINE
*! GDX_SORT_OUTPUT .T. in the CustProc.h header file.
#IFNDEF GDX_SORT_OUTPUT
#DEFINE GDX_SORT_OUTPUT .F.
#ENDIF
**************************************************************************
** Error Messages
**************************************************************************
#DEFINE NO_DATABASE_IN_USE_LOC "No Database is in use. " + ;
"This program must have a database " + ;
"available."
#DEFINE INVALID_PARAMETERS_LOC "Invalid Parameters..." + CRLF + ;
"An output file must be specified." + CRLF +;
'ie: DO GENDBCX WITH "filename.prg"'
#DEFINE INVALID_DESTINATION_LOC "Invalid Destination File "
#DEFINE NO_TEMP_FILE_LOC "Could not create temporary file: "
#DEFINE NO_OUTPUT_WRITTEN_LOC "Could not create or write to output file"
#DEFINE ERROR_TITLE_LOC "Aborting GenDBCX..."
#DEFINE UNRECOVERABLE_LOC "Unrecoverable Error: "
#DEFINE AT_LINE_LOC " At Line: "
#DEFINE NO_FIND_LOC "Could not set RI Information"
#DEFINE NO_FILE_FOUND_LOC "Warning! No Procedure File Found!"
#DEFINE GETFILE_GEN_LOC "Generate..."
#DEFINE NOT_SUPPORTED_LOC "Filters on PRIMARY keys are not supported at this time. " + ;
"A comment will be added to your output file specifying the filters."
#DEFINE NS_COMMENT_LOC "****** These filters need to be added manually ******"
#DEFINE WARNING_TITLE_LOC "GenDBCX Warning..."
#DEFINE USING_ALTER_TABLE_LOC "CREATE TABLE command line length exceeded," + CRLF + ;
"using ALTER TABLE to add remaining fields for table. " + CRLF + ;
"This is an information "
#DEFINE WARNING_OVERWRITING_LOC "Warning! This program will overwrite your existing database and tables! Continue?"
#DEFINE PROGRAM_CANCELLED_LOC "Program cancelled"
**************************************************************************
** Comments And Other Information
**************************************************************************
#DEFINE BEGIN_RELATION_LOC "Relations Setup"
#DEFINE BEGIN_TABLE_LOC "Table setup for "
#DEFINE BEGIN_INDEX_LOC "Create each index for "
#DEFINE BEGIN_PROP_LOC "Change properties for "
#DEFINE BEGIN_VIEW_LOC "View setup for "
#DEFINE BEGIN_PROC_LOC "Procedure Re-Creation"
#DEFINE BEGIN_CONNECTIONS_LOC "Connection Definition"
#DEFINE BEGIN_RI_LOC "Referential Integrity Setup"
#DEFINE OPEN_DATABASE_LOC "Select Database..."
#DEFINE PACK_DATABASE_LOC "Pack the Database"
#DEFINE SAVE_PRG_NAME_LOC "Enter output program name..."
#DEFINE NO_MODIFY_LOC "*** WARNING *** DO NOT MODIFY THIS FILE IN ANY WAY! *** WARNING ***"
#DEFINE TABLE_NAME_LOC "* Table Name: "
#DEFINE PRIMARY_KEY_LOC "* Primary Key: "
#DEFINE FILTER_EXP_LOC "* Filter Expression: "
#DEFINE HEADING_LINE "* *********************************************************************"
#DEFINE HEADING_1_LOC HEADING_LINE + CRLF + ;
"* *" + CRLF
#DEFINE HEADING_2_LOC "* *" + CRLF + ;
HEADING_LINE + CRLF + ;
"* *" + CRLF + ;
"* * Description:" + CRLF + ;
"* * This program was automatically generated by GenDBCX Version 1.13," + CRLF + ;
"* * a modified version of Microsoft's utility GenDBC Version 2.26.67." + CRLF + ;
"* * with changes Made to this version on VFP9" + CRLF + ;
"* *" + CRLF + ;
HEADING_LINE + CRLF
#DEFINE CREATE_DBC_LOC "*-- Create the Database Container"
#DEFINE CREATE_TABLES_LOC "*-- Create Tables"
#DEFINE CREATE_INDICES_LOC "*-- Create Indices"
#DEFINE CREATE_VIEWS_LOC "*-- Create Views"
#DEFINE CREATE_CONNECTIONS_LOC "*-- Create Connections"
#DEFINE CALL_CUSTOM_LOC "*-- Call custom method"
#DEFINE FINISH_TABLES_LOC "*-- Finish Tables (add rules/triggers/properties)"
**************************************************************************
** Public Variables
**************************************************************************
IF SET("TALK") = "ON" && To restore SET TALK after use
SET TALK OFF && -- Have to do it this way so
g_cSetTalk = "ON" && -- nothing gets on screen
ELSE
g_cSetTalk = "OFF"
ENDIF
g_cFullPath = SET("FULLPATH") && To restore old FULLPATH setting
g_cOnError = ON("ERROR") && To restore old ON ERROR condition
g_cSetDeleted = SET("DELETED") && To restore SET DELETED later
g_cSetStatusBar = SET("STATUS BAR") && To restore STATUS bar
g_cStatusText = SYS(2001, "MESSAGE", 1) && To restore text that may be on it
g_nMax = 7 && For status line information
g_nCurrentStat = 1 && For status line information
g_cFilterExp = "" && For Non-Supported Filter Info
g_cSetSafety = SET("SAFETY") && To restore SAFETY setting
g_cSetExact = SET("EXACT") && To restore EXACT setting
**************************************************************************
*! Our generic error handling routine
ON ERROR DO GenDBC_Error WITH MESSAGE(), LINENO()
DEACTIVATE WINDOW "Project Manager"
*! Begin program body
SET DELETED ON
SET FULLPATH ON
IF m.g_cSetStatusBar = "OFF"
SET STATUS BAR ON
ENDIF
SET SAFETY OFF
SET EXACT ON
*! Hook for a custom process to run after the DBC and table structures are created
lcHookStr = CALL_CUSTOM_LOC + CRLF + GDX_CMDMARGIN + "=this.CustomProcess()"
*! Make sure a database is open
IF EMPTY(DBC())
g_cFullDatabase = GETFILE("DBC", OPEN_DATABASE_LOC, GETFILE_GEN_LOC, 0)
IF EMPTY(m.g_cFullDatabase)
=FatalAlert(NO_DATABASE_IN_USE_LOC, .F.)
ENDIF
OPEN DATABASE (m.g_cFullDatabase)
ENDIF
*! Set global variable to the database name and format it
g_cDatabase = FULLPATH(DBC())
IF RAT("\", m.g_cDatabase) > 0
m.g_cDatabase = SUBSTR(m.g_cDatabase, RAT("\", m.g_cDatabase) + 1)
ENDIF
*! Get the fullpath of database
g_cFullDatabase = DBC()
*! Check for valid parameters
IF TYPE('m.tcOutFile') != "C" or EMPTY(m.tcOutFile)
tcOutFile = ""
tcOutFile = PUTFILE(SAVE_PRG_NAME_LOC, SUBSTR(m.g_cDatabase, 1, RAT(".", m.g_cDatabase)) + "PRG", "PRG")
IF EMPTY(m.tcOutFile)
=FatalAlert(INVALID_PARAMETERS_LOC, .F.)
ENDIF
ENDIF
*! Check for proper extensions or add one if none specified
IF RAT(".PRG", m.tcOutFile) = 0 AND RAT(".", m.tcOutFile) = 0
tcOutFile = m.tcOutFile + ".PRG"
ENDIF
*! Make sure the output file is valid
hFile = FCREATE(m.tcOutFile)
IF m.hFile <= 0
=FatalAlert(INVALID_DESTINATION_LOC + m.tcOutFile, .F.)
ENDIF
=FCLOSE(m.hFile)
ERASE (m.tcOutFile)
*! Remember all our tables that are open for this database
g_nTotal_Tables_Used = AUSED(g_aAlias_Used)
IF m.g_nTotal_Tables_Used > 0
DIMENSION g_aTables_Used[m.g_nTotal_Tables_Used]
*! Get real names of tables opened
FOR nLoop = 1 TO m.g_nTotal_Tables_Used
g_aTables_Used[m.nLoop] = DBF(g_aAlias_Used[m.nLoop, 1])
ENDFOR
ENDIF
*! Get number of tables contained in database
nTotal_Tables = ADBOBJECTS(aAll_Tables, "Table")
g_nMax = m.g_nMax + m.nTotal_Tables
=Stat_Message()
*! Get number of views contained in database
nTotal_Views = ADBOBJECTS(aAll_Views, "View")
g_nMax = m.g_nMax + m.nTotal_Views
=Stat_Message()
*! Get number of connections contained in database
nTotal_Connections = ADBOBJECTS(aAll_Connections, "Connection")
g_nMax = m.g_nMax + m.nTotal_Connections
=Stat_Message()
*! Get number of relations contained in database
nTotal_Relations = ADBOBJECTS(aAll_Relations, "Relation")
*! SEA: We process relations so fast now, it's better not to include them
*! in the "status" line, as they distort the progress indicator
* m.g_nMax = m.g_nMax + m.nTotal_Relations
=Stat_Message()
CLOSE DATABASES ALL
**************************
*** Get Stored Procedures
**************************
*! Create an output file that will be appended to the database
*! as procedures
cFile = UPPER(SUBSTR(m.tcOutFile, 1, AT(".", m.tcOutFile))) + "krt"
cDPTemp = "GenDBC.$$$"
cSPTemp = "GenDBC.k$$"
IF tlSeparateSPObjCode
cSPObjTemp = "GenDBC.o$$"
cSPObjFile = UPPER(SUBSTR(m.tcOutFile, 1, AT(".", m.tcOutFile))) + "kro"
ENDIF
*! Place Header Information For Source/Object
hFile = FCREATE(m.cSPTemp)
IF m.hFile <= 0
=FCLOSE(m.hFile)
=FatalAlert(NO_OUTPUT_WRITTEN_LOC, .T.)
ENDIF
=FPUTS(m.hFile, NO_MODIFY_LOC)
=FCLOSE(m.hFile)
*! Now we are going to copy the object and source code
*! for the stored procedures
OPEN DATABASE (m.g_cFullDatabase) EXCLUSIVE
COMPILE DATABASE (m.g_cFullDatabase)
CLOSE DATABASES ALL
USE (m.g_cFullDatabase) SHARED
LOCATE FOR Objectname = 'StoredProceduresSource'
IF FOUND()
COPY MEMO Code TO (m.cSPTemp) ADDITIVE
ENDIF
=ADIR(aTemp, m.cSPTemp)
nSourceSize = m.aTemp[1,2] - LEN(NO_MODIFY_LOC)
LOCATE FOR Objectname = 'StoredProceduresObject'
IF FOUND()
IF tlSeparateSPObjCode
COPY MEMO Code TO (m.cSPObjTemp)
ELSE
COPY MEMO Code TO (m.cSPTemp) ADDITIVE
ENDIF
ENDIF
USE
*! Open the database again
OPEN DATABASE (m.g_cFullDatabase) EXCLUSIVE
hOutFile = FCREATE(cDPTemp)
IF m.hOutFile <= 0
=FCLOSE(m.hFile)
=FatalAlert(NO_OUTPUT_WRITTEN_LOC, .T.)
ENDIF
*******************************************************************************
** Create a class that holds all of the database regeneration code as
** public methods that can be called by anyone who can create a reference
** to the class.
**
** The body of the program now consists of two lines: 1) the creation of
** the GenDBCX class object, and 2) a call to a "summary" method that
** calls all of the methods that create the database, tables, views and
** connections, and set relations and referential integrity.
**
** Using this approach, you can now DO the program to recreate the entire
** database, or you can create an object reference to the GenDBCX class,
** and recreate tables, views and connections by appending the table/view
** name to its related prefix ("tb" for tables, "vw" for views, "cn" for
** connections), relations by calling the SetRelations method, and RI by
** calling the SetRI method.
**
** Additionally, if you want to execute the code of a method without
** creating a GenDBCX object, you can call the program with the name of
** a single method to execute.
*******************************************************************************
*! Write the program heading
=WriteFile(m.hOutFile, ;
HEADING_1_LOC + "* * " + DTOC(DATE()) +;
SPACE(25 - LEN(m.g_cDatabase) / 2) + ;
m.g_cDatabase + SPACE(25 - LEN(m.g_cDatabase) / 2) +;
TIME() + CRLF + HEADING_2_LOC + CRLF + ;
IIF(!EMPTY(m.g_cFilterExp), NS_COMMENT_LOC + m.g_cFilterExp + ;
CRLF + REPLICATE("*", 52) + CRLF, ""))
*! Write the body of the program
lcObjectName = "oDBC"
lcClassName = IIF(TYPE("m.tcClassName")="C" AND !EMPTY(m.tcClassName),tcClassName,"GenDBCX")
lcGenAllProc = "GenerateAll"
=WriteFile(m.hOutFile, ;
"LPARAMETERS tcMethod" + CRLF + ;
"LOCAL " + lcObjectName + CRLF + ;
lcObjectName + " = CreateObject('" + m.lcClassName + "')" + CRLF + ;
"IF TYPE('" + lcObjectName + "') != 'O'" + CRLF + ;
GDX_INDENT + "RETURN" + CRLF + ;
"ENDIF" + CRLF + ;
"cExactSet = SET('EXACT')" + CRLF + ;
"SET EXACT ON" + CRLF + ;
"IF EMPTY(tcMethod)" + CRLF + ;
GDX_INDENT + "=" + lcObjectName + "." + m.lcGenAllProc + "()" + CRLF + ;
"ELSE" + CRLF + ;
GDX_INDENT + "=EVALUATE('" + lcObjectName + ".' + tcMethod)" + CRLF + ;
"ENDIF" + CRLF + ;
"IF cExactSet = 'OFF'" + CRLF + ;
GDX_INDENT + "SET EXACT OFF" + CRLF + ;
"ENDIF" + CRLF + CRLF ;
)
*! Begin defining the class
=WriteFile(m.hOutFile, ;
"DEFINE CLASS " + m.lcClassName + " AS Custom" + CRLF + ;
GDX_PRCMARGIN + "PROTECTED aTables[1]" + CRLF + ;
GDX_PRCMARGIN + "PROTECTED nTables" + CRLF ;
)
*! Write the CustomProcess method
#IFDEF CUSTOM_METHOD_LOC
=WriteFile(m.hOutFile, ;
GDX_PRCMARGIN + "PROCEDURE CustomProcess" + CRLF + ;
GDX_CMDMARGIN + CUSTOM_METHOD_LOC + ;
GDX_PRCMARGIN + "ENDPROC" + CRLF ;
)
#ELSE
=WriteFile(m.hOutFile, ;
GDX_PRCMARGIN + "PROCEDURE CustomProcess" + CRLF + ;
GDX_CMDMARGIN + "*-- Your code goes here" + CRLF + ;
GDX_PRCMARGIN + "ENDPROC" + CRLF ;
)
#ENDIF
*! Write the Init method, storing the names of any tables in an existing dbc
*! into the class' "table names" array
=WriteFile(m.hOutFile, ;
GDX_PRCMARGIN + "PROCEDURE Init" + CRLF + ;
GDX_CMDMARGIN + "IF FILE('" + m.g_cDatabase + "')" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "OPEN DATABASE " + m.g_cDatabase + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "this.nTables = ADBOBJECTS(this.aTables, 'Table')" + CRLF + ;
GDX_CMDMARGIN + "ENDIF" + CRLF + ;
GDX_CMDMARGIN + "LOCAL ARRAY xx[1]" + CRLF + ;
GDX_CMDMARGIN + "IF ADIR(xx,'*.DBC') > 0 OR ADIR(xx,'*.DBF') > 0" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "IF MessageBox('" + WARNING_OVERWRITING_LOC + "', " + ;
LTRIM(STR(48+4+256)) + ;
", '" + WARNING_TITLE_LOC + "') != 6" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "RETURN .F." + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "ENDIF" + CRLF + ;
GDX_CMDMARGIN + "ENDIF" + CRLF + ;
GDX_PRCMARGIN + "ENDPROC" + CRLF ;
)
*! Create the call to the DBC recreation method, to be inserted into the
*! "summary" method below
lcCreateDBCProc = "CreateDBC"
lcCDStr = "=this." + m.lcCreateDBCProc + "()"
*! Begin the database container recreation method
=WriteFile(m.hOutFile, ;
GDX_PRCMARGIN + "PROCEDURE " + m.lcCreateDBCProc + CRLF + ;
GDX_CMDMARGIN + CREATE_DBC_LOC + CRLF + ;
GDX_CMDMARGIN + "CLOSE DATA ALL" + CRLF + ;
GDX_CMDMARGIN + "CREATE DATABASE '" + m.g_cDatabase + "'" ;
)
*! If the database contained any stored procedures (i.e. we wrote something
*! other than the NO_MODIFY_LOC constant to the .k$$ file), then output
*! the code required to recreate the procedure file.
=ADIR(aTemp, m.cSPTemp)
IF m.aTemp[1, 2] > LEN(NO_MODIFY_LOC) + LEN(CRLF)
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "********* " + BEGIN_PROC_LOC + " *********")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "IF !FILE([" + SUBSTR(m.cFile, RAT("\", m.cFile) + 1) + "])")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "? [" + NO_FILE_FOUND_LOC + "]")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "ELSE")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "CLOSE DATABASE")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "USE '" + m.g_cDatabase + "'")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "g_SetSafety = SET('SAFETY')")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "SET SAFETY OFF")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "LOCATE FOR Objectname = 'StoredProceduresSource'")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "IF FOUND()")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "APPEND MEMO Code FROM [" + SUBSTR(m.cFile, RAT("\", m.cFile) + 1) + "] OVERWRITE")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "REPLACE Code WITH SUBSTR(Code, " + ALLTRIM(STR(LEN(NO_MODIFY_LOC) + 3)) + ", " + ALLTRIM(STR(m.nSourceSize - 2)) + ")")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "ENDIF")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "LOCATE FOR Objectname = 'StoredProceduresObject'")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "IF FOUND()")
IF tlSeparateSPObjCode
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "APPEND MEMO Code FROM [" + SUBSTR(m.cSPObjFile, RAT("\", m.cSPObjFile) + 1) + "] OVERWRITE")
ELSE
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "APPEND MEMO Code FROM [" + SUBSTR(m.cFile, RAT("\", m.cFile) + 1) + "] OVERWRITE")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "REPLACE Code WITH SUBSTR(Code, " + ALLTRIM(STR(LEN(NO_MODIFY_LOC) + m.nSourceSize + 1)) + ")")
ENDIF
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "ENDIF")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "IF UPPER(g_SetSafety) = 'ON'")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "SET SAFETY ON")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "ENDIF")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "USE")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "OPEN DATABASE [" + m.g_cDatabase + "]")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "ENDIF")
=WriteFile(m.hOutFile, "")
ELSE
ERASE (m.cSPTemp)
ENDIF
*! Close the database container recreation method
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC" + CRLF)
=Stat_Message()
**************************
*** Get Tables
**************************
lcCTProc = ""
lcCRTPProc = ""
lcCIDXProc = ""
lcCrtTables = ""
lcCrtIndices = ""
lcCrtRTPs = ""
lcCTStr = ""
lcCRTPStr = ""
lcCIDXStr = ""
lcProcPrefix = "tb"
IF m.nTotal_Tables > 0
lcCTProc = "CreateTables"
lcCIDXProc = "CreateIndices"
lcCRTPProc = "RulesTriggersProperties"
lcCrtTables = "=this." + lcCTProc + "()"
lcCrtIndices = "=this." + lcCIDXProc + "()"
lcCrtRTPs = "=this." + lcCRTPProc + "()"
lcCTStr = GDX_CMDMARGIN + CREATE_TABLES_LOC
lcCIDXStr = GDX_CMDMARGIN + CREATE_INDICES_LOC
lcCRTPStr = GDX_CMDMARGIN + FINISH_TABLES_LOC
#IF GDX_SORT_OUTPUT
=ASORT(aAll_Tables,1)
#ENDIF
FOR m.nLoop = 1 TO m.nTotal_Tables
lcProcName = m.lcProcPrefix + ALLTRIM(CHRTRAN(aAll_Tables[m.nLoop]," ","_"))
DO GetTable WITH ALLTRIM(aAll_Tables(m.nLoop)), m.hOutFile, m.lcProcName
lcCTStr = lcCTStr + CRLF + GDX_CMDMARGIN + "=this." + m.lcProcName + "(1)"
lcCRTPStr = lcCRTPStr + CRLF + GDX_CMDMARGIN + "=this." + m.lcProcName + "(2)"
lcCIDXStr = lcCIDXStr + CRLF + GDX_CMDMARGIN + "=this." + m.lcProcName + "(3)"
=Stat_Message()
ENDFOR
ENDIF
**************************
*** Get Connections
**************************
lcCCProc = ""
lcCrtConns = ""
lcCCStr = ""
lcProcPrefix = "cn"
IF m.nTotal_Connections > 0
lcCCProc = "CreateConnections"
lcCrtConns = "=this." + lcCCProc + "()"
lcCCStr = GDX_CMDMARGIN + CREATE_CONNECTIONS_LOC
#IF GDX_SORT_OUTPUT
=ASORT(aAll_Connections,1)
#ENDIF
FOR m.nLoop = 1 TO m.nTotal_Connections
lcProcName = m.lcProcPrefix + ALLTRIM(CHRTRAN(aAll_Connections[m.nLoop]," ","_"))
DO GetConn WITH ALLTRIM(aAll_Connections[m.nLoop]), m.hOutFile, m.lcProcName
lcCCStr = lcCCStr + CRLF + GDX_CMDMARGIN + "=this." + m.lcProcName + "()"
=Stat_Message()
ENDFOR
ENDIF
**************************
*** Get Views
**************************
lcCVProc = ""
lcCrtViews = ""
lcCVStr = ""
lcProcPrefix = "vw"
IF m.nTotal_Views > 0
lcCVProc = "CreateViews"
lcCrtViews = "=this." + m.lcCVProc + "()"
lcCVStr = GDX_CMDMARGIN + CREATE_VIEWS_LOC
#IF GDX_SORT_OUTPUT
=ASORT(aAll_Views,1)
#ENDIF
FOR m.nLoop = 1 TO m.nTotal_Views
lcProcName = m.lcProcPrefix + ALLTRIM(CHRTRAN(aAll_Views[m.nLoop]," ","_"))
DO GetView WITH ALLTRIM(aAll_Views[m.nLoop]), m.hOutFile, m.lcProcName
lcCVStr = lcCVStr + CRLF + GDX_CMDMARGIN + "=this." + m.lcProcName + "()"
=Stat_Message()
ENDFOR
ENDIF
**************************
*** Get Relations
**************************
lcSetRelStr = ""
lcProcName = "SetRelations"
IF m.nTotal_Relations > 0
lcSetRelStr = "=this." + m.lcProcName + "()"
LOCAL lcRelString
lcRelString = CRLF + GDX_PRCMARGIN + "***** " + BEGIN_RELATION_LOC + " *****" + ;
CRLF + GDX_PRCMARGIN + "PROCEDURE " + m.lcProcName + CRLF
#IF GDX_SORT_OUTPUT
=ASORT(aAll_Relations,1)
#ENDIF
FOR m.nLoop = 1 TO m.nTotal_Relations
lcRelString = lcRelString + GDX_CMDMARGIN + "ALTER TABLE '" + aAll_Relations[m.nLoop,1] +;
"' ADD FOREIGN KEY TAG " + aAll_Relations[m.nLoop,3] + ;
" REFERENCES " + IIF(" " $ aAll_Relations[m.nLoop,2], ["], []) + ;
aAll_Relations[m.nLoop,2] + IIF(" " $ aAll_Relations[m.nLoop,2], ["], []) + ;
" TAG " + aAll_Relations[m.nLoop,4] + IIF(ALTER_TABLE_NOVALIDATE, " NOVALIDATE", "") + CRLF
*! SEA: As mentioned above, we process relations so fast now, it's better
*! not to include them in the progress bar, as they distort the indicator
* =Stat_Message()
ENDFOR
lcRelString = m.lcRelString + GDX_PRCMARGIN + "ENDPROC" + CRLF
=WriteFile(m.hOutFile, m.lcRelString)
ENDIF
CLOSE DATABASE && Because we're going to start peeking into the
&& table structure of the DBC
**************************
*** Get RI Info
**************************
lcSetRIStr = ""
lcProcName = "SetRI"
IF m.nTotal_Relations > 0
DO GetRI WITH m.hOutFile, m.lcProcName
lcSetRIStr = "=this." + m.lcProcName + "()"
ENDIF
=Stat_Message()
******************************************************************************
*! Create the RemoveTable() function, which allows individual methods to
*! recreate a table that is part of the database, but has been accidentally
*! erased.
******************************************************************************
=WriteFile(m.hOutFile, CRLF + ;
GDX_PRCMARGIN + "FUNCTION RemoveTable(tcTable, tcFile)" + CRLF + ;
GDX_CMDMARGIN + "LOCAL ARRAY laTables[1]" + CRLF + ;
GDX_CMDMARGIN + "=ADBOBJECTS(laTables,'Table')" + CRLF + ;
GDX_CMDMARGIN + "IF ASCAN(laTables,tcTable) = 0" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- No such table in the current dbc, nothing to do!" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "RETURN .t." + CRLF + ;
GDX_CMDMARGIN + "ENDIF" + CRLF + ;
GDX_CMDMARGIN + "IF !FILE(tcFile)" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- If the file has been erased, REMOVE TABLE fails with a 'File does not" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- exist' error, because it can't find the file to either remove its" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- database reference or delete the file ." + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- We'll work around this problem by creating a phony file with the same" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- name, and use REMOVE TABLE DELETE to both remove the table reference" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "*-- in the dbc, and erase the phony file." + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "LOCAL lhFile" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "lhFile = FCREATE(tcFile)" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "IF lhFile = -1" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + GDX_INDENT + "RETURN .f." + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "ENDIF" + CRLF + ;
GDX_CMDMARGIN + GDX_INDENT + "=FCLOSE(lhFile)" + CRLF + ;
GDX_CMDMARGIN + "ENDIF" + CRLF + ;
GDX_CMDMARGIN + "REMOVE TABLE (tcTable) DELETE" + CRLF + ;
GDX_CMDMARGIN + "=ADBOBJECTS(laTables,'Table')" + CRLF + ;
GDX_CMDMARGIN + "RETURN ASCAN(laTables,tcTable) = 0" + CRLF + ;
GDX_PRCMARGIN + "ENDFUNC" + CRLF ;
)
******************************************************************************
*! Create the "summary" procedure that calls all the other procedures
*! to create tables, views, etc.
******************************************************************************
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + lcGenAllProc)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcCDStr)
IF !EMPTY(m.lcCrtTables)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcCrtTables)
ENDIF
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcHookStr)
=WriteFile(m.hOutFile, "")
IF MAKE_INDEX_PASS and !EMPTY(m.lcCrtIndices)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcCrtIndices)
ENDIF
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcCrtRTPs)
IF !EMPTY(m.lcCrtConns)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcCrtConns)
ENDIF
IF !EMPTY(m.lcCrtViews)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcCrtViews)
ENDIF
IF !EMPTY(m.lcSetRelStr)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcSetRelStr)
ENDIF
IF !EMPTY(m.lcSetRIStr)
=WriteFile(m.hOutFile, GDX_CMDMARGIN + m.lcSetRIStr)
ENDIF
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
IF !EMPTY(m.lcCTStr)
******************************************************************************
*! Create a method that creates the tables in the database
******************************************************************************
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + m.lcCTProc)
=WriteFile(m.hOutFile, m.lcCTStr)
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
ENDIF
IF MAKE_INDEX_PASS and !EMPTY(m.lcCIDXStr)
******************************************************************************
*! Create a method that creates the indexes for each table
******************************************************************************
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + m.lcCIDXProc)
=WriteFile(m.hOutFile, m.lcCIDXStr)
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
ENDIF
IF !EMPTY(m.lcCRTPStr)
******************************************************************************
*! Create a method that creates the rules, triggers and properties for each table
******************************************************************************
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + m.lcCRTPProc)
=WriteFile(m.hOutFile, m.lcCRTPStr)
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
ENDIF
IF !EMPTY(m.lcCCStr)
******************************************************************************
*! Create a method that creates the database's connections
******************************************************************************
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + m.lcCCProc)
=WriteFile(m.hOutFile, m.lcCCStr)
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
ENDIF
IF !EMPTY(m.lcCVStr)
******************************************************************************
*! Create a method that creates the database's views
******************************************************************************
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + m.lcCVProc)
=WriteFile(m.hOutFile, m.lcCVStr)
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
ENDIF
******************************************************************************
*! End the class definition
******************************************************************************
=WriteFile(m.hOutFile, "ENDDEFINE")
******************************************************************************
*! Make the output file(s) permanent
******************************************************************************
=FCLOSE(m.hOutFile)
COPY FILE (m.cDPTemp) TO (m.tcOutFile)
ERASE (m.cDPTemp)
IF FILE(m.cSPTemp)
COPY FILE (m.cSPTemp) to (m.cFile)
ERASE (m.cSPTemp)
ENDIF
IF tlSeparateSPObjCode AND FILE(m.cSPObjTemp)
COPY FILE (m.cSPObjTemp) to (m.cSPObjFile)
ERASE (m.cSPObjTemp)
ENDIF
=Stat_Message()
COMPILE (m.tcOutFile)
=GenDBC_CleanUp(.T.)
*********************** END OF PROGRAM ***********************
**************************************************************************
**
** Function Name: GETRI()
** Creation Date: 1994.12.02
** Purpose:
**
** To take existing FoxPro 3.0/5.0 RI Infomration, and generate an output
** program that can be used to "re-create" this.
**
** Parameters:
**
** hOutFile - The file handle of the output file
** cProcName - A character string containing the name of the
** current procedure
**
** Modification History:
**
** 1995.01.05 KRT Created function
** 1995.12.20 KRT Allow better lookup when trying to find
** the right record to add the RI information
** 1997.02.24 SEA Output code within a PROCEDURE / ENDPROC block to
** avoid error when compiling procedures > 64K
** database re-creation program a procedure
** 1997.02.24 SEA Write directly to a single output file, the handle
** of which is passed in hOutFile
** 1997.12.29 SEA Added code to break up the RI regeneration code
** into multiple procedures when it exceeds 64K
** 1998.03.26 SEA ALLTRIM()'d the dbc's ObjectName field when searching
** for relations.
** 2022.06.11 SF From VFP 6 up (at least)
** The code to rewrite RIINFO might find the wrong record
** an odd set of tags and tables might be targeted with $ operator.
** Added delimiters
**************************************************************************
PROCEDURE GetRI
LPARAMETERS hOutFile, cProcName
PRIVATE ALL EXCEPT g_*
llVersion5 ="VISUAL FOXPRO" $ upper(version(1)) AND;
VAL(LEFT(STRTRAN(VERSION(1),'Visual FoxPro ',''),AT('.',STRTRAN(VERSION(1),'Visual FoxPro ',''))-1))>=5
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "***** " + BEGIN_RI_LOC + " *****")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE " + cProcName)
*! USE the database
USE (m.g_cFullDatabase) EXCLUSIVE
LOCATE FOR ObjectType = "Relation" AND !EMPTY(RiInfo)
IF FOUND()
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "CLOSE DATABASE")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "USE '" + m.g_cDatabase + "'")
lnRILen = 50 && approx length of code so far
lnRIProcNum = 1
DO WHILE FOUND()
*! SEA: If we're about to exceed the max size of a procedure (64K),
*! close this proc and start another one, which we'll call from this proc.
*! Since we're using approximations, I'll cut off at 64000 instead of
*! 65000+.
IF lnRILen > 64000
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "=" + cProcName+"_"+ltrim(str(lnRIProcNum))+"()")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "ENDPROC")
=WriteFile(m.hOutFile, "")
=WriteFile(m.hOutFile, GDX_PRCMARGIN + "PROCEDURE "+cProcName+"_"+ltrim(str(lnRIProcNum)))
lnRIProcNum = lnRIProcNum + 1
lnRILen = 20
ENDIF
*! Have to get the parent name to verify we are adding
*! Information to the right record.
m.nParentID = ParentID
*! We use select so we won't mess up our LOCATE ... CONTINUE command
SELECT ObjectName FROM (m.g_cFullDatabase) WHERE ObjectID = nParentID INTO ARRAY aTableName
nStart = 1
cITag = ""
cTable = ""
cRTag = ""
DO WHILE m.nStart <= LEN(Property)
nSize = ASC(SUBSTR(Property, m.nStart, 1)) +;
(ASC(SUBSTR(Property, m.nStart + 1, 1)) * 256) +;
(ASC(SUBSTR(Property, m.nStart + 2, 1)) * 256^2) + ;
(ASC(SUBSTR(Property, m.nStart + 3, 1)) * 256^3)
nKey = ASC(SUBSTR(Property, m.nStart + 6, 1))
DO CASE
CASE m.nKey = 13
cITag = SUBSTR(Property, m.nStart + 7, m.nSize - 8)
CASE m.nKey = 18
cTable = SUBSTR(Property, m.nStart + 7, m.nSize - 8)
CASE m.nKey = 19
cRTag = SUBSTR(Property, m.nStart + 7, m.nSize - 8)
ENDCASE
nStart = m.nStart + m.nSize
ENDDO
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "LOCATE FOR ObjectType = 'Table' AND UPPER(ALLTRIM(ObjectName)) == '" + UPPER(ALLTRIM(aTableName[1])) + "'")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + "IF FOUND()")
=WriteFile(m.hOutFile, GDX_CMDMARGIN + GDX_INDENT + "nObjectID = ObjectID")
** SF The code to rewrite RIINFO might find the wrong record
** an odd set of tags and tables might be targeted with $ operator like
** ALTER TABLE myTab ADD FOREIGN KEY Tag longDay REFERENCES Day TAG lo
** and
** ALTER TABLE myTab ADD FOREIGN KEY Tag Day REFERENCES Long TAG ngd
** searching for "Day"$Property AND "Long"$Property AND "ngd"$Property
** might find the one or the other, because "longDay" holds all.
** also the property field might hold a comment - and then it is unprdictable at all.