@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,291 @@
1
+ /**
2
+ @file
3
+ @brief Create a CARDS file from a SAS dataset.
4
+ @details Uses dataset attributes to convert all data into datalines.
5
+ Running the generated file will rebuild the original dataset. Includes
6
+ support for large decimals, binary data, PROCESSED_DTTM columns, and
7
+ alternative encoding. If the input dataset is empty, the cards file will
8
+ still be created.
9
+
10
+ Additional support to generate a random sample and max rows.
11
+
12
+ Usage:
13
+
14
+ %mp_ds2cards(sashelp.class
15
+ , tgt_ds=work.class
16
+ , cards_file= "C:\temp\class.sas"
17
+ , showlog=NO
18
+ , maxobs=5
19
+ )
20
+
21
+ TODO:
22
+ - labelling the dataset
23
+ - explicity setting a unix LF
24
+ - constraints / indexes etc
25
+
26
+ @param [in] base_ds Should be two level - eg work.blah. This is the table
27
+ that is converted to a cards file.
28
+ @param [in] tgt_ds= Table that the generated cards file would create.
29
+ Optional - if omitted, will be same as BASE_DS.
30
+ @param [out] cards_file= ("%sysfunc(pathname(work))/cardgen.sas") Location in
31
+ which to write the (.sas) cards file
32
+ @param [in] maxobs= (max) To limit output to the first <code>maxobs</code>
33
+ observations, enter an integer here.
34
+ @param [in] random_sample= (NO) Set to YES to generate a random sample of
35
+ data. Can be quite slow.
36
+ @param [in] showlog= (YES) Whether to show generated cards file in the SAS
37
+ log. Valid values:
38
+ @li YES
39
+ @li NO
40
+ @param [in] outencoding= Provide encoding value for file statement (eg utf-8)
41
+ @param [in] append= (NO) If NO then will rebuild the cards file if it already
42
+ exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
43
+
44
+ <h4> Related Macros </h4>
45
+ @li mp_lib2cards.sas
46
+ @li mp_ds2inserts.sas
47
+ @li mp_mdtablewrite.sas
48
+
49
+ @version 9.2
50
+ @author Allan Bowe
51
+ @cond
52
+ **/
53
+
54
+ %macro mp_ds2cards(base_ds, tgt_ds=
55
+ ,cards_file="%sysfunc(pathname(work))/cardgen.sas"
56
+ ,maxobs=max
57
+ ,random_sample=NO
58
+ ,showlog=YES
59
+ ,outencoding=
60
+ ,append=NO
61
+ )/*/STORE SOURCE*/;
62
+ %local i setds nvars;
63
+
64
+ %if not %sysfunc(exist(&base_ds)) %then %do;
65
+ %put %str(WARN)ING: &base_ds does not exist;
66
+ %return;
67
+ %end;
68
+
69
+ %if %index(&base_ds,.)=0 %then %let base_ds=WORK.&base_ds;
70
+ %if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
71
+ %if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
72
+ %if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
73
+ %if ("&append" = "" or "&append" = "NO") %then %let append=;
74
+ %else %let append=mod;
75
+
76
+ /* get varcount */
77
+ %let nvars=0;
78
+ proc sql noprint;
79
+ select count(*) into: nvars from dictionary.columns
80
+ where upcase(libname)="%scan(%upcase(&base_ds),1)"
81
+ and upcase(memname)="%scan(%upcase(&base_ds),2)";
82
+ %if &nvars=0 %then %do;
83
+ %put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
84
+ %return;
85
+ %end;
86
+
87
+ /* get indexes */
88
+ proc sort
89
+ data=sashelp.vindex(
90
+ where=(upcase(libname)="%scan(%upcase(&base_ds),1)"
91
+ and upcase(memname)="%scan(%upcase(&base_ds),2)")
92
+ )
93
+ out=_data_;
94
+ by indxname indxpos;
95
+ run;
96
+
97
+ %local indexes;
98
+ data _null_;
99
+ set &syslast end=last;
100
+ if _n_=1 then call symputx('indexes','(index=(','l');
101
+ by indxname indxpos;
102
+ length vars $32767 nom uni $8;
103
+ retain vars;
104
+ if first.indxname then do;
105
+ idxcnt+1;
106
+ nom='';
107
+ uni='';
108
+ vars=name;
109
+ end;
110
+ else vars=catx(' ',vars,name);
111
+ if last.indxname then do;
112
+ if nomiss='yes' then nom='/nomiss';
113
+ if unique='yes' then uni='/unique';
114
+ call symputx('indexes'
115
+ ,catx(' ',symget('indexes'),indxname,'=(',vars,')',nom,uni)
116
+ ,'l');
117
+ end;
118
+ if last then call symputx('indexes',cats(symget('indexes'),'))'),'l');
119
+ run;
120
+
121
+
122
+ data;run;
123
+ %let setds=&syslast;
124
+ proc sql
125
+ %if %datatyp(&maxobs)=NUMERIC %then %do;
126
+ outobs=&maxobs;
127
+ %end;
128
+ ;
129
+ create table &setds as select * from &base_ds
130
+ %if &random_sample=YES %then %do;
131
+ order by ranuni(42)
132
+ %end;
133
+ ;
134
+ reset outobs=max;
135
+ create table datalines1 as
136
+ select name,type,length,varnum,format,label from dictionary.columns
137
+ where upcase(libname)="%upcase(%scan(&base_ds,1))"
138
+ and upcase(memname)="%upcase(%scan(&base_ds,2))";
139
+
140
+ /**
141
+ Due to long decimals cannot use best. format
142
+ So - use bestd. format and then use character functions to strip trailing
143
+ zeros, if NOT an integer or missing!! Cannot use int() as it upsets
144
+ note2err when there are missings.
145
+ resolved code = ifc( mod(coalesce(VARIABLE,0),1)=0
146
+ ,put(VARIABLE,best32.)
147
+ ,substrn(put(VARIABLE,bestd32.),1
148
+ ,findc(put(VARIABLE,bestd32.),'0','TBK')));
149
+ **/
150
+
151
+ data datalines_2;
152
+ format dataline $32000.;
153
+ set datalines1 (where=(upcase(name) not in
154
+ ('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM')));
155
+ if type='num' then dataline=
156
+ cats('ifc(mod(coalesce(',name,',0),1)=0
157
+ ,put(',name,',best32.-l)
158
+ ,substrn(put(',name,',bestd32.-l),1
159
+ ,findc(put(',name,',bestd32.-l),"0","TBK")))');
160
+ /**
161
+ * binary data must be converted, to store in text format. It is identified
162
+ * by the presence of the $HEX keyword in the format.
163
+ */
164
+ else if upcase(format)=:'$HEX' then
165
+ dataline=cats('put(trim(',name,'),',format,')');
166
+ /**
167
+ * There is no easy way to store line breaks in a cards file.
168
+ * To discuss this, use: https://github.com/sasjs/core/issues/80
169
+ * Removing all nonprintables with kw (keep writeable)
170
+ */
171
+ else dataline=cats('compress(',name,', ,"kw")');
172
+ run;
173
+
174
+ proc sql noprint;
175
+ select dataline into: datalines separated by ',' from datalines_2;
176
+
177
+ %local
178
+ process_dttm_flg
179
+ valid_from_dttm_flg
180
+ valid_to_dttm_flg
181
+ ;
182
+ %let process_dttm_flg = N;
183
+ %let valid_from_dttm_flg = N;
184
+ %let valid_to_dttm_flg = N;
185
+ data _null_;
186
+ set datalines1 ;
187
+ /* build attrib statement */
188
+ if type='char' then type2='$';
189
+ if strip(format) ne '' then format2=cats('format=',format);
190
+ if strip(label) ne '' then label2=cats('label=',quote(trim(label)));
191
+ str1=catx(' ',(put(name,$33.)||'length=')
192
+ ,put(cats(type2,length),$7.)||format2,label2);
193
+
194
+
195
+ /* Build input statement */
196
+ if upcase(format)=:'$HEX' then type3=':'!!format;
197
+ else if type='char' then type3=':$char.';
198
+ str2=put(name,$33.)||type3;
199
+
200
+
201
+ if(upcase(name) = "PROCESSED_DTTM") then
202
+ call symputx("process_dttm_flg", "Y", "L");
203
+ if(upcase(name) = "VALID_FROM_DTTM") then
204
+ call symputx("valid_from_dttm_flg", "Y", "L");
205
+ if(upcase(name) = "VALID_TO_DTTM") then
206
+ call symputx("valid_to_dttm_flg", "Y", "L");
207
+
208
+
209
+ call symputx(cats("attrib_stmt_", put(_N_, 8.)), str1, "L");
210
+ call symputx(cats("input_stmt_", put(_N_, 8.))
211
+ , ifc(upcase(name) not in
212
+ ('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM'), str2, ""), "L");
213
+ run;
214
+
215
+ data _null_;
216
+ file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
217
+ length __attrib $32767;
218
+ if _n_=1 then do;
219
+ put '/**';
220
+ put ' @file';
221
+ put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
222
+ put " @details Generated by %nrstr(%%)mp_ds2cards()";
223
+ put " Source: https://github.com/sasjs/core";
224
+ put ' @cond ';
225
+ put '**/';
226
+ put "data &tgt_ds &indexes;";
227
+ put "attrib ";
228
+ %do i = 1 %to &nvars;
229
+ __attrib=symget("attrib_stmt_&i");
230
+ put __attrib;
231
+ %end;
232
+ put ";";
233
+
234
+ %if &process_dttm_flg. eq Y %then %do;
235
+ put 'retain PROCESSED_DTTM %sysfunc(datetime());';
236
+ %end;
237
+ %if &valid_from_dttm_flg. eq Y %then %do;
238
+ put 'retain VALID_FROM_DTTM &low_date;';
239
+ %end;
240
+ %if &valid_to_dttm_flg. eq Y %then %do;
241
+ put 'retain VALID_TO_DTTM &high_date;';
242
+ %end;
243
+ if __nobs=0 then do;
244
+ put 'call missing(of _all_);/* avoid uninitialised notes */';
245
+ put 'stop;';
246
+ put 'run;';
247
+ end;
248
+ else do;
249
+ put "infile cards dsd;";
250
+ put "input ";
251
+ %do i = 1 %to &nvars.;
252
+ %if(%length(&&input_stmt_&i..)) %then
253
+ put " &&input_stmt_&i..";
254
+ ;
255
+ %end;
256
+ put ";";
257
+ put 'missing a b c d e f g h i j k l m n o p q r s t u v w x y z _;';
258
+ put "datalines4;";
259
+ end;
260
+ end;
261
+ set &setds end=__lastobs nobs=__nobs;
262
+ /* remove all formats for write purposes - some have long underlying decimals */
263
+ format _numeric_ best30.29;
264
+ length __dataline $32767;
265
+ __dataline=catq('cqtmb',&datalines);
266
+ put __dataline;
267
+ if __lastobs then do;
268
+ put ';;;;';
269
+ put 'run;';
270
+ put '/** @endcond **/';
271
+ stop;
272
+ end;
273
+ run;
274
+ proc sql;
275
+ drop table &setds;
276
+ quit;
277
+
278
+ %if &showlog=YES %then %do;
279
+ data _null_;
280
+ infile &cards_file lrecl=32767;
281
+ input;
282
+ put _infile_;
283
+ run;
284
+ %end;
285
+
286
+ %put NOTE: CARDS FILE SAVED IN:;
287
+ %put NOTE-;%put NOTE-;
288
+ %put NOTE- %sysfunc(dequote(&cards_file.));
289
+ %put NOTE-;%put NOTE-;
290
+ %mend mp_ds2cards;
291
+ /** @endcond **/
@@ -0,0 +1,212 @@
1
+ /**
2
+ @file
3
+ @brief Export a dataset to a CSV file WITH leading blanks
4
+ @details Export a dataset to a file or fileref, retaining leading blanks.
5
+
6
+ When using SASJS headerformat, the input statement is provided in the first
7
+ row of the CSV.
8
+
9
+ Usage:
10
+
11
+ %mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
12
+
13
+ filename example temp;
14
+ %mp_ds2csv(sashelp.air,outref=example,headerformat=SASJS)
15
+ data; infile example; input;put _infile_; if _n_>5 then stop;run;
16
+
17
+ data _null_;
18
+ infile example;
19
+ input;
20
+ call symputx('stmnt',_infile_);
21
+ stop;
22
+ run;
23
+ data work.want;
24
+ infile example dsd firstobs=2;
25
+ input &stmnt;
26
+ run;
27
+
28
+ Why use mp_ds2csv over, say, proc export?
29
+
30
+ 1. Ability to retain leading blanks (this is a major one)
31
+ 2. Control the header format
32
+ 3. Simple one-liner
33
+
34
+ @param [in] ds The dataset to be exported
35
+ @param [in] dlm= (COMMA) The delimeter to apply. For SASJS, will always be
36
+ COMMA. Supported values:
37
+ @li COMMA
38
+ @li SEMICOLON
39
+ @param [in] headerformat= (LABEL) The format to use for the header section.
40
+ Valid values:
41
+ @li LABEL - Use the variable label (or name, if blank)
42
+ @li NAME - Use the variable name
43
+ @li SASJS - Used to create sasjs-formatted input CSVs, eg for use in
44
+ mp_testservice.sas. This format will supply an input statement in the
45
+ first row, making ingestion by datastep a breeze. Special misisng values
46
+ will be prefixed with a period (eg `.A`) to enable ingestion on both SAS 9
47
+ and Viya. Dates / Datetimes etc are identified by the format type (lookup
48
+ with mcf_getfmttype.sas) and converted to human readable formats (not
49
+ numbers).
50
+ @param [out] outfile= The output filename - should be quoted.
51
+ @param [out] outref= (0) The output fileref (takes precedence if provided)
52
+ @param [in] outencoding= (0) The (quoted) output encoding to use, eg `"UTF-8"`
53
+ @param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
54
+ always be CRLF. Valid values:
55
+ @li CRLF
56
+ @li LF
57
+
58
+ <h4> SAS Macros </h4>
59
+ @li mcf_getfmttype.sas
60
+ @li mf_getuniquename.sas
61
+ @li mf_getvarformat.sas
62
+ @li mf_getvarlist.sas
63
+ @li mf_getvartype.sas
64
+
65
+ @version 9.2
66
+ @author Allan Bowe (credit mjsq)
67
+ **/
68
+
69
+ %macro mp_ds2csv(ds
70
+ ,dlm=COMMA
71
+ ,outref=0
72
+ ,outfile=
73
+ ,outencoding=0
74
+ ,headerformat=LABEL
75
+ ,termstr=CRLF
76
+ )/*/STORE SOURCE*/;
77
+
78
+ %local outloc delim i varlist var vcnt vat dsv vcom vmiss fmttype vfmt;
79
+
80
+ %if not %sysfunc(exist(&ds)) %then %do;
81
+ %put %str(WARN)ING: &ds does not exist;
82
+ %return;
83
+ %end;
84
+
85
+ %if %index(&ds,.)=0 %then %let ds=WORK.&ds;
86
+
87
+ %if &outencoding=0 %then %let outencoding=;
88
+ %else %let outencoding=encoding=&outencoding;
89
+
90
+ %if &outref=0 %then %let outloc=&outfile;
91
+ %else %let outloc=&outref;
92
+
93
+ %if &headerformat=SASJS %then %do;
94
+ %let delim=",";
95
+ %let termstr=CRLF;
96
+ %mcf_getfmttype(wrap=YES)
97
+ %end;
98
+ %else %if &dlm=COMMA %then %let delim=",";
99
+ %else %let delim=";";
100
+
101
+ /* credit to mjsq - https://stackoverflow.com/a/55642267 */
102
+
103
+ /* first get headers */
104
+ data _null_;
105
+ file &outloc &outencoding lrecl=32767 termstr=&termstr;
106
+ length header $ 2000 varnm vfmt $32 dlm $1 fmttype $8;
107
+ call missing(of _all_);
108
+ dsid=open("&ds.","i");
109
+ num=attrn(dsid,"nvars");
110
+ dlm=&delim;
111
+ do i=1 to num;
112
+ varnm=upcase(varname(dsid,i));
113
+ if i=num then dlm='';
114
+ %if &headerformat=NAME %then %do;
115
+ header=cats(varnm,dlm);
116
+ %end;
117
+ %else %if &headerformat=LABEL %then %do;
118
+ header = cats(coalescec(varlabel(dsid,i),varnm),dlm);
119
+ %end;
120
+ %else %if &headerformat=SASJS %then %do;
121
+ if vartype(dsid,i)='C' then header=cats(varnm,':$char',varlen(dsid,i),'.');
122
+ else do;
123
+ vfmt=coalescec(varfmt(dsid,i),'0');
124
+ fmttype=mcf_getfmttype(vfmt);
125
+ if fmttype='DATE' then header=cats(varnm,':date9.');
126
+ else if fmttype='DATETIME' then header=cats(varnm,':E8601DT26.6');
127
+ else if fmttype='TIME' then header=cats(varnm,':TIME12.');
128
+ else header=cats(varnm,':best.');
129
+ end;
130
+ %end;
131
+ %else %do;
132
+ %put &sysmacroname: Invalid headerformat value (&headerformat);
133
+ %return;
134
+ %end;
135
+ put header @;
136
+ end;
137
+ rc=close(dsid);
138
+ run;
139
+
140
+ %let varlist=%mf_getvarlist(&ds);
141
+ %let vcnt=%sysfunc(countw(&varlist));
142
+
143
+ /**
144
+ * The $quote modifier (without a width) will take the length from the variable
145
+ * and increase by two. However this will lead to truncation where the value
146
+ * contains double quotes (which are doubled up). To get around this, scan the
147
+ * data to see the max number of double quotes, so that the appropriate width
148
+ * can be applied in the subsequent step.
149
+ */
150
+ data _null_;
151
+ set &ds end=last;
152
+ %do i=1 %to &vcnt;
153
+ %let var=%scan(&varlist,&i);
154
+ %if %mf_getvartype(&ds,&var)=C %then %do;
155
+ %let dsv1=%mf_getuniquename(prefix=csvcol1_);
156
+ %let dsv2=%mf_getuniquename(prefix=csvcol2_);
157
+ retain &dsv1 0;
158
+ &dsv2=length(&var)+countc(&var,'"');
159
+ if &dsv2>&dsv1 then &dsv1=&dsv2;
160
+ if last then call symputx(
161
+ "vlen&i"
162
+ /* should be no shorter than varlen, and no longer than 32767 */
163
+ ,cats('$quote',min(&dsv1+2,32767),'.')
164
+ ,'l'
165
+ );
166
+ %end;
167
+ %end;
168
+
169
+ %let vat=@;
170
+ %let vcom=&delim;
171
+ %let vmiss=%mf_getuniquename(prefix=csvcol3_);
172
+ /* next, export data */
173
+ data _null_;
174
+ set &ds.;
175
+ file &outloc mod dlm=&delim dsd &outencoding lrecl=32767 termstr=&termstr;
176
+ if _n_=1 then &vmiss=' ';
177
+ %do i=1 %to &vcnt;
178
+ %let var=%scan(&varlist,&i);
179
+ %if &i=&vcnt %then %do;
180
+ %let vat=;
181
+ %let vcom=;
182
+ %end;
183
+ %if %mf_getvartype(&ds,&var)=N %then %do;
184
+ %if &headerformat = SASJS %then %do;
185
+ %let vcom=&delim;
186
+ %let fmttype=%sysfunc(mcf_getfmttype(%mf_getvarformat(&ds,&var)0));
187
+ %if &fmttype=DATE %then %let vfmt=DATE9.;
188
+ %else %if &fmttype=DATETIME %then %let vfmt=E8601DT26.6;
189
+ %else %if &fmttype=TIME %then %let vfmt=TIME12.;
190
+ %else %do;
191
+ %let vfmt=;
192
+ %let vcom=;
193
+ %end;
194
+ %end;
195
+ %else %let vcom=;
196
+
197
+ /* must use period - in order to work in both 9.4 and Viya 3.5 */
198
+ if missing(&var) and &var ne %sysfunc(getoption(MISSING)) then do;
199
+ &vmiss=cats('.',&var);
200
+ put &vmiss &vat;
201
+ end;
202
+ else put &var &vfmt &vcom &vat;
203
+
204
+ %end;
205
+ %else %do;
206
+ %if &i ne &vcnt %then %let vcom=&delim;
207
+ put &var &&vlen&i &vcom &vat;
208
+ %end;
209
+ %end;
210
+ run;
211
+
212
+ %mend mp_ds2csv;
@@ -0,0 +1,30 @@
1
+ /**
2
+ @file
3
+ @brief A wrapper for mp_getddl.sas
4
+ @details In the next release, this will be the main version.
5
+
6
+
7
+ <h4> SAS Macros </h4>
8
+ @li mp_getddl.sas
9
+
10
+ **/
11
+
12
+ %macro mp_ds2ddl(libds,fref=getddl,flavour=SAS,showlog=YES,schema=
13
+ ,applydttm=NO
14
+ )/*/STORE SOURCE*/;
15
+
16
+ %local libref;
17
+ %let libds=%upcase(&libds);
18
+ %let libref=%scan(&libds,1,.);
19
+ %if &libref=&libds %then %let libds=WORK.&libds;
20
+
21
+ %mp_getddl(%scan(&libds,1,.)
22
+ ,%scan(&libds,2,.)
23
+ ,fref=&fref
24
+ ,flavour=SAS
25
+ ,showlog=&showlog
26
+ ,schema=&schema
27
+ ,applydttm=&applydttm
28
+ )
29
+
30
+ %mend mp_ds2ddl;
@@ -0,0 +1,105 @@
1
+ /**
2
+ @file
3
+ @brief Converts every value in a dataset to formatted value
4
+ @details Converts every value to it's formatted value. All variables will
5
+ become character, and will be in the same order as the original dataset.
6
+
7
+ Lengths will be adjusted according to the format lengths, where applicable.
8
+
9
+ Usage:
10
+
11
+ %mp_ds2fmtds(sashelp.cars,work.cars)
12
+ %mp_ds2fmtds(sashelp.vallopt,vw_vallopt)
13
+
14
+ @param [in] libds The library.dataset to be converted
15
+ @param [out] outds The dataset to create.
16
+
17
+ <h4> SAS Macros </h4>
18
+ @li mf_existds.sas
19
+
20
+ <h4> Related Macros </h4>
21
+ @li mp_jsonout.sas
22
+
23
+ @version 9.2
24
+ @author Allan Bowe
25
+ **/
26
+
27
+ %macro mp_ds2fmtds(libds, outds
28
+ )/*/STORE SOURCE*/;
29
+
30
+ /* validations */
31
+
32
+ %if not %mf_existds(libds=&libds) %then %do;
33
+ %put %str(WARN)ING: &libds does not exist as either a VIEW or DATASET;
34
+ %return;
35
+ %end;
36
+ %if %index(&libds,.)=0 %then %let libds=WORK.&libds;
37
+
38
+ /* grab metadata */
39
+ proc contents noprint data=&libds
40
+ out=_data_(keep=name type length format formatl formatd varnum);
41
+ run;
42
+ proc sort;
43
+ by varnum;
44
+ run;
45
+
46
+ /* prepare formats and varnames */
47
+ data _null_;
48
+ set &syslast end=last;
49
+ name=upcase(name);
50
+ /* fix formats */
51
+ if type=2 or type=6 then do;
52
+ length fmt $49.;
53
+ if format='' then fmt=cats('$',length,'.');
54
+ else if formatl=0 then fmt=cats(format,'.');
55
+ else fmt=cats(format,formatl,'.');
56
+ newlen=max(formatl,length);
57
+ end;
58
+ else do;
59
+ if format='' then fmt='best.';
60
+ else if formatl=0 then fmt=cats(format,'.');
61
+ else if formatd=0 then fmt=cats(format,formatl,'.');
62
+ else fmt=cats(format,formatl,'.',formatd);
63
+ /* needs to be wide, for datetimes etc */
64
+ newlen=max(length,formatl,24);
65
+ end;
66
+ /* 32 char unique name */
67
+ newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27);
68
+
69
+ call symputx(cats('name',_n_),name,'l');
70
+ call symputx(cats('newname',_n_),newname,'l');
71
+ call symputx(cats('len',_n_),newlen,'l');
72
+ call symputx(cats('fmt',_n_),fmt,'l');
73
+ call symputx(cats('type',_n_),type,'l');
74
+ if last then call symputx('nobs',_n_,'l');
75
+ run;
76
+
77
+ /* clean up */
78
+ proc sql;
79
+ drop table &syslast;
80
+
81
+ %if &nobs=0 %then %do;
82
+ %put Dataset &libds has no columns!
83
+ data &outds;
84
+ set &libds;
85
+ run;
86
+ %return;
87
+ %end;
88
+
89
+ data &outds;
90
+ /* rename on entry */
91
+ set &libds(rename=(
92
+ %local i;
93
+ %do i=1 %to &nobs;
94
+ &&name&i=&&newname&i
95
+ %end;
96
+ ));
97
+ %do i=1 %to &nobs;
98
+ length &&name&i $&&len&i;
99
+ &&name&i=left(put(&&newname&i,&&fmt&i));
100
+ drop &&newname&i;
101
+ %end;
102
+ if _error_ then call symputx('syscc',1012);
103
+ run;
104
+
105
+ %mend mp_ds2fmtds;