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 /* a plain $ informat strips leading blanks - use $char instead */
108 if _n_=1 then call symputx('input_statement'
109 ,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
110 );
111 list;
112 data work.&table;
113 infile "%sysfunc(pathname(work))/&table..csv" firstobs=2 dsd
114 termstr=crlf;
115 input &input_statement;
116 run;
117 %end;
118 %end;
119 %else %do i=1 %to &_webin_file_count;
120 /* read in any files that are sent */
121 %if &_EXECUTIONTASKS=true %then %do;
122 filename indata "%sysfunc(pathname(&&_webin_fileref&i))" lrecl=999999;
123 %end;
124 %else %do;
125 filename indata filesrvc "&&_webin_fileuri&i" lrecl=999999;
126 %end;
127 data _null_;
128 infile indata termstr=crlf lrecl=32767;
129 input;
130 /* a plain $ informat strips leading blanks - use $char instead */
131 if _n_=1 then call symputx('input_statement'
132 ,prxchange('s/:\$(?=[0-9 ])/:\$char/i',-1,_infile_)
133 );
134 %if %str(&_debug) ge 128 %then %do;
135 if _n_<20 then putlog _infile_;
136 else stop;
137 %end;
138 %else %do;
139 stop;
140 %end;
141 run;
142 data &&_webin_name&i;
143 infile indata firstobs=2 dsd termstr=crlf ;
144 input &input_statement;
145 run;
146 %let sasjs_tables=&sasjs_tables &&_webin_name&i;
147 %end;
148%end;
149%else %if &action=OPEN %then %do;
150 /* setup webout */
151 OPTIONS NOBOMFILE;
152 %if "X&SYS_JES_JOB_URI.X"="XX" %then %do;
153 filename _webout temp lrecl=999999 mod;
154 %end;
155 %else %do;
156 filename _webout filesrvc parenturi="&SYS_JES_JOB_URI"
157 name="_webout.json" lrecl=999999 mod;
158 %end;
159
160 /* setup temp ref */
161 %if %upcase(&fref) ne _WEBOUT %then %do;
162 filename &fref temp lrecl=999999 permission='A::u::rwx,A::g::rw-,A::o::---';
163 %end;
164
165 /* setup json */
166 data _null_;file &fref;
167 %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
168 put '>>weboutBEGIN<<';
169 %end;
170 put '{"SYSDATE" : "' "&SYSDATE" '"';
171 put ',"SYSTIME" : "' "&SYSTIME" '"';
172 run;
173%end;
174%else %if &action=ARR or &action=OBJ %then %do;
175 %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
176 ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
177 )
178%end;
179%else %if &action=CLOSE %then %do;
180 %if %str(&workobs) > 0 %then %do;
181 /* send back first XX records of each work table for debugging */
182 data;run;%let tempds=%scan(&syslast,2,.);
183 ods output Members=&tempds;
184 proc datasets library=WORK memtype=data;
185 %local wtcnt;%let wtcnt=0;
186 data _null_;
187 set &tempds;
188 if not (upcase(name) =:"DATA"); /* ignore temp datasets */
189 i+1;
190 call symputx(cats('wt',i),name,'l');
191 call symputx('wtcnt',i,'l');
192 data _null_; file &fref mod; put ",""WORK"":{";
193 %do i=1 %to &wtcnt;
194 %let wt=&&wt&i;
195 data _null_; file &fref mod;
196 dsid=open("WORK.&wt",'is');
197 nlobs=attrn(dsid,'NLOBS');
198 nvars=attrn(dsid,'NVARS');
199 rc=close(dsid);
200 if &i>1 then put ','@;
201 put " ""&wt"" : {";
202 put '"nlobs":' nlobs;
203 put ',"nvars":' nvars;
204 %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
205 ,maxobs=&workobs
206 )
207 data _null_; file &fref mod;put "}";
208 %end;
209 data _null_; file &fref mod;put "}";run;
210 %end;
211
212 /* close off json */
213 data _null_;file &fref mod;
214 length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
215 put ",""_DEBUG"" : ""&_debug"" ";
216 _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
217 put ',"_PROGRAM" : ' _PROGRAM ;
218 autoexec=quote(urlencode(trim(getoption('autoexec'))));
219 put ',"AUTOEXEC" : ' autoexec;
220 put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
221 SYS_JES_JOB_URI=quote(trim(resolve(symget('SYS_JES_JOB_URI'))));
222 put ',"SYS_JES_JOB_URI" : ' SYS_JES_JOB_URI ;
223 put ",""SYSJOBID"" : ""&sysjobid"" ";
224 put ",""SYSCC"" : ""&syscc"" ";
225 syserrortext=cats(symget('syserrortext'));
226 if findc(syserrortext,'"\'!!'0A0D09000E0F010210111A'x) then do;
227 syserrortext='"'!!trim(
228 prxchange('s/"/\\"/',-1, /* double quote */
229 prxchange('s/\x0A/\n/',-1, /* new line */
230 prxchange('s/\x0D/\r/',-1, /* carriage return */
231 prxchange('s/\x09/\\t/',-1, /* tab */
232 prxchange('s/\x00/\\u0000/',-1, /* NUL */
233 prxchange('s/\x0E/\\u000E/',-1, /* SS */
234 prxchange('s/\x0F/\\u000F/',-1, /* SF */
235 prxchange('s/\x01/\\u0001/',-1, /* SOH */
236 prxchange('s/\x02/\\u0002/',-1, /* STX */
237 prxchange('s/\x10/\\u0010/',-1, /* DLE */
238 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
239 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
240 prxchange('s/\\/\\\\/',-1,syserrortext)
241 )))))))))))))!!'"';
242 end;
243 else syserrortext=cats('"',syserrortext,'"');
244 put ',"SYSERRORTEXT" : ' syserrortext;
245 put ",""SYSHOSTNAME"" : ""&syshostname"" ";
246 put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
247 put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
248 SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
249 put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
250 put ",""SYSJOBID"" : ""&sysjobid"" ";
251 put ",""SYSSCPL"" : ""&sysscpl"" ";
252 put ",""SYSSITE"" : ""&syssite"" ";
253 put ",""SYSUSERID"" : ""&sysuserid"" ";
254 sysvlong=quote(trim(symget('sysvlong')));
255 put ',"SYSVLONG" : ' sysvlong;
256 syswarningtext=cats(symget('syswarningtext'));
257 if findc(syswarningtext,'"\'!!'0A0D09000E0F010210111A'x) then do;
258 syswarningtext='"'!!trim(
259 prxchange('s/"/\\"/',-1, /* double quote */
260 prxchange('s/\x0A/\n/',-1, /* new line */
261 prxchange('s/\x0D/\r/',-1, /* carriage return */
262 prxchange('s/\x09/\\t/',-1, /* tab */
263 prxchange('s/\x00/\\u0000/',-1, /* NUL */
264 prxchange('s/\x0E/\\u000E/',-1, /* SS */
265 prxchange('s/\x0F/\\u000F/',-1, /* SF */
266 prxchange('s/\x01/\\u0001/',-1, /* SOH */
267 prxchange('s/\x02/\\u0002/',-1, /* STX */
268 prxchange('s/\x10/\\u0010/',-1, /* DLE */
269 prxchange('s/\x11/\\u0011/',-1, /* DC1 */
270 prxchange('s/\x1A/\\u001A/',-1, /* SUB */
271 prxchange('s/\\/\\\\/',-1,syswarningtext)
272 )))))))))))))!!'"';
273 end;
274 else syswarningtext=cats('"',syswarningtext,'"');
275 put ',"SYSWARNINGTEXT" : ' syswarningtext;
276 put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
277 length memsize $32;
278 memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
279 memsize=quote(cats(memsize));
280 put ',"MEMSIZE" : ' memsize;
281 put "}";
282 %if %str(&_debug) ge 128 and &_EXECUTIONTASKS=true %then %do;
283 put '>>weboutEND<<';
284 %end;
285 %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;
286 data _null_; rc=fcopy("&fref","_webout");run;
287 %end;
288
289%end;
290
291%mend mv_webout;