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
46 <h4> Related Macros </h4>
47 @li ms_webout.sas
48 @li mv_webout.sas
49
50 @version 9.3
51 @author Allan Bowe
52
53**/
54%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
55 ,showmeta=N,maxobs=MAX,workobs=0
56);
57%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
58 sasjs_tables;
59%local i tempds jsonengine;
60
61/* see https://github.com/sasjs/core/issues/41 */
62%if "%upcase(&SYSENCODING)" ne "UTF-8" %then %let jsonengine=PROCJSON;
63%else %let jsonengine=DATASTEP;
64
65
66%if &action=FETCH %then %do;
67 %if %str(&_debug) ge 131 %then %do;
68 options mprint notes mprintnest;
69 %end;
70 %let _webin_file_count=%eval(&_webin_file_count+0);
71 /* now read in the data */
72 %do i=1 %to &_webin_file_count;
73 %if &_webin_file_count=1 %then %do;
74 %let _webin_fileref1=&_webin_fileref;
75 %let _webin_name1=&_webin_name;
76 %end;
77 data _null_;
78 infile &&_webin_fileref&i termstr=crlf;
79 input;
80 /* a plain $ informat strips leading blanks - use $char instead */
81 call symputx('input_statement'
82 ,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
83 );
84 putlog "&&_webin_name&i input statement: " _infile_;
85 stop;
86 data &&_webin_name&i;
87 infile &&_webin_fileref&i firstobs=2 dsd termstr=crlf encoding='utf-8';
88 input &input_statement;
89 %if %str(&_debug) ge 131 %then %do;
90 if _n_<20 then putlog _infile_;
91 %end;
92 run;
93 %let sasjs_tables=&sasjs_tables &&_webin_name&i;
94 %end;
95%end;
96
97%else %if &action=OPEN %then %do;
98 /* fix encoding */
99 OPTIONS NOBOMFILE;
100
101 /**
102 * check xengine type to avoid the below err message:
103 * > Function is only valid for filerefs using the CACHE access method.
104 */
105 data _null_;
106 set sashelp.vextfl(where=(fileref="_WEBOUT"));
107 if xengine='STREAM' then do;
108 rc=stpsrv_header('Content-type',"text/html; encoding=utf-8");
109 end;
110 run;
111
112 /* setup json */
113 data _null_;file &fref encoding='utf-8';
114 %if %str(&_debug) ge 131 %then %do;
115 put '>>weboutBEGIN<<';
116 %end;
117 put '{"SYSDATE" : "' "&SYSDATE" '"';
118 put ',"SYSTIME" : "' "&SYSTIME" '"';
119 run;
120
121%end;
122
123%else %if &action=ARR or &action=OBJ %then %do;
124 %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
125 ,engine=&jsonengine,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
126 )
127%end;
128%else %if &action=CLOSE %then %do;
129 /* To avoid issues with _webout on EBI we use a temporary file */
130 filename _sjsref temp lrecl=131068;
131 %if %str(&workobs) > 0 %then %do;
132 /* if debug mode, send back first XX records of each work table also */
133 data;run;%let tempds=%scan(&syslast,2,.);
134 ods output Members=&tempds;
135 proc datasets library=WORK memtype=data;
136 %local wtcnt;%let wtcnt=0;
137 data _null_;
138 set &tempds;
139 if not (upcase(name) =:"DATA"); /* ignore temp datasets */
140 i+1;
141 call symputx(cats('wt',i),name,'l');
142 call symputx('wtcnt',i,'l');
143 data _null_; file _sjsref mod encoding='utf-8';
144 put ",""WORK"":{";
145 %do i=1 %to &wtcnt;
146 %let wt=&&wt&i;
147 data _null_; file _sjsref mod encoding='utf-8';
148 dsid=open("WORK.&wt",'is');
149 nlobs=attrn(dsid,'NLOBS');
150 nvars=attrn(dsid,'NVARS');
151 rc=close(dsid);
152 if &i>1 then put ','@;
153 put " ""&wt"" : {";
154 put '"nlobs":' nlobs;
155 put ',"nvars":' nvars;
156 %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y
157 ,maxobs=&workobs
158 )
159 data _null_; file _sjsref mod encoding='utf-8';
160 put "}";
161 %end;
162 data _null_; file _sjsref mod encoding='utf-8';
163 put "}";
164 run;
165 %end;
166 /* close off json */
167 data _null_;file _sjsref mod encoding='utf-8';
168 length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
169 put ",""_DEBUG"" : ""&_debug"" ";
170 _METAUSER=quote(trim(symget('_METAUSER')));
171 put ",""_METAUSER"": " _METAUSER;
172 _METAPERSON=quote(trim(symget('_METAPERSON')));
173 put ',"_METAPERSON": ' _METAPERSON;
174 _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
175 put ',"_PROGRAM" : ' _PROGRAM ;
176 autoexec=quote(urlencode(trim(getoption('autoexec'))));
177 put ',"AUTOEXEC" : ' autoexec;
178 put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
179 put ",""SYSCC"" : ""&syscc"" ";
180 put ",""SYSENCODING"" : ""&sysencoding"" ";
181 syserrortext=cats(symget('syserrortext'));
182 if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
183 syserrortext='"'!!trim(
184 prxchange('s/"/\\"/',-1, /* double quote */
185 prxchange('s/\x0A/\n/',-1, /* new line */
186 prxchange('s/\x0D/\r/',-1, /* carriage return */
187 prxchange('s/\x09/\\t/',-1, /* tab */
188 prxchange('s/\x00/\\u0000/',-1, /* NUL */
189 prxchange('s/\x0E/\\u000E/',-1, /* SS */
190 prxchange('s/\x0F/\\u000F/',-1, /* SF */
191 prxchange('s/\x01/\\u0001/',-1, /* SOH */
192 prxchange('s/\x02/\\u0002/',-1, /* STX */
193 prxchange('s/\x10/\\u0010/',-1, /* DLE */
194 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
195 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
196 prxchange('s/\\/\\\\/',-1,syserrortext)
197 )))))))))))))!!'"';
198 end;
199 else syserrortext=cats('"',syserrortext,'"');
200 put ',"SYSERRORTEXT" : ' syserrortext;
201 put ",""SYSHOSTNAME"" : ""&syshostname"" ";
202 put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
203 put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
204 SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
205 put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
206 put ",""SYSJOBID"" : ""&sysjobid"" ";
207 put ",""SYSSCPL"" : ""&sysscpl"" ";
208 put ",""SYSSITE"" : ""&syssite"" ";
209 put ",""SYSUSERID"" : ""&sysuserid"" ";
210 sysvlong=quote(trim(symget('sysvlong')));
211 put ',"SYSVLONG" : ' sysvlong;
212 syswarningtext=cats(symget('syswarningtext'));
213 if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
214 syswarningtext='"'!!trim(
215 prxchange('s/"/\\"/',-1, /* double quote */
216 prxchange('s/\x0A/\n/',-1, /* new line */
217 prxchange('s/\x0D/\r/',-1, /* carriage return */
218 prxchange('s/\x09/\\t/',-1, /* tab */
219 prxchange('s/\x00/\\u0000/',-1, /* NUL */
220 prxchange('s/\x0E/\\u000E/',-1, /* SS */
221 prxchange('s/\x0F/\\u000F/',-1, /* SF */
222 prxchange('s/\x01/\\u0001/',-1, /* SOH */
223 prxchange('s/\x02/\\u0002/',-1, /* STX */
224 prxchange('s/\x10/\\u0010/',-1, /* DLE */
225 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
226 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
227 prxchange('s/\\/\\\\/',-1,syswarningtext)
228 )))))))))))))!!'"';
229 end;
230 else syswarningtext=cats('"',syswarningtext,'"');
231 put ',"SYSWARNINGTEXT" : ' syswarningtext;
232 put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
233 length memsize $32;
234 memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
235 memsize=quote(cats(memsize));
236 put ',"MEMSIZE" : ' memsize;
237 put "}" @;
238 %if %str(&_debug) ge 131 %then %do;
239 put '>>weboutEND<<';
240 %end;
241 run;
242 /* now write to _webout 1 char at a time */
243 data _null_;
244 infile _sjsref lrecl=1 recfm=n;
245 file &fref mod lrecl=1 recfm=n;
246 input sourcechar $char1. @@;
247 format sourcechar hex2.;
248 put sourcechar char1. @@;
249 run;
250 filename _sjsref clear;
251
252%end;
253
254%mend mm_webout;