@sasjs/utils 2.48.2 → 2.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/fs/generateCompileProgram.d.ts +1 -0
  2. package/fs/generateCompileProgram.js +154 -0
  3. package/fs/generateCompileProgram.js.map +1 -0
  4. package/fs/hash.d.ts +22 -0
  5. package/fs/hash.js +225 -0
  6. package/fs/hash.js.map +1 -0
  7. package/fs/index.d.ts +3 -0
  8. package/fs/index.js +13 -0
  9. package/fs/index.js.map +1 -0
  10. package/fs/internal/helper.d.ts +4 -0
  11. package/fs/internal/helper.js +125 -0
  12. package/fs/internal/helper.js.map +1 -0
  13. package/fs/sync.d.ts +6 -0
  14. package/fs/sync.js +208 -0
  15. package/fs/sync.js.map +1 -0
  16. package/index.d.ts +1 -0
  17. package/index.js +1 -0
  18. package/index.js.map +1 -1
  19. package/macros/mf_abort.sas +27 -0
  20. package/macros/mf_dedup.sas +53 -0
  21. package/macros/mf_deletefile.sas +31 -0
  22. package/macros/mf_existds.sas +30 -0
  23. package/macros/mf_existfeature.sas +59 -0
  24. package/macros/mf_existfileref.sas +33 -0
  25. package/macros/mf_existfunction.sas +37 -0
  26. package/macros/mf_existvar.sas +39 -0
  27. package/macros/mf_existvarlist.sas +56 -0
  28. package/macros/mf_fmtdttm.sas +42 -0
  29. package/macros/mf_getapploc.sas +78 -0
  30. package/macros/mf_getattrc.sas +34 -0
  31. package/macros/mf_getattrn.sas +34 -0
  32. package/macros/mf_getengine.sas +54 -0
  33. package/macros/mf_getfilesize.sas +64 -0
  34. package/macros/mf_getfmtlist.sas +61 -0
  35. package/macros/mf_getfmtname.sas +44 -0
  36. package/macros/mf_getkeyvalue.sas +32 -0
  37. package/macros/mf_getplatform.sas +75 -0
  38. package/macros/mf_getquotedstr.sas +57 -0
  39. package/macros/mf_getschema.sas +43 -0
  40. package/macros/mf_getuniquefileref.sas +60 -0
  41. package/macros/mf_getuniquelibref.sas +57 -0
  42. package/macros/mf_getuniquename.sas +22 -0
  43. package/macros/mf_getuser.sas +44 -0
  44. package/macros/mf_getvalue.sas +36 -0
  45. package/macros/mf_getvarcount.sas +49 -0
  46. package/macros/mf_getvarformat.sas +72 -0
  47. package/macros/mf_getvarlen.sas +56 -0
  48. package/macros/mf_getvarlist.sas +74 -0
  49. package/macros/mf_getvarnum.sas +58 -0
  50. package/macros/mf_getvartype.sas +52 -0
  51. package/macros/mf_getxengine.sas +43 -0
  52. package/macros/mf_increment.sas +29 -0
  53. package/macros/mf_isblank.sas +27 -0
  54. package/macros/mf_isdir.sas +34 -0
  55. package/macros/mf_isint.sas +36 -0
  56. package/macros/mf_islibds.sas +40 -0
  57. package/macros/mf_loc.sas +29 -0
  58. package/macros/mf_mkdir.sas +67 -0
  59. package/macros/mf_mval.sas +19 -0
  60. package/macros/mf_nobs.sas +26 -0
  61. package/macros/mf_trimstr.sas +50 -0
  62. package/macros/mf_uid.sas +21 -0
  63. package/macros/mf_verifymacvars.sas +72 -0
  64. package/macros/mf_wordsinstr1andstr2.sas +53 -0
  65. package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
  66. package/macros/mf_writefile.sas +67 -0
  67. package/macros/mp_abort.sas +301 -0
  68. package/macros/mp_appendfile.sas +57 -0
  69. package/macros/mp_applyformats.sas +181 -0
  70. package/macros/mp_assert.sas +56 -0
  71. package/macros/mp_assertcols.sas +145 -0
  72. package/macros/mp_assertcolvals.sas +168 -0
  73. package/macros/mp_assertdsobs.sas +121 -0
  74. package/macros/mp_assertscope.sas +147 -0
  75. package/macros/mp_base64copy.sas +117 -0
  76. package/macros/mp_binarycopy.sas +79 -0
  77. package/macros/mp_chop.sas +194 -0
  78. package/macros/mp_cleancsv.sas +78 -0
  79. package/macros/mp_cntlout.sas +85 -0
  80. package/macros/mp_copyfolder.sas +84 -0
  81. package/macros/mp_coretable.sas +73 -0
  82. package/macros/mp_createconstraints.sas +70 -0
  83. package/macros/mp_createwebservice.sas +32 -0
  84. package/macros/mp_csv2ds.sas +140 -0
  85. package/macros/mp_deleteconstraints.sas +52 -0
  86. package/macros/mp_deletefolder.sas +70 -0
  87. package/macros/mp_dictionary.sas +52 -0
  88. package/macros/mp_dirlist.sas +263 -0
  89. package/macros/mp_distinctfmtvalues.sas +50 -0
  90. package/macros/mp_dropmembers.sas +46 -0
  91. package/macros/mp_ds2cards.sas +291 -0
  92. package/macros/mp_ds2csv.sas +212 -0
  93. package/macros/mp_ds2ddl.sas +30 -0
  94. package/macros/mp_ds2fmtds.sas +105 -0
  95. package/macros/mp_ds2inserts.sas +180 -0
  96. package/macros/mp_ds2md.sas +103 -0
  97. package/macros/mp_ds2squeeze.sas +120 -0
  98. package/macros/mp_dsmeta.sas +110 -0
  99. package/macros/mp_filtercheck.sas +251 -0
  100. package/macros/mp_filtergenerate.sas +105 -0
  101. package/macros/mp_filterstore.sas +254 -0
  102. package/macros/mp_filtervalidate.sas +105 -0
  103. package/macros/mp_getcols.sas +67 -0
  104. package/macros/mp_getconstraints.sas +115 -0
  105. package/macros/mp_getdbml.sas +335 -0
  106. package/macros/mp_getddl.sas +400 -0
  107. package/macros/mp_getformats.sas +124 -0
  108. package/macros/mp_getmaxvarlengths.sas +130 -0
  109. package/macros/mp_getpk.sas +281 -0
  110. package/macros/mp_gsubfile.sas +58 -0
  111. package/macros/mp_guesspk.sas +332 -0
  112. package/macros/mp_hashdataset.sas +91 -0
  113. package/macros/mp_hashdirectory.sas +162 -0
  114. package/macros/mp_include.sas +106 -0
  115. package/macros/mp_init.sas +76 -0
  116. package/macros/mp_jsonout.sas +406 -0
  117. package/macros/mp_lib2cards.sas +78 -0
  118. package/macros/mp_lib2inserts.sas +77 -0
  119. package/macros/mp_loadformat.sas +303 -0
  120. package/macros/mp_lockanytable.sas +243 -0
  121. package/macros/mp_lockfilecheck.sas +103 -0
  122. package/macros/mp_makedata.sas +107 -0
  123. package/macros/mp_md5.sas +58 -0
  124. package/macros/mp_perflog.sas +43 -0
  125. package/macros/mp_prevobs.sas +88 -0
  126. package/macros/mp_recursivejoin.sas +90 -0
  127. package/macros/mp_replace.sas +151 -0
  128. package/macros/mp_reseterror.sas +27 -0
  129. package/macros/mp_resetoption.sas +39 -0
  130. package/macros/mp_retainedkey.sas +247 -0
  131. package/macros/mp_runddl.sas +49 -0
  132. package/macros/mp_searchcols.sas +103 -0
  133. package/macros/mp_searchdata.sas +131 -0
  134. package/macros/mp_setkeyvalue.sas +55 -0
  135. package/macros/mp_sortinplace.sas +122 -0
  136. package/macros/mp_stackdiffs.sas +592 -0
  137. package/macros/mp_storediffs.sas +230 -0
  138. package/macros/mp_stprequests.sas +74 -0
  139. package/macros/mp_streamfile.sas +212 -0
  140. package/macros/mp_testjob.sas +92 -0
  141. package/macros/mp_testservice.sas +39 -0
  142. package/macros/mp_testwritespeedlibrary.sas +59 -0
  143. package/macros/mp_tree.sas +71 -0
  144. package/macros/mp_unzip.sas +90 -0
  145. package/macros/mp_updatevarlength.sas +93 -0
  146. package/macros/mp_validatecol.sas +97 -0
  147. package/macros/mp_wait4file.sas +35 -0
  148. package/macros/mp_webin.sas +59 -0
  149. package/macros/mp_zip.sas +85 -0
  150. package/package.json +5 -2
  151. package/scripts/copySASjsCore.ts +36 -0
  152. package/types/hash.d.ts +9 -0
  153. package/types/hash.js +3 -0
  154. package/types/hash.js.map +1 -0
  155. package/types/index.d.ts +1 -0
  156. package/types/index.js +1 -0
  157. package/types/index.js.map +1 -1
  158. package/utils/index.d.ts +1 -1
  159. package/utils/index.js +2 -1
  160. package/utils/index.js.map +1 -1
  161. package/utils/utils.d.ts +1 -0
  162. package/utils/utils.js +7 -1
  163. package/utils/utils.js.map +1 -1
@@ -0,0 +1,105 @@
1
+ /**
2
+ @file
3
+ @brief Checks a generated filter query for validity
4
+ @details Runs a generated filter in proc sql with the validate option.
5
+ Used in mp_filtercheck.sas in an fcmp container.
6
+
7
+ Built to support dynamic filtering in
8
+ [Data Controller for SAS®](https://datacontroller.io).
9
+
10
+ Usage:
11
+
12
+ data work.filtertable;
13
+ infile datalines4 dsd;
14
+ input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
15
+ OPERATOR_NM:$10. RAW_VALUE:$4000.;
16
+ datalines4;
17
+ AND,AND,1,AGE,=,12
18
+ AND,AND,1,SEX,<=,"'M'"
19
+ AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
20
+ AND,OR,2,Weight,>=,7
21
+ ;;;;
22
+ run;
23
+
24
+ %mp_filtergenerate(work.filtertable,outref=myfilter)
25
+
26
+ %mp_filtervalidate(myfilter,sashelp.class)
27
+
28
+
29
+ @returns The SYSCC value will be 1008 if there are validation issues.
30
+
31
+ @param [in] inref The input fileref to validate (generated by
32
+ mp_filtergenerate.sas)
33
+ @param [in] targetds The target dataset against which to verify the query
34
+ @param [out] abort= (YES) If YES will call mp_abort.sas on any exceptions
35
+ @param [out] outds= (work.mp_filtervalidate) Output dataset containing the
36
+ err / warning message, if one exists. If this table contains any rows,
37
+ there are problems!
38
+
39
+ <h4> SAS Macros </h4>
40
+ @li mf_getuniquefileref.sas
41
+ @li mf_nobs.sas
42
+ @li mp_abort.sas
43
+
44
+ <h4> Related Macros </h4>
45
+ @li mp_filtercheck.sas
46
+ @li mp_filtergenerate.sas
47
+
48
+ @version 9.3
49
+ @author Allan Bowe
50
+
51
+ **/
52
+
53
+ %macro mp_filtervalidate(inref,targetds,abort=YES,outds=work.mp_filtervalidate);
54
+
55
+ %mp_abort(iftrue= (&syscc ne 0 or &syserr ne 0)
56
+ ,mac=&sysmacroname
57
+ ,msg=%str(syscc=&syscc / syserr=&syserr - on macro entry)
58
+ )
59
+
60
+ %local fref1;
61
+ %let fref1=%mf_getuniquefileref();
62
+
63
+ data _null_;
64
+ file &fref1;
65
+ infile &inref end=eof;
66
+ if _n_=1 then do;
67
+ put "proc sql;";
68
+ put "validate select * from &targetds";
69
+ put "where " ;
70
+ end;
71
+ input;
72
+ put _infile_;
73
+ putlog _infile_;
74
+ if eof then put ";quit;";
75
+ run;
76
+
77
+ %inc &fref1;
78
+
79
+ data &outds;
80
+ if &sqlrc or &syscc or &syserr then do;
81
+ REASON_CD='VALIDATION_ERR'!!'OR: '!!
82
+ coalescec(symget('SYSERRORTEXT'),symget('SYSWARNINGTEXT'));
83
+ output;
84
+ end;
85
+ else stop;
86
+ run;
87
+
88
+ filename &fref1 clear;
89
+
90
+ %if %mf_nobs(&outds)>0 %then %do;
91
+ %if &abort=YES %then %do;
92
+ data _null_;
93
+ set &outds;
94
+ call symputx('REASON_CD',reason_cd,'l');
95
+ stop;
96
+ run;
97
+ %mp_abort(
98
+ mac=&sysmacroname,
99
+ msg=%str(Filter validation issues.)
100
+ )
101
+ %end;
102
+ %let syscc=1008;
103
+ %end;
104
+
105
+ %mend mp_filtervalidate;
@@ -0,0 +1,67 @@
1
+ /**
2
+ @file
3
+ @brief Creates a dataset with column metadata.
4
+ @details This macro takes the `proc contents` output and "tidies it up" in the
5
+ following ways:
6
+
7
+ @li Blank labels are filled in with column names
8
+ @li Formats are reconstructed with default values
9
+ @li Types such as DATE / TIME / DATETIME are inferred from the formats
10
+
11
+ Example usage:
12
+
13
+ %mp_getcols(sashelp.airline,outds=work.myds)
14
+
15
+ @param ds The dataset from which to obtain column metadata
16
+ @param outds= (work.cols) The output dataset to create. Sample data:
17
+ |NAME:$32.|LENGTH:best.|VARNUM:best.|LABEL:$256.|FMTNAME:$32.|FORMAT:$49.|TYPE:$1.|DDTYPE:$9.|
18
+ |---|---|---|---|---|---|---|---|
19
+ |`AIR `|`8 `|`2 `|`international airline travel (thousands) `|` `|`8. `|`N `|`NUMERIC `|
20
+ |`DATE `|`8 `|`1 `|`DATE `|`MONYY `|`MONYY. `|`N `|`DATE `|
21
+ |`REGION `|`3 `|`3 `|`REGION `|` `|`$3. `|`C `|`CHARACTER `|
22
+
23
+ <h4> Related Macros </h4>
24
+ @li mf_getvarlist.sas
25
+ @li mm_getcols.sas
26
+
27
+ @version 9.2
28
+ @author Allan Bowe
29
+
30
+ **/
31
+
32
+ %macro mp_getcols(ds, outds=work.cols);
33
+ %local dropds;
34
+ proc contents noprint data=&ds
35
+ out=_data_ (keep=name type length label varnum format:);
36
+ run;
37
+ %let dropds=&syslast;
38
+ data &outds(keep=name type length varnum format label ddtype fmtname);
39
+ set &dropds(rename=(format=fmtname type=type2));
40
+ name=upcase(name);
41
+ if type2=2 then do;
42
+ length format $49.;
43
+ if fmtname='' then format=cats('$',length,'.');
44
+ else if formatl=0 then format=cats(fmtname,'.');
45
+ else format=cats(fmtname,formatl,'.');
46
+ type='C';
47
+ ddtype='CHARACTER';
48
+ end;
49
+ else do;
50
+ if fmtname='' then format=cats(length,'.');
51
+ else if formatl=0 then format=cats(fmtname,'.');
52
+ else if formatd=0 then format=cats(fmtname,formatl,'.');
53
+ else format=cats(fmtname,formatl,'.',formatd);
54
+ type='N';
55
+ if format=:'DATETIME' or format=:'E8601DT' then ddtype='DATETIME';
56
+ else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
57
+ or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
58
+ or format=:'MONYY'
59
+ then ddtype='DATE';
60
+ else if format=:'TIME' then ddtype='TIME';
61
+ else ddtype='NUMERIC';
62
+ end;
63
+ if label='' then label=name;
64
+ run;
65
+ proc sql;
66
+ drop table &dropds;
67
+ %mend mp_getcols;
@@ -0,0 +1,115 @@
1
+ /**
2
+ @file mp_getconstraints.sas
3
+ @brief Get constraint details at column level
4
+ @details Useful for capturing constraints before they are dropped / reapplied
5
+ during an update.
6
+
7
+ proc sql;
8
+ create table work.example(
9
+ TX_FROM float format=datetime19.,
10
+ DD_TYPE char(16),
11
+ DD_SOURCE char(2048),
12
+ DD_SHORTDESC char(256),
13
+ constraint pk primary key(tx_from, dd_type,dd_source),
14
+ constraint unq unique(tx_from, dd_type),
15
+ constraint nnn not null(DD_SHORTDESC)
16
+ );
17
+
18
+ %mp_getconstraints(lib=work,ds=example,outds=work.constraints)
19
+
20
+ @param [in] lib= (WORK) The target library
21
+ @param [in] ds= The target dataset. Leave blank (default) for all datasets.
22
+ @param [in] mdebug= (0) Set to 1 to preserve temp tables, print var values etc
23
+ @param [out] outds= (mp_getconstraints) the output dataset
24
+
25
+ <h4> SAS Macros </h4>
26
+ @li mf_getuniquename.sas
27
+ @li mp_dropmembers.sas
28
+
29
+ @version 9.2
30
+ @author Allan Bowe
31
+
32
+ **/
33
+
34
+ %macro mp_getconstraints(lib=WORK
35
+ ,ds=
36
+ ,outds=mp_getconstraints
37
+ ,mdebug=0
38
+ )/*/STORE SOURCE*/;
39
+
40
+ %let lib=%upcase(&lib);
41
+ %let ds=%upcase(&ds);
42
+
43
+ /**
44
+ * Cater for environments where sashelp.vcncolu is not available
45
+ */
46
+ %if %sysfunc(exist(sashelp.vcncolu,view))=0 %then %do;
47
+ proc sql;
48
+ create table &outds(
49
+ libref char(8)
50
+ ,TABLE_NAME char(32)
51
+ ,constraint_type char(8) label='Constraint Type'
52
+ ,constraint_name char(32) label='Constraint Name'
53
+ ,column_name char(32) label='Column'
54
+ ,constraint_order num
55
+ );
56
+ %return;
57
+ %end;
58
+
59
+ /**
60
+ * Neither dictionary tables nor sashelp provides a constraint order column,
61
+ * however they DO arrive in the correct order. So, create the col.
62
+ **/
63
+ %local vw;
64
+ %let vw=%mf_getuniquename(prefix=mp_getconstraints_vw_);
65
+ data &vw /view=&vw;
66
+ set sashelp.vcncolu;
67
+ where table_catalog="&lib";
68
+
69
+ /* use retain approach to reset the constraint order with each constraint */
70
+ length tmp $1000;
71
+ retain tmp;
72
+ drop tmp;
73
+ if tmp ne catx('|',table_catalog,table_name,constraint_name) then do;
74
+ constraint_order=1;
75
+ end;
76
+ else constraint_order+1;
77
+ tmp=catx('|',table_catalog, table_name,constraint_name);
78
+ run;
79
+
80
+ /* must use SQL as proc datasets does not support length changes */
81
+ proc sql noprint;
82
+ create table &outds as
83
+ select upcase(a.TABLE_CATALOG) as libref
84
+ ,upcase(a.TABLE_NAME) as TABLE_NAME
85
+ ,a.constraint_type
86
+ ,a.constraint_name
87
+ ,b.column_name
88
+ ,b.constraint_order
89
+ from dictionary.TABLE_CONSTRAINTS a
90
+ left join &vw b
91
+ on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
92
+ and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
93
+ and a.constraint_name=b.constraint_name
94
+ /**
95
+ * We cannot apply this clause to the underlying dictionary table. See:
96
+ * https://communities.sas.com/t5/SAS-Programming/Unexpected-Where-Clause-behaviour-in-dictionary-TABLE/m-p/771554#M244867
97
+ * cannot use`where calculated libref="&lib"` either as it will STILL execute
98
+ * all the underlying constraint queries, causing exception errors in some
99
+ * cases: https://github.com/sasjs/core/issues/283
100
+ */
101
+ where a.TABLE_CATALOG="&lib"
102
+ %if "&ds" ne "" %then %do;
103
+ and upcase(a.TABLE_NAME)="&ds"
104
+ and upcase(b.TABLE_NAME)="&ds"
105
+ %end;
106
+ order by libref, table_name, constraint_name, constraint_order
107
+ ;
108
+
109
+ /* tidy up */
110
+ %mp_dropmembers(
111
+ &vw,
112
+ iftrue=(&mdebug=0)
113
+ )
114
+
115
+ %mend mp_getconstraints;
@@ -0,0 +1,335 @@
1
+ /**
2
+ @file
3
+ @brief Extract DBML from SAS Libraries
4
+ @details DBML is an open source markup format to represent databases.
5
+ More details: https://www.dbml.org/home/
6
+
7
+ Usage:
8
+
9
+
10
+ %mp_getdbml(liblist=SASHELP WORK,outref=mydbml,showlog=YES)
11
+
12
+ Take the log output and paste it into the renderer at https://dbdiagram.io
13
+ to view your data model diagram. The code takes a "best guess" at
14
+ the one to one and one to many relationships (based on constraints
15
+ and indexes, and assuming that the column names would match).
16
+
17
+ You may need to adjust the rendered DBML to suit your needs.
18
+
19
+ ![dbml for sas](https://i.imgur.com/8T1tIZp.gif)
20
+
21
+
22
+ <h4> SAS Macros </h4>
23
+ @li mf_getquotedstr.sas
24
+ @li mp_getconstraints.sas
25
+
26
+ @param liblist= Space seperated list of librefs to take as
27
+ input (Default=SASHELP)
28
+ @param outref= Fileref to contain the DBML (Default=getdbml)
29
+ @param showlog= set to YES to show the DBML in the log (Default is NO)
30
+
31
+ @version 9.3
32
+ @author Allan Bowe
33
+ **/
34
+
35
+ %macro mp_getdbml(liblist=SASHELP,outref=getdbml,showlog=NO
36
+ )/*/STORE SOURCE*/;
37
+
38
+ /* check fileref is assigned */
39
+ %if %sysfunc(fileref(&outref)) > 0 %then %do;
40
+ filename &outref temp;
41
+ %end;
42
+
43
+ %let liblist=%upcase(&liblist);
44
+
45
+ proc sql noprint;
46
+ create table _data_ as
47
+ select * from dictionary.tables
48
+ where upcase(libname) in (%mf_getquotedstr(&liblist))
49
+ order by libname,memname;
50
+ %local tabinfo; %let tabinfo=&syslast;
51
+
52
+ create table _data_ as
53
+ select * from dictionary.columns
54
+ where upcase(libname) in (%mf_getquotedstr(&liblist))
55
+ order by libname,memname,varnum;
56
+ %local colinfo; %let colinfo=&syslast;
57
+
58
+ %local dsnlist;
59
+ select distinct upcase(cats(libname,'.',memname)) into: dsnlist
60
+ separated by ' '
61
+ from &syslast
62
+ ;
63
+
64
+ create table _data_ as
65
+ select * from dictionary.indexes
66
+ where upcase(libname) in (%mf_getquotedstr(&liblist))
67
+ order by idxusage, indxname, indxpos;
68
+ %local idxinfo; %let idxinfo=&syslast;
69
+
70
+ /* Extract all Primary Key and Unique data constraints */
71
+ %mp_getconstraints(lib=%scan(&liblist,1),outds=_data_)
72
+ %local colconst; %let colconst=&syslast;
73
+
74
+ %do x=2 %to %sysfunc(countw(&liblist));
75
+ %mp_getconstraints(lib=%scan(&liblist,&x),outds=_data_)
76
+ proc append base=&colconst data=&syslast;
77
+ run;
78
+ %end;
79
+
80
+
81
+
82
+
83
+ /* header info */
84
+ data _null_;
85
+ file &outref;
86
+ put "// DBML generated by &sysuserid on %sysfunc(datetime(),datetime19.) ";
87
+ put "Project sasdbml {";
88
+ put " database_type: 'SAS'";
89
+ put " Note: 'Generated by the mp_getdbml() macro'";
90
+ put "}";
91
+ run;
92
+
93
+ /* create table groups */
94
+ data _null_;
95
+ file &outref mod;
96
+ set &tabinfo;
97
+ by libname;
98
+ if first.libname then put "TableGroup " libname "{";
99
+ ds=quote(cats(libname,'.',memname));
100
+ put ' ' ds;
101
+ if last.libname then put "}";
102
+ run;
103
+
104
+ /* table for pks */
105
+ data _data_;
106
+ length curds const col $39;
107
+ call missing (of _all_);
108
+ stop;
109
+ run;
110
+ %let pkds=&syslast;
111
+
112
+ %local x curds constraints_used constcheck;
113
+ %do x=1 %to %sysfunc(countw(&dsnlist,%str( )));
114
+ %let curds=%scan(&dsnlist,&x,%str( ));
115
+ %let constraints_used=;
116
+ %let constcheck=0;
117
+ data _null_;
118
+ file &outref mod;
119
+ length lab $1024 typ $20;
120
+ set &colinfo (where=(
121
+ libname="%scan(&curds,1,.)" and upcase(memname)="%scan(&curds,2,.)"
122
+ )) end=last;
123
+
124
+ if _n_=1 then do;
125
+ table='Table "'!!"&curds"!!'"{';
126
+ put table;
127
+ end;
128
+ name=upcase(name);
129
+ lab=" note:"!!quote(trim(tranwrd(label,'"',"'")));
130
+ if upcase(format)=:'DATETIME' then typ='datetime';
131
+ else if type='char' then typ=cats('char(',length,')');
132
+ else typ='num';
133
+
134
+ if notnull='yes' then notnul=' not null';
135
+ if notnull='no' and missing(label) then put ' ' name typ;
136
+ else if notnull='yes' and missing(label) then do;
137
+ put ' ' name typ '[' notnul ']';
138
+ end;
139
+ else if notnull='no' then put ' ' name typ '[' lab ']';
140
+ else put ' ' name typ '[' notnul ',' lab ']';
141
+
142
+ run;
143
+
144
+ data _data_(keep=curds const col);
145
+ length ctype $11 cols constraints_used $5000;
146
+ set &colconst (where=(
147
+ upcase(libref)="%scan(&curds,1,.)"
148
+ and upcase(table_name)="%scan(&curds,2,.)"
149
+ and constraint_type in ('PRIMARY','UNIQUE')
150
+ )) end=last;
151
+ file &outref mod;
152
+ by constraint_type constraint_name;
153
+ retain cols;
154
+ column_name=upcase(column_name);
155
+
156
+ if _n_=1 then put / ' indexes {';
157
+
158
+ if upcase(strip(constraint_type)) = 'PRIMARY' then ctype='[pk]';
159
+ else ctype='[unique]';
160
+
161
+ if first.constraint_name then cols = cats('(',column_name);
162
+ else cols=cats(cols,',',column_name);
163
+
164
+ if last.constraint_name then do;
165
+ cols=cats(cols,')',ctype)!!' //'!!constraint_name;
166
+ put ' ' cols;
167
+ constraints_used=catx(' ',constraints_used, constraint_name);
168
+ call symputx('constcheck',1);
169
+ end;
170
+
171
+ if last then call symput('constraints_used',cats(upcase(constraints_used)));
172
+
173
+ length curds const col $39;
174
+ curds="&curds";
175
+ const=constraint_name;
176
+ col=column_name;
177
+ run;
178
+
179
+ proc append base=&pkds data=&syslast;run;
180
+
181
+ /* Create Unique Indexes, but only if they were not already defined within
182
+ the Constraints section. */
183
+ data _data_(keep=curds const col);
184
+ set &idxinfo (where=(
185
+ libname="%scan(&curds,1,.)"
186
+ and upcase(memname)="%scan(&curds,2,.)"
187
+ and unique='yes'
188
+ and upcase(indxname) not in (%mf_getquotedstr(&constraints_used))
189
+ ));
190
+ file &outref mod;
191
+ by idxusage indxname;
192
+ name=upcase(name);
193
+ if &constcheck=1 then stop; /* we only care about PKs so stop if we have */
194
+ if _n_=1 and &constcheck=0 then put / ' indexes {';
195
+
196
+ length cols $5000;
197
+ retain cols;
198
+ if first.indxname then cols = cats('(',name);
199
+ else cols=cats(cols,',',name);
200
+
201
+ if last.indxname then do;
202
+ cols=cats(cols,')[unique]')!!' //'!!indxname;
203
+ put ' ' cols;
204
+ call symputx('constcheck',1);
205
+ end;
206
+
207
+ length curds const col $39;
208
+ curds="&curds";
209
+ const=indxname;
210
+ col=name;
211
+ run;
212
+ proc append base=&pkds data=&syslast;run;
213
+
214
+ data _null_;
215
+ file &outref mod;
216
+ if &constcheck =1 then put ' }';
217
+ put '}';
218
+ run;
219
+
220
+ %end;
221
+
222
+ /**
223
+ * now we need to figure out the relationships
224
+ */
225
+
226
+ /* sort alphabetically so we can have one set of unique cols per table */
227
+ proc sort data=&pkds nodupkey;
228
+ by curds const col;
229
+ run;
230
+
231
+ data &pkds.1 (keep=curds col)
232
+ &pkds.2 (keep=curds cols);
233
+ set &pkds;
234
+ by curds const;
235
+ length retconst $39 cols $5000;
236
+ retain retconst cols;
237
+ if first.curds then do;
238
+ retconst=const;
239
+ cols=upcase(col);
240
+ end;
241
+ else cols=catx(' ',cols,upcase(col));
242
+ if retconst=const then do;
243
+ output &pkds.1;
244
+ if last.const then output &pkds.2;
245
+ end;
246
+ run;
247
+
248
+ %let curdslist="0";
249
+ %do x=1 %to %sysfunc(countw(&dsnlist,%str( )));
250
+ %let curds=%scan(&dsnlist,&x,%str( ));
251
+
252
+ %let pkcols=0;
253
+ data _null_;
254
+ set &pkds.2(where=(curds="&curds"));
255
+ call symputx('pkcols',cols);
256
+ run;
257
+ %if &pkcols ne 0 %then %do;
258
+ %let curdslist=&curdslist,"&curds";
259
+
260
+ /* start with one2one */
261
+ data &pkds.4;
262
+ file &outref mod;
263
+ set &pkds.2(where=(cols="&pkcols" and curds not in (&curdslist)));
264
+ line='Ref: "'!!"&curds"
265
+ !!cats('".(',"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))",')')
266
+ !!' - '
267
+ !!cats(quote(trim(curds))
268
+ ,'.('
269
+ ,"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
270
+ ,')'
271
+ );
272
+ put line;
273
+ run;
274
+
275
+ /* now many2one */
276
+ /* get table with one row per col */
277
+ data &pkds.5;
278
+ set &pkds.1(where=(curds="&curds"));
279
+ run;
280
+ /* get tables which contain the PK columns */
281
+ proc sql;
282
+ create table &pkds.5a as
283
+ select upcase(cats(b.libname,'.',b.memname)) as curds
284
+ ,b.name
285
+ from &pkds.5 a
286
+ inner join &colinfo b
287
+ on a.col=upcase(b.name);
288
+ /* count to make sure those tables contain ALL the columns */
289
+ create table &pkds.5b as
290
+ select curds,count(*) as cnt
291
+ from &pkds.5a
292
+ where curds not in (
293
+ select curds from &pkds.2 where cols="&pkcols"
294
+ ) /* not a one to one match */
295
+ and curds ne "&curds" /* exclude self */
296
+ group by 1;
297
+ create table &pkds.6 as
298
+ select a.*
299
+ ,b.cols
300
+ from &pkds.5b a
301
+ left join &pkds.4 b
302
+ on a.curds=b.curds;
303
+
304
+ data _null_;
305
+ set &pkds.6;
306
+ file &outref mod;
307
+ colcnt=%sysfunc(countw(&pkcols));
308
+ if cnt=colcnt then do;
309
+ /* table contains all the PK cols, and was not a direct / 121 match */
310
+ line='Ref: "'!!"&curds"
311
+ !!'".('
312
+ !!"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
313
+ !!') > '
314
+ !!cats(quote(trim(curds))
315
+ ,'.('
316
+ ,"%mf_getquotedstr(&pkcols,dlm=%str(,),quote=%str( ))"
317
+ ,')'
318
+ );
319
+ put line;
320
+ end;
321
+ run;
322
+ %end;
323
+ %end;
324
+
325
+
326
+ %if %upcase(&showlog)=YES %then %do;
327
+ options ps=max;
328
+ data _null_;
329
+ infile &outref;
330
+ input;
331
+ putlog _infile_;
332
+ run;
333
+ %end;
334
+
335
+ %mend mp_getdbml;