1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-10 02:40:05 +00:00

Compare commits

..

11 Commits

Author SHA1 Message Date
Allan Bowe
62837b512b feat: mm_getstpinfo.sas
Actually this came from a previous commit but the message was squashed out:  1b5effd584
2022-08-19 11:28:15 +01:00
Allan Bowe
5d5a99fd77 Merge pull request #304 from sasjs/allanbowe/need-a-macro-to-extract-303
chore(lint): reduce length
2022-08-19 11:00:00 +01:00
Allan Bowe
1b5effd584 chore(lint): reduce length 2022-08-19 09:58:42 +00:00
Allan Bowe
1613ab2c9e Merge pull request #302 from sasjs/allanbowe/proc-format-max-can-be-300
fix: switching MAX for LENGTH to get max label value.  Closes #300
2022-08-17 21:59:14 +01:00
Allan Bowe
a2df4e35be fix: switching MAX for LENGTH to get max label value. Closes #300 2022-08-17 20:54:14 +00:00
Allan Bowe
aabbcfdf6b Merge pull request #299 from sasjs/allanbowe/remove-work-tables-from-298
fix: removing automatic dump of WORK tables in mX_webout macros.  Clo…
2022-08-15 18:48:06 +01:00
Allan Bowe
7b7759e1ce chore: fix renegade closing bracket 2022-08-15 17:44:24 +00:00
Allan Bowe
e5a3053600 fix: removing automatic dump of WORK tables in mX_webout macros. Closes 298 2022-08-15 17:21:00 +00:00
Allan Bowe
9856d0ef58 Merge pull request #297 from sasjs/allanbowe/improve-efficiency-of-295
Further improvements to mp_jsonout
2022-08-15 00:48:23 +01:00
Allan Bowe
77b37e5503 chore: regenerated web service macros 2022-08-14 23:43:42 +00:00
Allan Bowe
793319fe38 fix: improved JSON performance for wide tables with a lot of formatted values. 50% improvement! 2022-08-14 23:43:19 +00:00
10 changed files with 893 additions and 357 deletions

View File

@@ -36,21 +36,21 @@ Documentation: https://core.sasjs.io
- OS independent - OS independent
- Works on all SAS Platforms - Works on all SAS Platforms
- No X command - No X command
- Prefixes: _mf_, _mp_ - Prefixes: `mf_`, `mp_`
### DDL folder (All Platforms) ### DDL folder (All Platforms)
- OS independent - OS independent
- Works on all SAS Platforms - Works on all SAS Platforms
- No X command - No X command
- Prefixes: _mddl_(lib)_ -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component) - Prefixes: `mddl_(lib)_` -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component)
This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications). This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications).
### FCMP folder (All Platforms) ### FCMP folder (All Platforms)
- Function and macro names are identical, except for special cases - Function and macro names are identical, except for special cases
- Prefixes: _mcf_ - Prefixes: `mcf_`
The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper. The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper.
@@ -72,7 +72,7 @@ endsubmit;
run; run;
``` ```
- Prefixes: _ml_ - Prefixes: `ml_`
### META folder (SAS9 only) ### META folder (SAS9 only)
@@ -81,14 +81,14 @@ Macros used in SAS EBI, which connect to the metadata server.
- OS independent - OS independent
- Metadata aware - Metadata aware
- No X command - No X command
- Prefixes: _mm_ - Prefixes: `mm_`
### METAX folder (SAS9 only) ### METAX folder (SAS9 only)
- OS specific - OS specific
- Metadata aware - Metadata aware
- X command enabled - X command enabled
- Prefixes: _mmw_,_mmu_,_mmx_ - Prefixes: `mmx_`
### SERVER folder (@sasjs/server only) ### SERVER folder (@sasjs/server only)
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS. These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
@@ -96,7 +96,7 @@ These macros are used for building applications using [@sasjs/server](https://se
- OS independent - OS independent
- @sasjs/server aware - @sasjs/server aware
- No X command - No X command
- Prefixes: _ms_ - Prefixes: `ms_`
### VIYA folder (Viya only) ### VIYA folder (Viya only)
@@ -104,7 +104,7 @@ Macros used for interfacing with SAS Viya.
- OS independent - OS independent
- No X command - No X command
- Prefixes: _mv_, _mvf_ - Prefixes: `mv_`, `mvf_`
### XPLATFORM folder (Viya, Meta, and Server) ### XPLATFORM folder (Viya, Meta, and Server)
@@ -112,7 +112,7 @@ Sometimes it is helpful to use a macro that can be used interchangeably regardle
- OS independent - OS independent
- No X command - No X command
- Prefixes: _mx_ - Prefixes: `mx_`
## Installation ## Installation

616
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -62,7 +62,7 @@
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to JSON that should be converted to JSON
<h4> Related Macros </h4> <h4> Related Files </h4>
@li mp_ds2fmtds.sas @li mp_ds2fmtds.sas
@version 9.2 @version 9.2
@@ -76,7 +76,8 @@
,showmeta=N ,showmeta=N
,maxobs=MAX ,maxobs=MAX
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; %local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval
tmpds1 tmpds2 tmpds3 tmpds4;
%let numcols=0; %let numcols=0;
%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); %if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;);
@@ -114,7 +115,7 @@
by varnum; by varnum;
run; run;
/* move meta to mac vars */ /* move meta to mac vars */
data _null_; data &colinfo;
if _n_=1 then call symputx('numcols',nobs,'l'); if _n_=1 then call symputx('numcols',nobs,'l');
set &colinfo end=last nobs=nobs; set &colinfo end=last nobs=nobs;
name=upcase(name); name=upcase(name);
@@ -135,18 +136,17 @@
end; end;
/* 32 char unique name */ /* 32 char unique name */
newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27); newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27);
maxlenv='maxlen'!!substr(cats(put(md5(name),$hex32.)),1,26);
call symputx(cats('name',_n_),name,'l'); call symputx(cats('name',_n_),name,'l');
call symputx(cats('newname',_n_),newname,'l'); call symputx(cats('newname',_n_),newname,'l');
call symputx(cats('maxlenv',_n_),maxlenv,'l');
call symputx(cats('length',_n_),length,'l'); call symputx(cats('length',_n_),length,'l');
/* overwritten when fmt=Y */
call symputx(cats('fmtlen',_n_),min(32767,ceil((length+3)*1.2)),'l');
call symputx(cats('fmt',_n_),fmt,'l'); call symputx(cats('fmt',_n_),fmt,'l');
call symputx(cats('type',_n_),type,'l'); call symputx(cats('type',_n_),type,'l');
call symputx(cats('typelong',_n_),typelong,'l'); call symputx(cats('typelong',_n_),typelong,'l');
call symputx(cats('label',_n_),coalescec(label,name),'l'); call symputx(cats('label',_n_),coalescec(label,name),'l');
/* overwritten when fmt=Y and a custom format exists in catalog */
if typelong='num' then call symputx(cats('fmtlen',_n_),200,'l');
else call symputx(cats('fmtlen',_n_),min(32767,ceil((length+3)*1.5)),'l');
run; run;
%let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
@@ -189,30 +189,82 @@
%end; %end;
%if &fmt=Y %then %do; %if &fmt=Y %then %do;
%let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); /**
/* need to find max length of formatted values */ * Extract format definitions
* First, by getting library locations from dictionary.formats
* Then, by exporting the width using proc format
* Cannot use maxw from sashelp.vformat as not always populated
* Cannot use fmtinfo() as not supported in all flavours
*/
%let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
proc sql noprint;
create table &tmpds1 as
select cats(libname,'.',memname) as fmtcat,
fmtname
from dictionary.formats
where fmttype='F' and libname is not null
and fmtname in (select format from &colinfo where format is not null)
order by 1;
create table &tmpds2(
FMTNAME char(32),
LENGTH num
);
%local catlist cat fmtlist i;
select distinct fmtcat into: catlist separated by ' ' from &tmpds1;
%do i=1 %to %sysfunc(countw(&catlist,%str( )));
%let cat=%scan(&catlist,&i,%str( ));
proc sql;
select distinct fmtname into: fmtlist separated by ' '
from &tmpds1 where fmtcat="&cat";
proc format lib=&cat cntlout=&tmpds3(keep=fmtname length);
select &fmtlist;
run;
proc sql;
insert into &tmpds2 select distinct fmtname,length from &tmpds3;
%end;
proc sql;
create table &tmpds4 as
select a.*, b.length as maxw
from &colinfo a
left join &tmpds2 b
on cats(a.format)=cats(upcase(b.fmtname))
order by a.varnum;
data _null_;
set &tmpds4;
if not missing(maxw);
call symputx(
cats('fmtlen',_n_),
/* vars need extra padding due to JSON escaping of special chars */
min(32767,ceil((max(length,maxw)+3)*1.5))
,'l'
);
run;
/* configure varlenchk - as we are explicitly shortening the variables */
%let optval=%sysfunc(getoption(varlenchk));
options varlenchk=NOWARN;
data _data_(compress=char); data _data_(compress=char);
/* shorten the new vars */
length
%do i=1 %to &numcols;
&&name&i $&&fmtlen&i
%end;
;
/* rename on entry */ /* rename on entry */
set &ds(rename=( set &ds(rename=(
%do i=1 %to &numcols; %do i=1 %to &numcols;
&&name&i=&&newname&i &&name&i=&&newname&i
%end; %end;
)); ));
&stmt_obs; &stmt_obs;
/* formatted values can be up to length 32767 */
length drop
%do i=1 %to &numcols; %do i=1 %to &numcols;
&&name&i &&newname&i
%end;
$32767;
retain &tempvar
%do i=1 %to &numcols;
&&maxlenv&i
%end;
0;
drop &tempvar
%do i=1 %to &numcols;
&&newname&i &&maxlenv&i
%end; %end;
; ;
%do i=1 %to &numcols; %do i=1 %to &numcols;
@@ -222,19 +274,14 @@
%else %do; %else %do;
&&name&i=put(&&newname&i,&&fmt&i); &&name&i=put(&&newname&i,&&fmt&i);
%end; %end;
/* grab max length of each value as we move down the data step */
&tempvar=length(&&name&i);
if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar;
%end;
if _n_=&lastobs then do;
/* add a 20% buffer in case of special chars that need to be escaped */
%do i=1 %to &numcols;
call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),'l');
%end; %end;
if _error_ then do;
call symputx('syscc',1012);
stop;
end; end;
if _error_ then call symputx('syscc',1012);
run; run;
%let fmtds=&syslast; %let fmtds=&syslast;
options varlenchk=&optval;
%end; %end;
proc format; /* credit yabwon for special null removal */ proc format; /* credit yabwon for special null removal */
@@ -249,9 +296,6 @@
%end; %end;
other = [best.]; other = [best.];
/* configure varlenchk - as we are explicitly shortening the variables */
%let optval=%sysfunc(getoption(varlenchk));
options varlenchk=NOWARN;
data &tempds; data &tempds;
attrib _all_ label=''; attrib _all_ label='';
%do i=1 %to &numcols; %do i=1 %to &numcols;
@@ -290,7 +334,6 @@
%end; %end;
%end; %end;
run; run;
options varlenchk=&optval;
filename _sjs3 temp lrecl=131068 ; filename _sjs3 temp lrecl=131068 ;
data _null_; data _null_;

View File

@@ -99,7 +99,8 @@ data _null_;
put ' ,showmeta=N '; put ' ,showmeta=N ';
put ' ,maxobs=MAX '; put ' ,maxobs=MAX ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval ';
put ' tmpds1 tmpds2 tmpds3 tmpds4; ';
put '%let numcols=0; '; put '%let numcols=0; ';
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
put ' '; put ' ';
@@ -137,7 +138,7 @@ data _null_;
put ' by varnum; '; put ' by varnum; ';
put ' run; '; put ' run; ';
put ' /* move meta to mac vars */ '; put ' /* move meta to mac vars */ ';
put ' data _null_; '; put ' data &colinfo; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; '; put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); '; put ' name=upcase(name); ';
@@ -158,18 +159,17 @@ data _null_;
put ' end; '; put ' end; ';
put ' /* 32 char unique name */ '; put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); ';
put ' '; put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' /* overwritten when fmt=Y */ ';
put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -212,30 +212,82 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' /** ';
put ' /* need to find max length of formatted values */ '; put ' * Extract format definitions ';
put ' * First, by getting library locations from dictionary.formats ';
put ' * Then, by exporting the width using proc format ';
put ' * Cannot use maxw from sashelp.vformat as not always populated ';
put ' * Cannot use fmtinfo() as not supported in all flavours ';
put ' */ ';
put ' %let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
put ' %do i=1 %to %sysfunc(countw(&catlist,%str( ))); ';
put ' %let cat=%scan(&catlist,&i,%str( )); ';
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.length as maxw ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
put ' order by a.varnum; ';
put ' data _null_; ';
put ' set &tmpds4; ';
put ' if not missing(maxw); ';
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data _data_(compress=char); '; put ' data _data_(compress=char); ';
put ' /* shorten the new vars */ ';
put ' length ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i $&&fmtlen&i ';
put ' %end; ';
put ' ; ';
put ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' &stmt_obs; '; put ' &stmt_obs; ';
put ' /* formatted values can be up to length 32767 */ '; put ' ';
put ' length '; put ' drop ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i '; put ' &&newname&i ';
put ' %end; ';
put ' $32767; ';
put ' retain &tempvar ';
put ' %do i=1 %to &numcols; ';
put ' &&maxlenv&i ';
put ' %end; ';
put ' 0; ';
put ' drop &tempvar ';
put ' %do i=1 %to &numcols; ';
put ' &&newname&i &&maxlenv&i ';
put ' %end; '; put ' %end; ';
put ' ; '; put ' ; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
@@ -245,19 +297,14 @@ data _null_;
put ' %else %do; '; put ' %else %do; ';
put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' &&name&i=put(&&newname&i,&&fmt&i); ';
put ' %end; '; put ' %end; ';
put ' /* grab max length of each value as we move down the data step */ ';
put ' &tempvar=length(&&name&i); ';
put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; ';
put ' %end; ';
put ' if _n_=&lastobs then do; ';
put ' /* add a 20% buffer in case of special chars that need to be escaped */ ';
put ' %do i=1 %to &numcols; ';
put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); ';
put ' %end; '; put ' %end; ';
put ' if _error_ then do; ';
put ' call symputx(''syscc'',1012); ';
put ' stop; ';
put ' end; '; put ' end; ';
put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; '; put ' run; ';
put ' %let fmtds=&syslast; '; put ' %let fmtds=&syslast; ';
put ' options varlenchk=&optval; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
@@ -272,9 +319,6 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data &tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
@@ -313,7 +357,6 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' options varlenchk=&optval; ';
put ' '; put ' ';
put ' filename _sjs3 temp lrecl=131068 ; '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' data _null_; '; put ' data _null_; ';
@@ -403,8 +446,8 @@ data _null_;
put ' %quote(&user) '; put ' %quote(&user) ';
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX '; put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); '; put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug '; put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; '; put ' sasjs_tables; ';
@@ -477,8 +520,8 @@ data _null_;
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' /* To avoid issues with _webout on EBI we use a temporary file */ '; put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
put ' filename _sjsref temp lrecl=131068; '; put ' filename _sjsref temp lrecl=131068; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ '; put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); '; put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; '; put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; '; put ' proc datasets library=WORK memtype=data; ';
@@ -502,7 +545,9 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10) '; put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file _sjsref mod encoding=''utf-8''; '; put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';

67
meta/mm_getstpinfo.sas Normal file
View File

@@ -0,0 +1,67 @@
/**
@file
@brief Get the properties of a Stored Process
@details Extracts various properties and creates an output table in the
structure below:
|STP_URI:$200.|SERVERCONTEXT:$200.|STOREDPROCESSCONFIGURATION:$1000.|SOURCECODE_FIRST32K:$32767.|PATH:$76.|
|---|---|---|---|---|
|`A5DN9TDQ.BH0000C8 `|`SASApp `|`<?xml version="1.0" encoding="UTF-8"?><StoredProcess><ServerContext LogicalServerType="Sps" OtherAllowed="false"/><ResultCapabilities Package="false" Streaming="true"/><OutputParameters/></StoredProcess> `|`%put first 32767 bytes of code; `|`/path/to/my/stp`|
@param [in] pgm The metadata path of the Stored Process
@param [out] outds= (work.mm_getstpinfo) The output table to create
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
<h4> Related Files </h4>
@li mm_getstpcode.sas
@li mm_getstps.sas
@li mm_createstp.sas
@li mm_deletestp.sas
**/
%macro mm_getstpinfo(pgm
,outds=work.mm_getstpinfo
,mDebug=0
);
%local mD;
%if &mDebug=1 %then %let mD=;
%else %let mD=%str(*);
%&mD.put Executing &sysmacroname..sas;
%&mD.put _local_;
data &outds;
length type stp_uri tsuri servercontext value $200
StoredProcessConfiguration $1000 sourcecode_first32k $32767;
keep path stp_uri sourcecode_first32k StoredProcessConfiguration
servercontext;
call missing (of _all_);
path="&pgm(StoredProcess)";
/* first, find the STP ID */
if metadata_pathobj("",path,"StoredProcess",type,stp_uri)>0 then do;
/* get attributes */
cnt=1;
do while (metadata_getnasn(stp_uri,"Notes",cnt,tsuri)>0);
rc1=metadata_getattr(tsuri,"Name",value);
&mD.put tsuri= value=;
if value="SourceCode" then do;
rc2=metadata_getattr(tsuri,"StoredText",sourcecode_first32k);
end;
else if value="Stored Process" then do;
rc3=metadata_getattr(tsuri,"StoredText",StoredProcessConfiguration);
end;
cnt+1;
end;
/* get context (should only be one) */
rc4=metadata_getnasn(stp_uri,"ComputeLocations",1,tsuri);
rc5=metadata_getattr(tsuri,"Name",servercontext);
end;
else do;
put "%str(ERR)OR: could not find " pgm;
put (_all_)(=);
end;
&md.put (_all_)(=);
run;
%mend mm_getstpinfo ;

View File

@@ -26,7 +26,7 @@
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@param [in] ds The dataset to send back to the frontend @param [in] ds The dataset to send back to the frontend
@param [out] dslabel= Value to use instead of table name for sending to JSON @param [out] dslabel= Value to use instead of table name for sending to JSON
@param [in] fmt=(Y) Set to N to send back unformatted values @param [in] fmt= (N) Setting Y converts all vars to their formatted values
@param [out] fref= (_webout) The fileref to which to write the JSON @param [out] fref= (_webout) The fileref to which to write the JSON
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`) (eg `null`) or as STRING values (eg `".a"` or `".b"`)
@@ -34,18 +34,25 @@
such as the column formats and types. The metadata is contained inside an such as the column formats and types. The metadata is contained inside an
object with the same name as the table but prefixed with a dollar sign - ie, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON that should be converted to output JSON
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
<h4> Related Macros </h4>
@li ms_webout.sas
@li mv_webout.sas
@version 9.3 @version 9.3
@author Allan Bowe @author Allan Bowe
**/ **/
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL %macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N,maxobs=MAX ,showmeta=N,maxobs=MAX,workobs=0
); );
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables; sasjs_tables;
@@ -118,8 +125,8 @@
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
/* To avoid issues with _webout on EBI we use a temporary file */ /* To avoid issues with _webout on EBI we use a temporary file */
filename _sjsref temp lrecl=131068; filename _sjsref temp lrecl=131068;
%if %str(&_debug) ge 131 %then %do; %if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first 10 records of each work table also */ /* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.); data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds; ods output Members=&tempds;
proc datasets library=WORK memtype=data; proc datasets library=WORK memtype=data;
@@ -143,7 +150,9 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10) %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10
,maxobs=&workobs
)
data _null_; file _sjsref mod encoding='utf-8'; data _null_; file _sjsref mod encoding='utf-8';
put "}"; put "}";
%end; %end;

View File

@@ -100,7 +100,8 @@ data _null_;
put ' ,showmeta=N '; put ' ,showmeta=N ';
put ' ,maxobs=MAX '; put ' ,maxobs=MAX ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval ';
put ' tmpds1 tmpds2 tmpds3 tmpds4; ';
put '%let numcols=0; '; put '%let numcols=0; ';
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
put ' '; put ' ';
@@ -138,7 +139,7 @@ data _null_;
put ' by varnum; '; put ' by varnum; ';
put ' run; '; put ' run; ';
put ' /* move meta to mac vars */ '; put ' /* move meta to mac vars */ ';
put ' data _null_; '; put ' data &colinfo; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; '; put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); '; put ' name=upcase(name); ';
@@ -159,18 +160,17 @@ data _null_;
put ' end; '; put ' end; ';
put ' /* 32 char unique name */ '; put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); ';
put ' '; put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' /* overwritten when fmt=Y */ ';
put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -213,30 +213,82 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' /** ';
put ' /* need to find max length of formatted values */ '; put ' * Extract format definitions ';
put ' * First, by getting library locations from dictionary.formats ';
put ' * Then, by exporting the width using proc format ';
put ' * Cannot use maxw from sashelp.vformat as not always populated ';
put ' * Cannot use fmtinfo() as not supported in all flavours ';
put ' */ ';
put ' %let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
put ' %do i=1 %to %sysfunc(countw(&catlist,%str( ))); ';
put ' %let cat=%scan(&catlist,&i,%str( )); ';
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.length as maxw ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
put ' order by a.varnum; ';
put ' data _null_; ';
put ' set &tmpds4; ';
put ' if not missing(maxw); ';
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data _data_(compress=char); '; put ' data _data_(compress=char); ';
put ' /* shorten the new vars */ ';
put ' length ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i $&&fmtlen&i ';
put ' %end; ';
put ' ; ';
put ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' &stmt_obs; '; put ' &stmt_obs; ';
put ' /* formatted values can be up to length 32767 */ '; put ' ';
put ' length '; put ' drop ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i '; put ' &&newname&i ';
put ' %end; ';
put ' $32767; ';
put ' retain &tempvar ';
put ' %do i=1 %to &numcols; ';
put ' &&maxlenv&i ';
put ' %end; ';
put ' 0; ';
put ' drop &tempvar ';
put ' %do i=1 %to &numcols; ';
put ' &&newname&i &&maxlenv&i ';
put ' %end; '; put ' %end; ';
put ' ; '; put ' ; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
@@ -246,19 +298,14 @@ data _null_;
put ' %else %do; '; put ' %else %do; ';
put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' &&name&i=put(&&newname&i,&&fmt&i); ';
put ' %end; '; put ' %end; ';
put ' /* grab max length of each value as we move down the data step */ ';
put ' &tempvar=length(&&name&i); ';
put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; ';
put ' %end; ';
put ' if _n_=&lastobs then do; ';
put ' /* add a 20% buffer in case of special chars that need to be escaped */ ';
put ' %do i=1 %to &numcols; ';
put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); ';
put ' %end; '; put ' %end; ';
put ' if _error_ then do; ';
put ' call symputx(''syscc'',1012); ';
put ' stop; ';
put ' end; '; put ' end; ';
put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; '; put ' run; ';
put ' %let fmtds=&syslast; '; put ' %let fmtds=&syslast; ';
put ' options varlenchk=&optval; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
@@ -273,9 +320,6 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data &tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
@@ -314,7 +358,6 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' options varlenchk=&optval; ';
put ' '; put ' ';
put ' filename _sjs3 temp lrecl=131068 ; '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' data _null_; '; put ' data _null_; ';
@@ -405,8 +448,8 @@ data _null_;
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
put ' '; put ' ';
put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX '; put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); '; put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug '; put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; '; put ' sasjs_tables; ';
@@ -469,8 +512,8 @@ data _null_;
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ '; put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); '; put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; '; put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; '; put ' proc datasets library=WORK memtype=data; ';
@@ -495,7 +538,9 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) '; put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; '; put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';

View File

@@ -23,7 +23,7 @@
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@param [in] ds The dataset to send back to the frontend @param [in] ds The dataset to send back to the frontend
@param [out] dslabel= value to use instead of table name for sending to JSON @param [out] dslabel= value to use instead of table name for sending to JSON
@param [in] fmt= (Y) Set to N to send back unformatted values @param [in] fmt= (N) Setting Y converts all vars to their formatted values
@param [out] fref= (_webout) The fileref to which to write the JSON @param [out] fref= (_webout) The fileref to which to write the JSON
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`) (eg `null`) or as STRING values (eg `".a"` or `".b"`)
@@ -31,6 +31,9 @@
such as the column formats and types. The metadata is contained inside an such as the column formats and types. The metadata is contained inside an
object with the same name as the table but prefixed with a dollar sign - ie, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON that should be converted to output JSON
@@ -48,8 +51,8 @@
**/ **/
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL %macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N,maxobs=MAX ,showmeta=N,maxobs=MAX,workobs=0
); );
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables; sasjs_tables;
@@ -112,8 +115,8 @@
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do; %if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first 10 records of each work table also */ /* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.); data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds; ods output Members=&tempds;
proc datasets library=WORK memtype=data; proc datasets library=WORK memtype=data;
@@ -138,7 +141,9 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10
,maxobs=&workobs
)
data _null_; file &fref mod encoding='utf-8' termstr=lf; data _null_; file &fref mod encoding='utf-8' termstr=lf;
put "}"; put "}";
%end; %end;

View File

@@ -242,7 +242,8 @@ data _null_;
put ' ,showmeta=N '; put ' ,showmeta=N ';
put ' ,maxobs=MAX '; put ' ,maxobs=MAX ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval ';
put ' tmpds1 tmpds2 tmpds3 tmpds4; ';
put '%let numcols=0; '; put '%let numcols=0; ';
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
put ' '; put ' ';
@@ -280,7 +281,7 @@ data _null_;
put ' by varnum; '; put ' by varnum; ';
put ' run; '; put ' run; ';
put ' /* move meta to mac vars */ '; put ' /* move meta to mac vars */ ';
put ' data _null_; '; put ' data &colinfo; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; '; put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); '; put ' name=upcase(name); ';
@@ -301,18 +302,17 @@ data _null_;
put ' end; '; put ' end; ';
put ' /* 32 char unique name */ '; put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); ';
put ' '; put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' /* overwritten when fmt=Y */ ';
put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -355,30 +355,82 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' /** ';
put ' /* need to find max length of formatted values */ '; put ' * Extract format definitions ';
put ' * First, by getting library locations from dictionary.formats ';
put ' * Then, by exporting the width using proc format ';
put ' * Cannot use maxw from sashelp.vformat as not always populated ';
put ' * Cannot use fmtinfo() as not supported in all flavours ';
put ' */ ';
put ' %let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
put ' %do i=1 %to %sysfunc(countw(&catlist,%str( ))); ';
put ' %let cat=%scan(&catlist,&i,%str( )); ';
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.length as maxw ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
put ' order by a.varnum; ';
put ' data _null_; ';
put ' set &tmpds4; ';
put ' if not missing(maxw); ';
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data _data_(compress=char); '; put ' data _data_(compress=char); ';
put ' /* shorten the new vars */ ';
put ' length ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i $&&fmtlen&i ';
put ' %end; ';
put ' ; ';
put ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' &stmt_obs; '; put ' &stmt_obs; ';
put ' /* formatted values can be up to length 32767 */ '; put ' ';
put ' length '; put ' drop ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i '; put ' &&newname&i ';
put ' %end; ';
put ' $32767; ';
put ' retain &tempvar ';
put ' %do i=1 %to &numcols; ';
put ' &&maxlenv&i ';
put ' %end; ';
put ' 0; ';
put ' drop &tempvar ';
put ' %do i=1 %to &numcols; ';
put ' &&newname&i &&maxlenv&i ';
put ' %end; '; put ' %end; ';
put ' ; '; put ' ; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
@@ -388,19 +440,14 @@ data _null_;
put ' %else %do; '; put ' %else %do; ';
put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' &&name&i=put(&&newname&i,&&fmt&i); ';
put ' %end; '; put ' %end; ';
put ' /* grab max length of each value as we move down the data step */ ';
put ' &tempvar=length(&&name&i); ';
put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; ';
put ' %end; ';
put ' if _n_=&lastobs then do; ';
put ' /* add a 20% buffer in case of special chars that need to be escaped */ ';
put ' %do i=1 %to &numcols; ';
put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); ';
put ' %end; '; put ' %end; ';
put ' if _error_ then do; ';
put ' call symputx(''syscc'',1012); ';
put ' stop; ';
put ' end; '; put ' end; ';
put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; '; put ' run; ';
put ' %let fmtds=&syslast; '; put ' %let fmtds=&syslast; ';
put ' options varlenchk=&optval; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
@@ -415,9 +462,6 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data &tempds; '; put ' data &tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
@@ -456,7 +500,6 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' options varlenchk=&optval; ';
put ' '; put ' ';
put ' filename _sjs3 temp lrecl=131068 ; '; put ' filename _sjs3 temp lrecl=131068 ; ';
put ' data _null_; '; put ' data _null_; ';
@@ -546,8 +589,8 @@ data _null_;
put ' %quote(&user) '; put ' %quote(&user) ';
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL '; put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX '; put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); '; put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name '; put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI; '; put ' sasjs_tables SYS_JES_JOB_URI; ';
@@ -654,8 +697,8 @@ data _null_;
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* send back first 10 records of each work table for debugging */ '; put ' /* send back first XX records of each work table for debugging */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); '; put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; '; put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; '; put ' proc datasets library=WORK memtype=data; ';
@@ -678,7 +721,9 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) '; put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file &fref mod;put "}"; '; put ' data _null_; file &fref mod;put "}"; ';
put ' %end; '; put ' %end; ';
put ' data _null_; file &fref mod;put "}";run; '; put ' data _null_; file &fref mod;put "}";run; ';

View File

@@ -25,7 +25,7 @@
@param [in] _webout= fileref for returning the json @param [in] _webout= fileref for returning the json
@param [out] fref=(_mvwtemp) Temp fileref to which to write the output @param [out] fref=(_mvwtemp) Temp fileref to which to write the output
@param [out] dslabel= value to use instead of table name for sending to JSON @param [out] dslabel= value to use instead of table name for sending to JSON
@param [in] fmt=(Y) change to N to strip formats from output @param [in] fmt= (N) Setting Y converts all vars to their formatted values
@param [in] stream=(Y) Change to N if not streaming to _webout @param [in] stream=(Y) Change to N if not streaming to _webout
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`) (eg `null`) or as STRING values (eg `".a"` or `".b"`)
@@ -35,17 +35,24 @@
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON that should be converted to output JSON
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@li mf_getuser.sas @li mf_getuser.sas
<h4> Related Macros </h4>
@li ms_webout.sas
@li mm_webout.sas
@version Viya 3.3 @version Viya 3.3
@author Allan Bowe, source: https://github.com/sasjs/core @author Allan Bowe, source: https://github.com/sasjs/core
**/ **/
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL %macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL
,showmeta=N,maxobs=MAX ,showmeta=N,maxobs=MAX,workobs=0
); );
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name %global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI; sasjs_tables SYS_JES_JOB_URI;
@@ -152,8 +159,8 @@
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do; %if %str(&workobs) > 0 %then %do;
/* send back first 10 records of each work table for debugging */ /* send back first XX records of each work table for debugging */
data;run;%let tempds=%scan(&syslast,2,.); data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds; ods output Members=&tempds;
proc datasets library=WORK memtype=data; proc datasets library=WORK memtype=data;
@@ -176,7 +183,9 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs
)
data _null_; file &fref mod;put "}"; data _null_; file &fref mod;put "}";
%end; %end;
data _null_; file &fref mod;put "}";run; data _null_; file &fref mod;put "}";run;