Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mm_webout.sas
Go to the documentation of this file.
1/**
2 @file mm_webout.sas
3 @brief Send data to/from SAS Stored Processes
4 @details This macro should be added to the start of each Stored Process,
5 **immediately** followed by a call to:
6
7 %mm_webout(FETCH)
8
9 This will read all the input data and create same-named SAS datasets in the
10 WORK library. You can then insert your code, and send data back using the
11 following syntax:
12
13 data some datasets; * make some data ;
14 retain some columns;
15 run;
16
17 %mm_webout(OPEN)
18 %mm_webout(ARR,some) * Array format, fast, suitable for large tables ;
19 %mm_webout(OBJ,datasets) * Object format, easier to work with ;
20
21 Finally, wrap everything up send some helpful system variables too
22
23 %mm_webout(CLOSE)
24
25
26 @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
27 @param [in] ds The dataset to send back to the frontend
28 @param [out] dslabel= Value to use instead of table name for sending to JSON
29 @param [in] fmt= (N) Setting Y converts all vars to their formatted values
30 @param [out] fref= (_webout) The fileref to which to write the JSON
31 @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
32 (eg `null`) or as STRING values (eg `".a"` or `".b"`)
33 @param [in] showmeta= (N) Set to Y to output metadata alongside each table,
34 such as the column formats and types. The metadata is contained inside an
35 object with the same name as the table but prefixed with a dollar sign - ie,
36 `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
37 @param [in] workobs= (0) When set to a positive integer, will create a new
38 output object (WORK) which contains this number of observations from all
39 tables in the WORK library.
40 @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
41 that should be converted to output JSON
42
43 <h4> SAS Macros </h4>
44 @li mp_jsonout.sas
45 @li mf_getuser.sas
46
47 <h4> Related Macros </h4>
48 @li ms_webout.sas
49 @li mv_webout.sas
50
51 @version 9.3
52 @author Allan Bowe
53
54**/
55%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
56 ,showmeta=N,maxobs=MAX,workobs=0
57);
58%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
59 sasjs_tables;
60%local i tempds jsonengine;
61
62/* see https://github.com/sasjs/core/issues/41 */
63%if "%upcase(&SYSENCODING)" ne "UTF-8" %then %let jsonengine=PROCJSON;
64%else %let jsonengine=DATASTEP;
65
66
67%if &action=FETCH %then %do;
68 %if %str(&_debug) ge 131 %then %do;
69 options mprint notes mprintnest;
70 %end;
71 %let _webin_file_count=%eval(&_webin_file_count+0);
72 /* now read in the data */
73 %do i=1 %to &_webin_file_count;
74 %if &_webin_file_count=1 %then %do;
75 %let _webin_fileref1=&_webin_fileref;
76 %let _webin_name1=&_webin_name;
77 %end;
78 data _null_;
79 infile &&_webin_fileref&i termstr=crlf;
80 input;
81 /* a plain $ informat strips leading blanks - use $char instead */
82 call symputx('input_statement'
83 ,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
84 );
85 putlog "&&_webin_name&i input statement: " _infile_;
86 stop;
87 data &&_webin_name&i;
88 infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8';
89 input &input_statement;
90 %if %str(&_debug) ge 131 %then %do;
91 if _n_<20 then putlog _infile_;
92 %end;
93 run;
94 %let sasjs_tables=&sasjs_tables &&_webin_name&i;
95 %end;
96%end;
97
98%else %if &action=OPEN %then %do;
99 /* fix encoding */
100 OPTIONS NOBOMFILE;
101
102 /**
103 * check xengine type to avoid the below err message:
104 * > Function is only valid for filerefs using the CACHE access method.
105 */
106 data _null_;
107 set sashelp.vextfl(where=(fileref="_WEBOUT"));
108 if xengine='STREAM' then do;
109 rc=stpsrv_header('Content-type',"text/html; encoding=utf-8");
110 end;
111 run;
112
113 /* setup json */
114 data _null_;file &fref encoding='utf-8';
115 %if %str(&_debug) ge 131 %then %do;
116 put '>>weboutBEGIN<<';
117 %end;
118 put '{"SYSDATE" : "' "&SYSDATE" '"';
119 put ',"SYSTIME" : "' "&SYSTIME" '"';
120 run;
121
122%end;
123
124%else %if &action=ARR or &action=OBJ %then %do;
125 %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
126 ,engine=&jsonengine,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
127 )
128%end;
129%else %if &action=CLOSE %then %do;
130 /* To avoid issues with _webout on EBI we use a temporary file */
131 filename _sjsref temp lrecl=131068;
132 %if %str(&workobs) > 0 %then %do;
133 /* if debug mode, send back first XX records of each work table also */
134 data;run;%let tempds=%scan(&syslast,2,.);
135 ods output Members=&tempds;
136 proc datasets library=WORK memtype=data;
137 %local wtcnt;%let wtcnt=0;
138 data _null_;
139 set &tempds;
140 if not (upcase(name) =:"DATA"); /* ignore temp datasets */
141 i+1;
142 call symputx(cats('wt',i),name,'l');
143 call symputx('wtcnt',i,'l');
144 data _null_; file _sjsref mod encoding='utf-8';
145 put ",""WORK"":{";
146 %do i=1 %to &wtcnt;
147 %let wt=&&wt&i;
148 data _null_; file _sjsref mod encoding='utf-8';
149 dsid=open("WORK.&wt",'is');
150 nlobs=attrn(dsid,'NLOBS');
151 nvars=attrn(dsid,'NVARS');
152 rc=close(dsid);
153 if &i>1 then put ','@;
154 put " ""&wt"" : {";
155 put '"nlobs":' nlobs;
156 put ',"nvars":' nvars;
157 %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y
158 ,maxobs=&workobs
159 )
160 data _null_; file _sjsref mod encoding='utf-8';
161 put "}";
162 %end;
163 data _null_; file _sjsref mod encoding='utf-8';
164 put "}";
165 run;
166 %end;
167 /* close off json */
168 data _null_;file _sjsref mod encoding='utf-8';
169 length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
170 put ",""_DEBUG"" : ""&_debug"" ";
171 _METAUSER=quote(trim(symget('_METAUSER')));
172 put ",""_METAUSER"": " _METAUSER;
173 _METAPERSON=quote(trim(symget('_METAPERSON')));
174 put ',"_METAPERSON": ' _METAPERSON;
175 _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
176 put ',"_PROGRAM" : ' _PROGRAM ;
177 autoexec=quote(urlencode(trim(getoption('autoexec'))));
178 put ',"AUTOEXEC" : ' autoexec;
179 put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
180 put ",""SYSCC"" : ""&syscc"" ";
181 put ",""SYSENCODING"" : ""&sysencoding"" ";
182 syserrortext=cats(symget('syserrortext'));
183 if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
184 syserrortext='"'!!trim(
185 prxchange('s/"/\\"/',-1, /* double quote */
186 prxchange('s/\x0A/\n/',-1, /* new line */
187 prxchange('s/\x0D/\r/',-1, /* carriage return */
188 prxchange('s/\x09/\\t/',-1, /* tab */
189 prxchange('s/\x00/\\u0000/',-1, /* NUL */
190 prxchange('s/\x0E/\\u000E/',-1, /* SS */
191 prxchange('s/\x0F/\\u000F/',-1, /* SF */
192 prxchange('s/\x01/\\u0001/',-1, /* SOH */
193 prxchange('s/\x02/\\u0002/',-1, /* STX */
194 prxchange('s/\x10/\\u0010/',-1, /* DLE */
195 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
196 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
197 prxchange('s/\\/\\\\/',-1,syserrortext)
198 )))))))))))))!!'"';
199 end;
200 else syserrortext=cats('"',syserrortext,'"');
201 put ',"SYSERRORTEXT" : ' syserrortext;
202 put ",""SYSHOSTNAME"" : ""&syshostname"" ";
203 put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
204 put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
205 SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
206 put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
207 put ",""SYSJOBID"" : ""&sysjobid"" ";
208 put ",""SYSSCPL"" : ""&sysscpl"" ";
209 put ",""SYSSITE"" : ""&syssite"" ";
210 put ",""SYSUSERID"" : ""&sysuserid"" ";
211 sysvlong=quote(trim(symget('sysvlong')));
212 put ',"SYSVLONG" : ' sysvlong;
213 syswarningtext=cats(symget('syswarningtext'));
214 if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
215 syswarningtext='"'!!trim(
216 prxchange('s/"/\\"/',-1, /* double quote */
217 prxchange('s/\x0A/\n/',-1, /* new line */
218 prxchange('s/\x0D/\r/',-1, /* carriage return */
219 prxchange('s/\x09/\\t/',-1, /* tab */
220 prxchange('s/\x00/\\u0000/',-1, /* NUL */
221 prxchange('s/\x0E/\\u000E/',-1, /* SS */
222 prxchange('s/\x0F/\\u000F/',-1, /* SF */
223 prxchange('s/\x01/\\u0001/',-1, /* SOH */
224 prxchange('s/\x02/\\u0002/',-1, /* STX */
225 prxchange('s/\x10/\\u0010/',-1, /* DLE */
226 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
227 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
228 prxchange('s/\\/\\\\/',-1,syswarningtext)
229 )))))))))))))!!'"';
230 end;
231 else syswarningtext=cats('"',syswarningtext,'"');
232 put ',"SYSWARNINGTEXT" : ' syswarningtext;
233 put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
234 length memsize $32;
235 memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
236 memsize=quote(cats(memsize));
237 put ',"MEMSIZE" : ' memsize;
238 put "}" @;
239 %if %str(&_debug) ge 131 %then %do;
240 put '>>weboutEND<<';
241 %end;
242 run;
243 /* now write to _webout 1 char at a time */
244 data _null_;
245 infile _sjsref lrecl=1 recfm=n;
246 file &fref mod lrecl=1 recfm=n;
247 input sourcechar $char1. @@;
248 format sourcechar hex2.;
249 put sourcechar char1. @@;
250 run;
251 filename _sjsref clear;
252
253%end;
254
255%mend mm_webout;