Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mv_webout.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief Send data to/from the SAS Viya Job Execution Service
4 @details This macro should be added to the start of each Job Execution
5 Service, **immediately** followed by a call to:
6
7 %mv_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 %mv_webout(OPEN)
18 %mv_webout(ARR,some) * Array format, fast, suitable for large tables ;
19 %mv_webout(OBJ,datasets) * Object format, easier to work with ;
20 %mv_webout(CLOSE)
21
22
23 @param [in] action Either OPEN, ARR, OBJ or CLOSE
24 @param [in] ds The dataset to send back to the frontend
25 @param [in] _webout= fileref for returning the json
26 @param [out] fref=(_mvwtemp) Temp fileref to which to write the output
27 @param [out] dslabel= value to use instead of table name for sending to JSON
28 @param [in] fmt= (N) Setting Y converts all vars to their formatted values
29 @param [in] stream=(Y) Change to N if not streaming to _webout
30 @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
31 (eg `null`) or as STRING values (eg `".a"` or `".b"`)
32 @param [in] showmeta= (N) Set to Y to output metadata alongside each table,
33 such as the column formats and types. The metadata is contained inside an
34 object with the same name as the table but prefixed with a dollar sign - ie,
35 `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
36 @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
37 that should be converted to output JSON
38 @param [in] workobs= (0) When set to a positive integer, will create a new
39 output object (WORK) which contains this number of observations from all
40 tables in the WORK library.
41
42 <h4> SAS Macros </h4>
43 @li mp_jsonout.sas
44 @li mf_getuser.sas
45
46 <h4> Related Macros </h4>
47 @li ms_webout.sas
48 @li mm_webout.sas
49
50 @version Viya 3.3
51 @author Allan Bowe, source: https://github.com/sasjs/core
52
53**/
54%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL
55 ,showmeta=N,maxobs=MAX,workobs=0
56);
57%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
58 sasjs_tables SYS_JES_JOB_URI _EXECUTIONTASKS;
59%if %index("&_debug",log) %then %let _debug=128;
60
61%local i tempds table;
62%let action=%upcase(&action);
63
64%if &action=FETCH %then %do;
65 %if %upcase(&_omittextlog)=FALSE or %str(&_debug) ge 128 %then %do;
66 options mprint notes mprintnest;
67 %end;
68
69 %if not %symexist(_webin_fileuri1) %then %do;
70 %let _webin_file_count=%eval(&_webin_file_count+0);
71 %let _webin_fileuri1=&_webin_fileuri;
72 %let _webin_name1=&_webin_name;
73 %if &_EXECUTIONTASKS=true %then %do;
74 /* TODO - remove this once SAS Track CS0409737 is resolved */
75 /* links: https://github.com/sasjs/adapter/issues/884 */
76 %if %upcase(&_webin_name)=_SASJS_NOOP %then %let _webin_file_count=0;
77 %end;
78 %end;
79
80 /* if the sasjs_tables param is passed, we expect param based upload */
81 %if %length(&sasjs_tables.X)>1 %then %do;
82
83 /* convert data from macro variables to datasets */
84 %do i=1 %to %sysfunc(countw(&sasjs_tables));
85 %let table=%scan(&sasjs_tables,&i,%str( ));
86 %if %symexist(sasjs&i.data0)=0 %then %let sasjs&i.data0=1;
87 data _null_;
88 file "%sysfunc(pathname(work))/&table..csv" recfm=n;
89 retain nrflg 0;
90 length line $32767;
91 do i=1 to &&sasjs&i.data0;
92 if &&sasjs&i.data0=1 then line=symget("sasjs&i.data");
93 else line=symget(cats("sasjs&i.data",i));
94 if i=1 and substr(line,1,7)='%nrstr(' then do;
95 nrflg=1;
96 line=substr(line,8);
97 end;
98 if i=&&sasjs&i.data0 and nrflg=1 then do;
99 line=substr(line,1,length(line)-1);
100 end;
101 put line +(-1) @;
102 end;
103 run;
104 data _null_;
105 infile "%sysfunc(pathname(work))/&table..csv" termstr=crlf ;
106 input;
107 if _n_=1 then call symputx('input_statement',_infile_);
108 list;
109 data work.&table;
110 infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd
111 termstr=crlf;
112 input &input_statement;
113 run;
114 %end;
115 %end;
116 %else %do i=1 %to &_webin_file_count;
117 /* read in any files that are sent */
118 %if &_EXECUTIONTASKS=true %then %do;
119 filename indata "%sysfunc(pathname(&&_webin_fileref&i))" lrecl=999999;
120 %end;
121 %else %do;
122 filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999;
123 %end;
124 data _null_;
125 infile indata termstr=crlf lrecl=32767;
126 input;
127 if _n_=1 then call symputx('input_statement',_infile_);
128 %if %str(&_debug) ge 128 %then %do;
129 if _n_<20 then putlog _infile_;
130 else stop;
131 %end;
132 %else %do;
133 stop;
134 %end;
135 run;
136 data &&_webin_name&i;
137 infile indata firstobs=2 dsd termstr=crlf ;
138 input &input_statement;
139 run;
140 %let sasjs_tables=&sasjs_tables &&_webin_name&i;
141 %end;
142%end;
143%else %if &action=OPEN %then %do;
144 /* setup webout */
145 OPTIONS NOBOMFILE;
146 %if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
147 filename _webout temp lrecl=999999 mod;
148 %end;
149 %else %do;
150 filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
151 name="_webout.json" lrecl=999999 mod;
152 %end;
153
154 /* setup temp ref */
155 %if %upcase(&fref) ne _WEBOUT %then %do;
156 filename &fref temp lrecl=999999 permission='A::u::rwx,A::g::rw-,A::o::---';
157 %end;
158
159 /* setup json */
160 data _null_;file &fref;
161 %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
162 put '>>weboutBEGIN<<';
163 %end;
164 put '{"SYSDATE" : "' "&SYSDATE" '"';
165 put ',"SYSTIME" : "' "&SYSTIME" '"';
166 run;
167%end;
168%else %if &action=ARR or &action=OBJ %then %do;
169 %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
170 ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
171 )
172%end;
173%else %if &action=CLOSE %then %do;
174 %if %str(&workobs) > 0 %then %do;
175 /* send back first XX records of each work table for debugging */
176 data;run;%let tempds=%scan(&syslast,2,.);
177 ods output Members=&tempds;
178 proc datasets library=WORK memtype=data;
179 %local wtcnt;%let wtcnt=0;
180 data _null_;
181 set &tempds;
182 if not (upcase(name) =:"DATA"); /* ignore temp datasets */
183 i+1;
184 call symputx(cats('wt',i),name,'l');
185 call symputx('wtcnt',i,'l');
186 data _null_; file &fref mod; put ",""WORK"":{";
187 %do i=1 %to &wtcnt;
188 %let wt=&&wt&i;
189 data _null_; file &fref mod;
190 dsid=open("WORK.&wt",'is');
191 nlobs=attrn(dsid,'NLOBS');
192 nvars=attrn(dsid,'NVARS');
193 rc=close(dsid);
194 if &i>1 then put ','@;
195 put " ""&wt"" : {";
196 put '"nlobs":' nlobs;
197 put ',"nvars":' nvars;
198 %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
199 ,maxobs=&workobs
200 )
201 data _null_; file &fref mod;put "}";
202 %end;
203 data _null_; file &fref mod;put "}";run;
204 %end;
205
206 /* close off json */
207 data _null_;file &fref mod;
208 length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
209 put ",""_DEBUG"" : ""&_debug"" ";
210 _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
211 put ',"_PROGRAM" : ' _PROGRAM ;
212 autoexec=quote(urlencode(trim(getoption('autoexec'))));
213 put ',"AUTOEXEC" : ' autoexec;
214 put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
215 SYS_JES_JOB_URI=quote(trim(resolve(symget('SYS_JES_JOB_URI'))));
216 put ',"SYS_JES_JOB_URI" : ' SYS_JES_JOB_URI ;
217 put ",""SYSJOBID"" : ""&sysjobid"" ";
218 put ",""SYSCC"" : ""&syscc"" ";
219 syserrortext=cats(symget('syserrortext'));
220 if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
221 syserrortext='"'!!trim(
222 prxchange('s/"/\\"/',-1, /* double quote */
223 prxchange('s/\x0A/\n/',-1, /* new line */
224 prxchange('s/\x0D/\r/',-1, /* carriage return */
225 prxchange('s/\x09/\\t/',-1, /* tab */
226 prxchange('s/\x00/\\u0000/',-1, /* NUL */
227 prxchange('s/\x0E/\\u000E/',-1, /* SS */
228 prxchange('s/\x0F/\\u000F/',-1, /* SF */
229 prxchange('s/\x01/\\u0001/',-1, /* SOH */
230 prxchange('s/\x02/\\u0002/',-1, /* STX */
231 prxchange('s/\x10/\\u0010/',-1, /* DLE */
232 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
233 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
234 prxchange('s/\\/\\\\/',-1,syserrortext)
235 )))))))))))))!!'"';
236 end;
237 else syserrortext=cats('"',syserrortext,'"');
238 put ',"SYSERRORTEXT" : ' syserrortext;
239 put ",""SYSHOSTNAME"" : ""&syshostname"" ";
240 put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
241 put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
242 SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
243 put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
244 put ",""SYSJOBID"" : ""&sysjobid"" ";
245 put ",""SYSSCPL"" : ""&sysscpl"" ";
246 put ",""SYSSITE"" : ""&syssite"" ";
247 put ",""SYSUSERID"" : ""&sysuserid"" ";
248 sysvlong=quote(trim(symget('sysvlong')));
249 put ',"SYSVLONG" : ' sysvlong;
250 syswarningtext=cats(symget('syswarningtext'));
251 if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
252 syswarningtext='"'!!trim(
253 prxchange('s/"/\\"/',-1, /* double quote */
254 prxchange('s/\x0A/\n/',-1, /* new line */
255 prxchange('s/\x0D/\r/',-1, /* carriage return */
256 prxchange('s/\x09/\\t/',-1, /* tab */
257 prxchange('s/\x00/\\u0000/',-1, /* NUL */
258 prxchange('s/\x0E/\\u000E/',-1, /* SS */
259 prxchange('s/\x0F/\\u000F/',-1, /* SF */
260 prxchange('s/\x01/\\u0001/',-1, /* SOH */
261 prxchange('s/\x02/\\u0002/',-1, /* STX */
262 prxchange('s/\x10/\\u0010/',-1, /* DLE */
263 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
264 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
265 prxchange('s/\\/\\\\/',-1,syswarningtext)
266 )))))))))))))!!'"';
267 end;
268 else syswarningtext=cats('"',syswarningtext,'"');
269 put ',"SYSWARNINGTEXT" : ' syswarningtext;
270 put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
271 length memsize $32;
272 memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
273 memsize=quote(cats(memsize));
274 put ',"MEMSIZE" : ' memsize;
275 put "}";
276 %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
277 put '>>weboutEND<<';
278 %end;
279 %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;
280 data _null_; rc=fcopy("&fref","_webout");run;
281 %end;
282
283%end;
284
285%mend mv_webout;