1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-25 12:11:19 +00:00

Compare commits

...

21 Commits

Author SHA1 Message Date
Allan Bowe
8cfa37ce8b Merge pull request #140 from sasjs/fix_dirlist
fix: proc append warnings for file attributes
2022-01-11 11:35:43 +02:00
Allan Bowe
351ceeb357 fix: tidy up 2022-01-10 18:42:52 +00:00
Ivor Townsend
259bcc0173 fix: proc append warnings for file attributes 2022-01-10 16:49:33 +00:00
Ivor Townsend
db195a8311 fix: proc append warnings for file attributes 2022-01-10 16:33:44 +00:00
munja
4307bfb1b5 fix: adding showmeta option to mX_createwebservce macros 2022-01-09 13:18:14 +01:00
Allan Bowe
df46ee6939 Merge pull request #139 from sasjs/webout_mac
feat: adding SHOWMETA option to mp_jsonout
2022-01-07 15:29:06 +02:00
munja
70b9b71104 fix: base table on mp_lockanytable.test.sas 2022-01-07 14:12:08 +01:00
munja
cd33355418 fix: formats 2022-01-07 13:14:40 +01:00
munja
77d1cdb753 feat: adding length to mp_webout meta 2022-01-07 12:23:40 +01:00
munja
545218e3b9 fix: avoiding type clash 2022-01-06 23:44:42 +01:00
munja
cb07305a87 feat: adding SHOWMETA option to mp_jsonout
Includes updates to associated wrapper macros, and a 30% performance improvement (for small tables).  Addresses https://github.com/sasjs/adapter/issues/607
2022-01-06 23:36:54 +01:00
Allan Bowe
76a39cad20 Merge pull request #138 from sasjs/webout_mac
fix: adding missing param to mx_createwebservice macros
2021-12-30 16:29:50 +02:00
munja
ebd567af48 fix: adding missing param to mx_createwebservice macros 2021-12-30 09:53:08 +00:00
Allan Bowe
a9c418e3f2 Merge pull request #137 from sasjs/specials
feat: updating mp_jsonout() to support special missing numeric values.
2021-12-30 11:43:00 +02:00
munja
e143acd67d chore: automated commit 2021-12-30 00:30:14 +00:00
munja
84eb2f1845 chore: automated commit 2021-12-30 00:29:48 +00:00
munja
b075e5d5d5 feat: updating mp_jsonout() to support special missing numeric values. Closes #136 2021-12-30 00:21:02 +00:00
Allan Bowe
a08f6aeea2 Merge pull request #135 from sasjs/abortfix
fix: removing 'Log Extract' from abort MSG in mp_abort when not capturing the log
2021-12-29 14:54:01 +02:00
munja
469bd574ac fix: removing 'Log Extract' from abort MSG in mp_abort when not capturing the log 2021-12-29 12:35:25 +00:00
Allan Bowe
c41918c0a8 Merge pull request #134 from sasjs/fmtfix
fix: preventing error when mp_applyformats has no formats to apply
2021-12-29 14:19:59 +02:00
munja
0361ca574d fix: preventing error when mp_applyformats has no formats to apply 2021-12-29 12:19:34 +00:00
18 changed files with 1189 additions and 800 deletions

View File

@@ -188,6 +188,13 @@ When contributing to this library, it is therefore important to ensure that all
- All macros should be compatible with SAS versions from support level B and above (so currently 9.2 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully (eg `%if %sysevalf(&sysver<9.3) %then %return`).
## Breaking Changes
We are currently on major release v3. The following changes are planned when the next major (breaking) release becomes necessary:
* Remove `dbg` parameter from mp_jsonout.sas (implement mdebug instead)
* Remove `END_DTTM` and `START_DTTM` from mx_webout JSON
## Star Gazing
If you find this library useful, please leave a [star](https://github.com/sasjs/core/stargazers) and help us grow our star graph!

914
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -110,8 +110,8 @@
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
%else %let logloc=%qsysfunc(getoption(LOG));
proc printto log=log;run;
%let logline=0;
%if %length(&logloc)>0 %then %do;
%let logline=0;
data _null_;
infile &logloc lrecl=5000;
input; putlog _infile_;
@@ -160,7 +160,10 @@
file _webout mod lrecl=32000 encoding='utf-8';
length msg $32767 ;
sasdatetime=datetime();
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
msg=symget('msg');
%if &logline>0 %then %do;
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
%end;
/* escape the quotes */
msg=tranwrd(msg,'"','\"');
/* ditch the CRLFs as chrome complains */

View File

@@ -142,16 +142,16 @@ run;
proc sql noprint;
select distinct lib into: liblist separated by ' ' from &inds;
%put &=liblist;
%do i=1 %to %sysfunc(countw(&liblist));
%if %length(&liblist)>0 %then %do i=1 %to %sysfunc(countw(&liblist));
%let lib=%scan(&liblist,1);
%let engine=%mf_getengine(&lib);
%if &engine ne V9 and &engine ne BASE %then %do;
%let msg=&lib has &engine engine - formats cannot be applied;
proc sql;
insert into &outds set lib="&lib",ds="_all_",var="_all", msg="&msg" ;
%if &errds=0 %then %put %str(ERR)OR: &msg;
%end;
%end;
quit;
%if %mf_nobs(&outds)>0 %then %return;

View File

@@ -6,8 +6,7 @@
Credit for the rename approach:
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
usage:
Usage:
%mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX)
@@ -23,12 +22,12 @@
X CMD) do please raise an issue!
@param [in] path= for which to return contents
@param [in] fref= Provide a DISK engine fileref as an alternative to PATH
@param [in] path= (%sysfunc(pathname(work))) Path for which to return contents
@param [in] fref= (0) Provide a DISK engine fileref as an alternative to PATH
@param [in] maxdepth= (0) Set to a positive integer to indicate the level of
subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
recursion, set to MAX.
@param [out] outds= the output dataset to create
@param [out] outds= (work.mp_dirlist) The output dataset to create
@param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname
functions are used to scan all properties - any characters that are not
valid in a SAS name (v7) are simply stripped, and the table is transposed
@@ -49,13 +48,15 @@
- OS SPECIFIC variables, if <code>getattrs=</code> is used.
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mf_getvarlist.sas
@li mf_wordsinstr1butnotstr2.sas
@li mp_dropmembers.sas
<h4> Related Macros </h4>
@li mp_dirlist.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_dirlist(path=%sysfunc(pathname(work))
@@ -193,9 +194,24 @@ data &out_ds;
set &out_ds(where=(filepath ne ''));
run;
/* update main table */
proc append base=&outds data=&out_ds;
run;
/**
* The above transpose can mean that some updates create additional columns.
* This necessitates the occasional use of datastep over proc append.
*/
%local basevars appvars usedatastep;
%let basevars=%mf_getvarlist(&outds);
%let appvars=%mf_getvarlist(&out_ds);
%let newvars=%length(%mf_wordsinstr1butnotstr2(Str1=&appvars,Str2=&basevars));
%if %mf_existds(&outds) and &newvars>0 %then %do;
data &outds;
set &outds &out_ds;
run;
%end;
%else %do;
proc append base=&outds data=&out_ds force nowarn;
run;
%end;
/* recursive call */
%if &maxdepth>&level or &maxdepth=MAX %then %do;

View File

@@ -19,11 +19,12 @@
%mp_jsonout(OPEN,jref=tmp)
%mp_jsonout(OBJ,class,jref=tmp)
%mp_jsonout(OBJ,class,dslabel=class2,jref=tmp,showmeta=YES)
%mp_jsonout(CLOSE,jref=tmp)
data _null_;
infile tmp;
input;list;
input;putlog _infile_;
run;
If you are building web apps with SAS then you are strongly encouraged to use
@@ -31,22 +32,24 @@
[sasjs adapter](https://github.com/sasjs/adapter).
For more information see https://sasjs.io
@param action Valid values:
@param [in] action Valid values:
@li OPEN - opens the JSON
@li OBJ - sends a table with each row as an object
@li ARR - sends a table with each row in an array
@li CLOSE - closes the JSON
@param ds the dataset to send. Must be a work table.
@param jref= the fileref to which to send the JSON
@param dslabel= the name to give the table in the exported JSON
@param fmt= Whether to keep or strip formats from the table
@param engine= Which engine to use to send the JSON, valid options are:
@param [in] ds The dataset to send. Must be a work table.
@param [out] jref= (_webout) The fileref to which to send the JSON
@param [out] dslabel= The name to give the table in the exported JSON
@param [in] fmt= (Y) Whether to keep (Y) or strip (N) formats from the table
@param [in] engine= (DATASTEP) Which engine to use to send the JSON. Options:
@li PROCJSON (default)
@li DATASTEP (more reliable when data has non standard characters)
@param dbg= DEPRECATED - was used to conditionally add PRETTY to
proc json but this can cause line truncation in large files.
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
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,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> Related Macros <h4>
@li mp_ds2fmtds.sas
@@ -57,9 +60,15 @@
**/
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP,dbg=0
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
,engine=DATASTEP
,dbg=0 /* DEPRECATED */
,missing=NULL
,showmeta=NO
)/*/STORE SOURCE*/;
%put output location=&jref;
%local tempds colinfo fmtds i numcols;
%let numcols=0;
%if &action=OPEN %then %do;
options nobomfile;
data _null_;file &jref encoding='utf-8' ;
@@ -68,118 +77,124 @@
%end;
%else %if (&action=ARR or &action=OBJ) %then %do;
options validvarname=upcase;
data _null_;file &jref mod encoding='utf-8' ;
data _null_; file &jref encoding='utf-8' mod;
put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":";
/* grab col defs */
proc contents noprint data=&ds
out=_data_(keep=name type length format formatl formatd varnum label);
run;
%let colinfo=%scan(&syslast,2,.);
proc sort data=&colinfo;
by varnum;
run;
/* move meta to mac vars */
data _null_;
if _n_=1 then call symputx('numcols',nobs,'l');
set &colinfo end=last nobs=nobs;
name=upcase(name);
/* fix formats */
if type=2 or type=6 then do;
typelong='char';
length fmt $49.;
if format='' then fmt=cats('$',length,'.');
else if formatl=0 then fmt=cats(format,'.');
else fmt=cats(format,formatl,'.');
newlen=max(formatl,length);
end;
else do;
typelong='num';
if format='' then fmt='best.';
else if formatl=0 then fmt=cats(format,'.');
else if formatd=0 then fmt=cats(format,formatl,'.');
else fmt=cats(format,formatl,'.',formatd);
/* needs to be wide, for datetimes etc */
newlen=max(length,formatl,24);
end;
/* 32 char unique name */
newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27);
call symputx(cats('name',_n_),name,'l');
call symputx(cats('newname',_n_),newname,'l');
call symputx(cats('len',_n_),newlen,'l');
call symputx(cats('length',_n_),length,'l');
call symputx(cats('fmt',_n_),fmt,'l');
call symputx(cats('type',_n_),type,'l');
call symputx(cats('typelong',_n_),typelong,'l');
call symputx(cats('label',_n_),coalescec(label,name),'l');
run;
%let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%if &engine=PROCJSON %then %do;
data;run;%let tempds=&syslast;
proc sql;drop table &tempds;
%if &missing=STRING %then %do;
%put &sysmacroname: Special Missings not supported in proc json.;
%put &sysmacroname: Switching to DATASTEP engine;
%goto datastep;
%end;
data &tempds /view=&tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */
proc json out=&jref pretty
%if &action=ARR %then nokeys ;
;export &tempds / nosastags fmtnumeric;
run;
proc sql;drop view &tempds;
%end;
%else %if &engine=DATASTEP %then %do;
%local cols i tempds;
%let cols=0;
%if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do;
%datastep:
%if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1
%then %do;
%put &sysmacroname: &ds NOT FOUND!!!;
%return;
%end;
%if &fmt=Y %then %do;
%put converting every variable to a formatted variable;
/* see mp_ds2fmtds.sas for source */
proc contents noprint data=&ds
out=_data_(keep=name type length format formatl formatd varnum);
run;
proc sort;
by varnum;
run;
%local fmtds;
%let fmtds=%scan(&syslast,2,.);
/* prepare formats and varnames */
data _null_;
if _n_=1 then call symputx('nobs',nobs,'l');
set &fmtds end=last nobs=nobs;
name=upcase(name);
/* fix formats */
if type=2 or type=6 then do;
length fmt $49.;
if format='' then fmt=cats('$',length,'.');
else if formatl=0 then fmt=cats(format,'.');
else fmt=cats(format,formatl,'.');
newlen=max(formatl,length);
end;
else do;
if format='' then fmt='best.';
else if formatl=0 then fmt=cats(format,'.');
else if formatd=0 then fmt=cats(format,formatl,'.');
else fmt=cats(format,formatl,'.',formatd);
/* needs to be wide, for datetimes etc */
newlen=max(length,formatl,24);
end;
/* 32 char unique name */
newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27);
call symputx(cats('name',_n_),name,'l');
call symputx(cats('newname',_n_),newname,'l');
call symputx(cats('len',_n_),newlen,'l');
call symputx(cats('fmt',_n_),fmt,'l');
call symputx(cats('type',_n_),type,'l');
run;
data &fmtds;
%if &fmt=Y %then %do;
data _data_;
/* rename on entry */
set &ds(rename=(
%local i;
%do i=1 %to &nobs;
%do i=1 %to &numcols;
&&name&i=&&newname&i
%end;
));
%do i=1 %to &nobs;
%do i=1 %to &numcols;
length &&name&i $&&len&i;
&&name&i=left(put(&&newname&i,&&fmt&i));
drop &&newname&i;
%end;
if _error_ then call symputx('syscc',1012);
run;
%let ds=&fmtds;
%end; /* &fmt=Y */
data _null_;file &jref mod encoding='utf-8' ;
put "["; call symputx('cols',0,'l');
proc sort
data=sashelp.vcolumn(where=(libname='WORK' & memname="%upcase(&ds)"))
out=_data_;
by varnum;
data _null_;
set _last_ end=last;
call symputx(cats('name',_n_),name,'l');
call symputx(cats('type',_n_),type,'l');
call symputx(cats('len',_n_),length,'l');
if last then call symputx('cols',_n_,'l');
run;
%let fmtds=&syslast;
%end;
proc format; /* credit yabwon for special null removal */
value bart ._ - .z = null
value bart (default=40)
%if &missing=NULL %then %do;
._ - .z = null
%end;
%else %do;
._ = [quote()]
. = null
.a - .z = [quote()]
%end;
other = [best.];
data;run; %let tempds=&syslast; /* temp table for spesh char management */
proc sql; drop table &tempds;
data &tempds/view=&tempds;
attrib _all_ label='';
%do i=1 %to &cols;
%if &&type&i=char %then %do;
%do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do;
length &&name&i $32767;
format &&name&i $32767.;
%end;
%end;
set &ds;
%if &fmt=Y %then %do;
set &fmtds;
%end;
%else %do;
set &ds;
%end;
format _numeric_ bart.;
%do i=1 %to &cols;
%if &&type&i=char %then %do;
%do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do;
&&name&i='"'!!trim(prxchange('s/"/\"/',-1,
prxchange('s/'!!'0A'x!!'/\n/',-1,
prxchange('s/'!!'0D'x!!'/\r/',-1,
@@ -189,44 +204,65 @@
%end;
%end;
run;
/* write to temp loc to avoid _webout truncation
- https://support.sas.com/kb/49/325.html */
filename _sjs temp lrecl=131068 encoding='utf-8';
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod ;
if _n_=1 then put "[";
set &tempds;
if _n_>1 then put "," @; put
%if &action=ARR %then "[" ; %else "{" ;
%do i=1 %to &cols;
%do i=1 %to &numcols;
%if &i>1 %then "," ;
%if &action=OBJ %then """&&name&i"":" ;
&&name&i
%end;
%if &action=ARR %then "]" ; %else "}" ; ;
proc sql;
drop view &tempds;
/* now write the long strings to _webout 1 byte at a time */
data _null_;
length filein 8 fileid 8;
filein = fopen("_sjs",'I',1,'B');
fileid = fopen("&jref",'A',1,'B');
rec = '20'x;
filein=fopen("_sjs",'I',1,'B');
fileid=fopen("&jref",'A',1,'B');
rec='20'x;
do while(fread(filein)=0);
rc = fget(filein,rec,1);
rc = fput(fileid, rec);
rc =fwrite(fileid);
rc=fget(filein,rec,1);
rc=fput(fileid, rec);
rc=fwrite(fileid);
end;
rc = fclose(filein);
rc = fclose(fileid);
/* close out the table */
rc=fput(fileid, "]");
rc=fwrite(fileid);
rc=fclose(filein);
rc=fclose(fileid);
run;
filename _sjs clear;
data _null_; file &jref mod encoding='utf-8' ;
put "]";
%end;
proc sql;
drop view &tempds;
drop table &colinfo;
%if &showmeta=YES %then %do;
data _null_; file &jref encoding='utf-8' mod;
put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{";
do i=1 to &numcols;
name=quote(trim(symget(cats('name',i))));
format=quote(trim(symget(cats('fmt',i))));
label=quote(trim(symget(cats('label',i))));
length=quote(trim(symget(cats('length',i))));
type=quote(trim(symget(cats('typelong',i))));
if i>1 then put "," @@;
put name ':{"format":' format ',"label":' label
',"length":' length ',"type":' type '}';
end;
put '}}';
run;
%end;
%end;
%else %if &action=CLOSE %then %do;
data _null_;file &jref encoding='utf-8' mod ;
data _null_; file &jref encoding='utf-8' mod ;
put "}";
run;
%end;

View File

@@ -15,7 +15,7 @@
length is 200 characters.
@param [out] ctl_ds= (0) The control table which controls the actual locking.
Should already be assigned and available. The definition is available by
running mp_coretable.sas as follows: `mp_coretable(LOCKTABLE)`.
running mp_coretable.sas as follows: `%mp_coretable(LOCKTABLE)`.
@param [in] loops= (25) Number of times to check for a lock.
@param [in] loop_secs= (1) Seconds to wait between each lock attempt

View File

@@ -39,14 +39,6 @@
,Server=SASApp
,stptype=2)
<h4> SAS Macros </h4>
@li mf_nobs.sas
@li mf_verifymacvars.sas
@li mm_getdirectories.sas
@li mm_updatestpsourcecode.sas
@li mp_dropmembers.sas
@li mm_getservercontexts.sas
@param stpname= Stored Process name. Avoid spaces - testing has shown that
the check to avoid creating multiple STPs in the same folder with the same
name does not work when the name contains spaces.
@@ -77,6 +69,17 @@
- fileuri
- texturi
<h4> SAS Macros </h4>
@li mf_nobs.sas
@li mf_verifymacvars.sas
@li mm_getdirectories.sas
@li mm_updatestpsourcecode.sas
@li mp_dropmembers.sas
@li mm_getservercontexts.sas
<h4> Related Macros </h4>
@li mm_createwebservice.sas
@version 9.2
@author Allan Bowe

View File

@@ -12,6 +12,7 @@ Usage:
%* parmcards lets us write to a text file from open code ;
filename ft15f001 temp;
parmcards4;
%webout(FETCH)
%* do some sas, any inputs are now already WORK tables;
data example1 example2;
set sashelp.class;
@@ -24,11 +25,8 @@ Usage:
;;;;
%mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001)
<h4> SAS Macros </h4>
@li mm_createstp.sas
@li mf_getuser.sas
@li mm_createfolder.sas
@li mm_deletestp.sas
For more examples of using these web services with the SASjs Adapter, see:
https://github.com/sasjs/adapter#readme
@param path= The full path (in SAS Metadata) where the service will be created
@param name= Stored Process name. Avoid spaces - testing has shown that
@@ -37,16 +35,22 @@ Usage:
@param desc= The description of the service (optional)
@param precode= Space separated list of filerefs, pointing to the code that
needs to be attached to the beginning of the service (optional)
@param code=(ft15f001) Space seperated fileref(s) of the actual code to be
@param code= (ft15f001) Space seperated fileref(s) of the actual code to be
added
@param server=(SASApp) The server which will run the STP. Server name or uri
@param server= (SASApp) The server which will run the STP. Server name or uri
is fine.
@param mDebug=(0) set to 1 to show debug messages in the log
@param replace=(YES) select NO to avoid replacing an existing service in that
@param mDebug= (0) set to 1 to show debug messages in the log
@param replace= (YES) select NO to avoid replacing an existing service in that
location
@param adapter=(sasjs) the macro uses the sasjs adapter by default. To use
@param adapter= (sasjs) the macro uses the sasjs adapter by default. To use
another adapter, add a (different) fileref here.
<h4> SAS Macros </h4>
@li mm_createstp.sas
@li mf_getuser.sas
@li mm_createfolder.sas
@li mm_deletestp.sas
@version 9.2
@author Allan Bowe
@@ -89,9 +93,15 @@ data _null_;
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
/* WEBOUT BEGIN */
put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP,dbg=0 ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP ';
put ' ,dbg=0 /* DEPRECATED */ ';
put ' ,missing=NULL ';
put ' ,showmeta=NO ';
put ')/*/STORE SOURCE*/; ';
put '%put output location=&jref; ';
put '%local tempds colinfo fmtds i numcols; ';
put '%let numcols=0; ';
put ' ';
put '%if &action=OPEN %then %do; ';
put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8'' ; ';
@@ -100,118 +110,124 @@ data _null_;
put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' options validvarname=upcase; ';
put ' data _null_;file &jref mod encoding=''utf-8'' ; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' ';
put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds ';
put ' out=_data_(keep=name type length format formatl formatd varnum label); ';
put ' run; ';
put ' %let colinfo=%scan(&syslast,2,.); ';
put ' proc sort data=&colinfo; ';
put ' by varnum; ';
put ' run; ';
put ' /* move meta to mac vars */ ';
put ' data _null_; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); ';
put ' /* fix formats */ ';
put ' if type=2 or type=6 then do; ';
put ' typelong=''char''; ';
put ' length fmt $49.; ';
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; ';
put ' else do; ';
put ' typelong=''num''; ';
put ' if format='''' then fmt=''best.''; ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; ';
put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' ';
put ' %if &engine=PROCJSON %then %do; ';
put ' data;run;%let tempds=&syslast; ';
put ' proc sql;drop table &tempds; ';
put ' %if &missing=STRING %then %do; ';
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; ';
put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty ';
put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; ';
put ' proc sql;drop view &tempds; ';
put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; ';
put ' %local cols i tempds; ';
put ' %let cols=0; ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
put ' %datastep: ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
put ' %then %do; ';
put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
put ' %return; ';
put ' %end; ';
put ' %if &fmt=Y %then %do; ';
put ' %put converting every variable to a formatted variable; ';
put ' /* see mp_ds2fmtds.sas for source */ ';
put ' proc contents noprint data=&ds ';
put ' out=_data_(keep=name type length format formatl formatd varnum); ';
put ' run; ';
put ' proc sort; ';
put ' by varnum; ';
put ' run; ';
put ' %local fmtds; ';
put ' %let fmtds=%scan(&syslast,2,.); ';
put ' /* prepare formats and varnames */ ';
put ' data _null_; ';
put ' if _n_=1 then call symputx(''nobs'',nobs,''l''); ';
put ' set &fmtds end=last nobs=nobs; ';
put ' name=upcase(name); ';
put ' /* fix formats */ ';
put ' if type=2 or type=6 then do; ';
put ' length fmt $49.; ';
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; ';
put ' else do; ';
put ' if format='''' then fmt=''best.''; ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; ';
put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' run; ';
put ' data &fmtds; ';
put ' %if &fmt=Y %then %do; ';
put ' data _data_; ';
put ' /* rename on entry */ ';
put ' set &ds(rename=( ';
put ' %local i; ';
put ' %do i=1 %to &nobs; ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i ';
put ' %end; ';
put ' )); ';
put ' %do i=1 %to &nobs; ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
put ' drop &&newname&i; ';
put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; ';
put ' %let ds=&fmtds; ';
put ' %end; /* &fmt=Y */ ';
put ' data _null_;file &jref mod encoding=''utf-8'' ; ';
put ' put "["; call symputx(''cols'',0,''l''); ';
put ' proc sort ';
put ' data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
put ' out=_data_; ';
put ' by varnum; ';
put ' ';
put ' data _null_; ';
put ' set _last_ end=last; ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''len'',_n_),length,''l''); ';
put ' if last then call symputx(''cols'',_n_,''l''); ';
put ' run; ';
put ' %let fmtds=&syslast; ';
put ' %end; ';
put ' ';
put ' proc format; /* credit yabwon for special null removal */ ';
put ' value bart ._ - .z = null ';
put ' value bart (default=40) ';
put ' %if &missing=NULL %then %do; ';
put ' ._ - .z = null ';
put ' %end; ';
put ' %else %do; ';
put ' ._ = [quote()] ';
put ' . = null ';
put ' .a - .z = [quote()] ';
put ' %end; ';
put ' other = [best.]; ';
put ' ';
put ' data;run; %let tempds=&syslast; /* temp table for spesh char management */ ';
put ' proc sql; drop table &tempds; ';
put ' data &tempds/view=&tempds; ';
put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &cols; ';
put ' %if &&type&i=char %then %do; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; ';
put ' format &&name&i $32767.; ';
put ' %end; ';
put ' %end; ';
put ' set &ds; ';
put ' %if &fmt=Y %then %do; ';
put ' set &fmtds; ';
put ' %end; ';
put ' %else %do; ';
put ' set &ds; ';
put ' %end; ';
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &cols; ';
put ' %if &&type&i=char %then %do; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
@@ -221,49 +237,72 @@ data _null_;
put ' %end; ';
put ' %end; ';
put ' run; ';
put ' ';
put ' /* write to temp loc to avoid _webout truncation ';
put ' - https://support.sas.com/kb/49/325.html */ ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; ';
put ' set &tempds; ';
put ' if _n_>1 then put "," @; put ';
put ' %if &action=ARR %then "[" ; %else "{" ; ';
put ' %do i=1 %to &cols; ';
put ' %do i=1 %to &numcols; ';
put ' %if &i>1 %then "," ; ';
put ' %if &action=OBJ %then """&&name&i"":" ; ';
put ' &&name&i ';
put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' /* now write the long strings to _webout 1 byte at a time */ ';
put ' data _null_; ';
put ' length filein 8 fileid 8; ';
put ' filein = fopen("_sjs",''I'',1,''B''); ';
put ' fileid = fopen("&jref",''A'',1,''B''); ';
put ' rec = ''20''x; ';
put ' filein=fopen("_sjs",''I'',1,''B''); ';
put ' fileid=fopen("&jref",''A'',1,''B''); ';
put ' rec=''20''x; ';
put ' do while(fread(filein)=0); ';
put ' rc = fget(filein,rec,1); ';
put ' rc = fput(fileid, rec); ';
put ' rc =fwrite(fileid); ';
put ' rc=fget(filein,rec,1); ';
put ' rc=fput(fileid, rec); ';
put ' rc=fwrite(fileid); ';
put ' end; ';
put ' rc = fclose(filein); ';
put ' rc = fclose(fileid); ';
put ' /* close out the table */ ';
put ' rc=fput(fileid, "]"); ';
put ' rc=fwrite(fileid); ';
put ' rc=fclose(filein); ';
put ' rc=fclose(fileid); ';
put ' run; ';
put ' filename _sjs clear; ';
put ' data _null_; file &jref mod encoding=''utf-8'' ; ';
put ' put "]"; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' drop table &colinfo; ';
put ' ';
put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); ';
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
put ' label=quote(trim(symget(cats(''label'',i)))); ';
put ' length=quote(trim(symget(cats(''length'',i)))); ';
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
put ' if i>1 then put "," @@; ';
put ' put name '':{"format":'' format '',"label":'' label ';
put ' '',"length":'' length '',"type":'' type ''}''; ';
put ' end; ';
put ' put ''}}''; ';
put ' run; ';
put ' %end; ';
put '%end; ';
put ' ';
put '%else %if &action=CLOSE %then %do; ';
put ' data _null_;file &jref encoding=''utf-8'' mod ; ';
put ' data _null_; file &jref encoding=''utf-8'' mod ; ';
put ' put "}"; ';
put ' run; ';
put '%end; ';
put '%mend mp_jsonout; ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y); ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL ';
put ' ,showmeta=NO ';
put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; ';
put '%local i tempds jsonengine; ';
@@ -321,14 +360,15 @@ data _null_;
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' put ''>>weboutBEGIN<<''; ';
put ' %end; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; ';
put ' ';
put '%end; ';
put ' ';
put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
put ' ,engine=&jsonengine,dbg=%str(&_debug) ';
put ' ,engine=&jsonengine,missing=&missing,showmeta=&showmeta ';
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
@@ -349,9 +389,6 @@ data _null_;
put ' put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; ';
put ' proc contents noprint data=&wt ';
put ' out=_data_ (keep=name type length format:); ';
put ' run;%let tempds=%scan(&syslast,2,.); ';
put ' data _null_; file &fref mod encoding=''utf-8''; ';
put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); ';
@@ -361,8 +398,7 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=&jsonengine) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=&jsonengine) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
put ' data _null_; file &fref mod encoding=''utf-8''; ';
put ' put "}"; ';
put ' %end; ';
@@ -391,6 +427,9 @@ data _null_;
put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
put ' memsize=quote(cats(memsize)); ';
put ' put '',"MEMSIZE" : '' memsize; ';
put ' put "}" @; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' put ''>>weboutEND<<''; ';
@@ -418,8 +457,10 @@ data _null_;
put ' ';
put '%mend mf_getuser; ';
/* WEBOUT END */
put '%macro webout(action,ds,dslabel=,fmt=);';
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt)';
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta';
put ' )';
put '%mend;';
run;

View File

@@ -23,17 +23,25 @@
%mm_webout(CLOSE)
@param action Either FETCH, OPEN, ARR, OBJ or CLOSE
@param ds The dataset to send back to the frontend
@param dslabel= value to use instead of the real name for sending to JSON
@param fmt=(Y) Set to N to send back unformatted values
@param fref=(_webout) The fileref to which to write the JSON
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@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 [in] fmt=(Y) Set to N to send back unformatted values
@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
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
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,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@version 9.3
@author Allan Bowe
**/
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y);
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
,showmeta=NO
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables;
%local i tempds jsonengine;
@@ -91,14 +99,15 @@
%if %str(&_debug) ge 131 %then %do;
put '>>weboutBEGIN<<';
%end;
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"';
run;
%end;
%else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,engine=&jsonengine,dbg=%str(&_debug)
,engine=&jsonengine,missing=&missing,showmeta=&showmeta
)
%end;
%else %if &action=CLOSE %then %do;
@@ -119,9 +128,6 @@
put ",""WORK"":{";
%do i=1 %to &wtcnt;
%let wt=&&wt&i;
proc contents noprint data=&wt
out=_data_ (keep=name type length format:);
run;%let tempds=%scan(&syslast,2,.);
data _null_; file &fref mod encoding='utf-8';
dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS');
@@ -131,8 +137,7 @@
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=&jsonengine)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=&jsonengine)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
data _null_; file &fref mod encoding='utf-8';
put "}";
%end;
@@ -161,6 +166,9 @@
put ',"SYSVLONG" : ' sysvlong;
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize;
put "}" @;
%if %str(&_debug) ge 131 %then %do;
put '>>weboutEND<<';

90
package-lock.json generated
View File

@@ -10,13 +10,13 @@
"ts-loader": "^9.2.6"
},
"devDependencies": {
"@sasjs/cli": "^2.39.0"
"@sasjs/cli": "^3.4.1"
}
},
"node_modules/@sasjs/adapter": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.12.0.tgz",
"integrity": "sha512-zzIuohhR8KUDl3DfIFOW38gv3LADPnOBCLOvLoKu4hH5R/UJDkjZ/Gdgc8B35vI7aOprYOLK/T5D/Z44OaTkqw==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-3.3.1.tgz",
"integrity": "sha512-rmdOG+sjmwGipq1AHczwEXNUlzRFV5efj89neVVJWQMZR6JBC1O6Dr9HjEyJHPKcnQ6z3vzH9rRA2PGi5lgMhA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@@ -29,16 +29,16 @@
}
},
"node_modules/@sasjs/cli": {
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.39.0.tgz",
"integrity": "sha512-n2LcU4n0QCEbUpXqZnBz/Ey5Td0nMJmgJpZRymMGfYEM0Y0x/CeXemd+kXHPjUvgQ+FX+SQzcvUQTEY/YlT4hA==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-3.4.1.tgz",
"integrity": "sha512-voc0/h8bkRAqrj7Pu1egYfCOSFLlLrrh9bXVLuGvSvWK81MezRZnWciTHlQGc9BgO2wU+LrQ0baIMd6u/HMB5Q==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@sasjs/adapter": "2.12.0",
"@sasjs/core": "2.45.2",
"@sasjs/adapter": "3.3.1",
"@sasjs/core": "^3.8.0",
"@sasjs/lint": "1.11.2",
"@sasjs/utils": "2.32.0",
"@sasjs/utils": "2.34.1",
"chalk": "4.1.2",
"csv-stringify": "5.6.5",
"dotenv": "10.0.0",
@@ -62,10 +62,13 @@
}
},
"node_modules/@sasjs/core": {
"version": "2.45.2",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.45.2.tgz",
"integrity": "sha512-tg+oZCD8GFMXsg+vDL66LMnyU+t151Hrqd7yl+pMXH2qwkA14N/j6QdkTBZOchskqOA/3PnpOlAZN/xxMW2gdg==",
"dev": true
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-3.8.1.tgz",
"integrity": "sha512-Yxak+WZwh8Z9IKcbi7aDDTRCcKlI6IUp7Ujavkec5pWMj3a2FSlLxu23lY2ERTBe7wMCGiaU7AseWlKcgd5joA==",
"dev": true,
"dependencies": {
"ts-loader": "^9.2.6"
}
},
"node_modules/@sasjs/lint": {
"version": "1.11.2",
@@ -77,9 +80,9 @@
}
},
"node_modules/@sasjs/utils": {
"version": "2.32.0",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.32.0.tgz",
"integrity": "sha512-xnvdEuI4PhTtulcdDEIMK7IxVj9bOMU1JTnxRuSEKWcsclY9P9Fw3cnMOOEgXCDffrOPn3f54DP7Wb1GXd+f8g==",
"version": "2.34.1",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.34.1.tgz",
"integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@@ -89,8 +92,11 @@
"cli-table": "^0.3.6",
"consola": "^2.15.0",
"csv-stringify": "^5.6.5",
"find": "0.3.0",
"fs-extra": "^10.0.0",
"jwt-decode": "^3.1.2",
"lodash.groupby": "4.6.0",
"lodash.uniqby": "4.7.0",
"prompts": "^2.4.1",
"rimraf": "^3.0.2",
"valid-url": "^1.0.9"
@@ -1051,9 +1057,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
"version": "1.14.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
"dev": true,
"funding": [
{
@@ -2581,9 +2587,9 @@
},
"dependencies": {
"@sasjs/adapter": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.12.0.tgz",
"integrity": "sha512-zzIuohhR8KUDl3DfIFOW38gv3LADPnOBCLOvLoKu4hH5R/UJDkjZ/Gdgc8B35vI7aOprYOLK/T5D/Z44OaTkqw==",
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-3.3.1.tgz",
"integrity": "sha512-rmdOG+sjmwGipq1AHczwEXNUlzRFV5efj89neVVJWQMZR6JBC1O6Dr9HjEyJHPKcnQ6z3vzH9rRA2PGi5lgMhA==",
"dev": true,
"requires": {
"@sasjs/utils": "^2.32.0",
@@ -2595,15 +2601,15 @@
}
},
"@sasjs/cli": {
"version": "2.39.0",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.39.0.tgz",
"integrity": "sha512-n2LcU4n0QCEbUpXqZnBz/Ey5Td0nMJmgJpZRymMGfYEM0Y0x/CeXemd+kXHPjUvgQ+FX+SQzcvUQTEY/YlT4hA==",
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-3.4.1.tgz",
"integrity": "sha512-voc0/h8bkRAqrj7Pu1egYfCOSFLlLrrh9bXVLuGvSvWK81MezRZnWciTHlQGc9BgO2wU+LrQ0baIMd6u/HMB5Q==",
"dev": true,
"requires": {
"@sasjs/adapter": "2.12.0",
"@sasjs/core": "2.45.2",
"@sasjs/adapter": "3.3.1",
"@sasjs/core": "^3.8.0",
"@sasjs/lint": "1.11.2",
"@sasjs/utils": "2.32.0",
"@sasjs/utils": "2.34.1",
"chalk": "4.1.2",
"csv-stringify": "5.6.5",
"dotenv": "10.0.0",
@@ -2624,10 +2630,13 @@
}
},
"@sasjs/core": {
"version": "2.45.2",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.45.2.tgz",
"integrity": "sha512-tg+oZCD8GFMXsg+vDL66LMnyU+t151Hrqd7yl+pMXH2qwkA14N/j6QdkTBZOchskqOA/3PnpOlAZN/xxMW2gdg==",
"dev": true
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-3.8.1.tgz",
"integrity": "sha512-Yxak+WZwh8Z9IKcbi7aDDTRCcKlI6IUp7Ujavkec5pWMj3a2FSlLxu23lY2ERTBe7wMCGiaU7AseWlKcgd5joA==",
"dev": true,
"requires": {
"ts-loader": "^9.2.6"
}
},
"@sasjs/lint": {
"version": "1.11.2",
@@ -2639,9 +2648,9 @@
}
},
"@sasjs/utils": {
"version": "2.32.0",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.32.0.tgz",
"integrity": "sha512-xnvdEuI4PhTtulcdDEIMK7IxVj9bOMU1JTnxRuSEKWcsclY9P9Fw3cnMOOEgXCDffrOPn3f54DP7Wb1GXd+f8g==",
"version": "2.34.1",
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.34.1.tgz",
"integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==",
"dev": true,
"requires": {
"@types/fs-extra": "^9.0.11",
@@ -2650,8 +2659,11 @@
"cli-table": "^0.3.6",
"consola": "^2.15.0",
"csv-stringify": "^5.6.5",
"find": "0.3.0",
"fs-extra": "^10.0.0",
"jwt-decode": "^3.1.2",
"lodash.groupby": "4.6.0",
"lodash.uniqby": "4.7.0",
"prompts": "^2.4.1",
"rimraf": "^3.0.2",
"valid-url": "^1.0.9"
@@ -3421,9 +3433,9 @@
}
},
"follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
"version": "1.14.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
"dev": true
},
"form-data": {

View File

@@ -33,9 +33,9 @@
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
},
"devDependencies": {
"@sasjs/cli": "^2.39.0"
"@sasjs/cli": "^3.4.1"
},
"dependencies": {
"ts-loader": "^9.2.6"
}
}
}

View File

@@ -20,11 +20,17 @@
%ms_webout(CLOSE)
@param action Either FETCH, OPEN, ARR, OBJ or CLOSE
@param ds The dataset to send back to the frontend
@param dslabel= value to use instead of the real name for sending to JSON
@param fmt=(Y) Set to N to send back unformatted values
@param fref=(_webout) The fileref to which to write the JSON
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@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 [in] fmt= (Y) Set to N to send back unformatted values
@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
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
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,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@@ -39,7 +45,9 @@
**/
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y);
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
,showmeta=NO
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables;
@@ -91,7 +99,7 @@
%else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,engine=DATASTEP,dbg=%str(&_debug)
,engine=DATASTEP,missing=&missing,showmeta=&showmeta
)
%end;
%else %if &action=CLOSE %then %do;
@@ -112,9 +120,6 @@
put ",""WORK"":{";
%do i=1 %to &wtcnt;
%let wt=&&wt&i;
proc contents noprint data=&wt
out=_data_ (keep=name type length format:);
run;%let tempds=%scan(&syslast,2,.);
data _null_; file &fref mod encoding='utf-8' termstr=lf;
dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS');
@@ -124,8 +129,7 @@
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
data _null_; file &fref mod encoding='utf-8' termstr=lf;
put "}";
%end;

View File

@@ -34,6 +34,11 @@ data work.test;
call symputx('dtval',dtval);
run;
%mp_assert(
iftrue=(&syscc=0),
desc=Checking for error condition,
outds=work.test_results
)
%mp_assert(
iftrue=(&dtval=&compare),

View File

@@ -0,0 +1,52 @@
/**
@file
@brief Testing mp_jsonout.sas macro with special missings
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@li mp_assert.sas
**/
filename webref temp;
data demo;
do x=._,.,.a,.b,.c,.d,.e,-99, 0, 1,2, 3.333333;
output;
end;
run;
%mp_jsonout(OPEN,jref=webref)
%mp_jsonout(OBJ,demo,jref=webref,fmt=N,missing=STRING)
%mp_jsonout(CLOSE,jref=webref)
data _null_;
infile webref;
input;
putlog _infile_;
run;
libname web JSON fileref=webref;
/* proc json turns to char - so switch back to numeric */
data work.test(keep=x);
set web.demo(rename=(x=y));
if y ='_' then x=._;
else if anyalpha(y) then x=input(cats(".",y),best.);
else x=input(y,best.);
put (_all_)(=);
run;
%mp_assert(
iftrue=(&syscc=0),
desc=Checking for error condition with special missing export,
outds=work.test_results
)
proc compare base=work.demo compare=work.test;
quit;
%mp_assert(
iftrue=(&sysinfo=0),
desc=Returned json is identical to input table for all special missings,
outds=work.test_results
)

View File

@@ -6,12 +6,13 @@
@li mp_lockanytable.sas
@li mp_assertcols.sas
@li mp_assertcolvals.sas
@li mp_coretable.sas
**/
/* check create table */
%mp_lockanytable(MAKETABLE, ctl_ds=work.controller)
%mp_coretable(LOCKTABLE,libds=work.controller)
%mp_assertcols(work.controller,
cols=lock_status_cd lock_lib lock_ds lock_user_nm lock_ref lock_pid

View File

@@ -54,9 +54,9 @@
that location
@param [in] adapter= the macro uses the sasjs adapter by default. To use
another adapter, add a (different) fileref here.
@param [in] contextname= Choose a specific context on which to run the Job. Leave
blank to use the default context. From Viya 3.5 it is possible to configure
a shared context - see
@param [in] contextname= Choose a specific context on which to run the Job.
Leave blank to use the default context. From Viya 3.5 it is possible to
configure a shared context - see
https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
@param [in] mdebug=(0) set to 1 to enable DEBUG messages
@@ -237,9 +237,15 @@ data _null_;
put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */";
/* WEBOUT BEGIN */
put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP,dbg=0 ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP ';
put ' ,dbg=0 /* DEPRECATED */ ';
put ' ,missing=NULL ';
put ' ,showmeta=NO ';
put ')/*/STORE SOURCE*/; ';
put '%put output location=&jref; ';
put '%local tempds colinfo fmtds i numcols; ';
put '%let numcols=0; ';
put ' ';
put '%if &action=OPEN %then %do; ';
put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8'' ; ';
@@ -248,118 +254,124 @@ data _null_;
put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' options validvarname=upcase; ';
put ' data _null_;file &jref mod encoding=''utf-8'' ; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
put ' put ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' ';
put ' /* grab col defs */ ';
put ' proc contents noprint data=&ds ';
put ' out=_data_(keep=name type length format formatl formatd varnum label); ';
put ' run; ';
put ' %let colinfo=%scan(&syslast,2,.); ';
put ' proc sort data=&colinfo; ';
put ' by varnum; ';
put ' run; ';
put ' /* move meta to mac vars */ ';
put ' data _null_; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); ';
put ' /* fix formats */ ';
put ' if type=2 or type=6 then do; ';
put ' typelong=''char''; ';
put ' length fmt $49.; ';
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; ';
put ' else do; ';
put ' typelong=''num''; ';
put ' if format='''' then fmt=''best.''; ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; ';
put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' ';
put ' %if &engine=PROCJSON %then %do; ';
put ' data;run;%let tempds=&syslast; ';
put ' proc sql;drop table &tempds; ';
put ' %if &missing=STRING %then %do; ';
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; ';
put ' %end; ';
put ' data &tempds /view=&tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
put ' proc json out=&jref pretty ';
put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; ';
put ' proc sql;drop view &tempds; ';
put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; ';
put ' %local cols i tempds; ';
put ' %let cols=0; ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
put ' %datastep: ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
put ' %then %do; ';
put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
put ' %return; ';
put ' %end; ';
put ' %if &fmt=Y %then %do; ';
put ' %put converting every variable to a formatted variable; ';
put ' /* see mp_ds2fmtds.sas for source */ ';
put ' proc contents noprint data=&ds ';
put ' out=_data_(keep=name type length format formatl formatd varnum); ';
put ' run; ';
put ' proc sort; ';
put ' by varnum; ';
put ' run; ';
put ' %local fmtds; ';
put ' %let fmtds=%scan(&syslast,2,.); ';
put ' /* prepare formats and varnames */ ';
put ' data _null_; ';
put ' if _n_=1 then call symputx(''nobs'',nobs,''l''); ';
put ' set &fmtds end=last nobs=nobs; ';
put ' name=upcase(name); ';
put ' /* fix formats */ ';
put ' if type=2 or type=6 then do; ';
put ' length fmt $49.; ';
put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; ';
put ' else do; ';
put ' if format='''' then fmt=''best.''; ';
put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; ';
put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' run; ';
put ' data &fmtds; ';
put ' %if &fmt=Y %then %do; ';
put ' data _data_; ';
put ' /* rename on entry */ ';
put ' set &ds(rename=( ';
put ' %local i; ';
put ' %do i=1 %to &nobs; ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i ';
put ' %end; ';
put ' )); ';
put ' %do i=1 %to &nobs; ';
put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
put ' drop &&newname&i; ';
put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; ';
put ' %let ds=&fmtds; ';
put ' %end; /* &fmt=Y */ ';
put ' data _null_;file &jref mod encoding=''utf-8'' ; ';
put ' put "["; call symputx(''cols'',0,''l''); ';
put ' proc sort ';
put ' data=sashelp.vcolumn(where=(libname=''WORK'' & memname="%upcase(&ds)")) ';
put ' out=_data_; ';
put ' by varnum; ';
put ' ';
put ' data _null_; ';
put ' set _last_ end=last; ';
put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''len'',_n_),length,''l''); ';
put ' if last then call symputx(''cols'',_n_,''l''); ';
put ' run; ';
put ' %let fmtds=&syslast; ';
put ' %end; ';
put ' ';
put ' proc format; /* credit yabwon for special null removal */ ';
put ' value bart ._ - .z = null ';
put ' value bart (default=40) ';
put ' %if &missing=NULL %then %do; ';
put ' ._ - .z = null ';
put ' %end; ';
put ' %else %do; ';
put ' ._ = [quote()] ';
put ' . = null ';
put ' .a - .z = [quote()] ';
put ' %end; ';
put ' other = [best.]; ';
put ' ';
put ' data;run; %let tempds=&syslast; /* temp table for spesh char management */ ';
put ' proc sql; drop table &tempds; ';
put ' data &tempds/view=&tempds; ';
put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &cols; ';
put ' %if &&type&i=char %then %do; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; ';
put ' format &&name&i $32767.; ';
put ' %end; ';
put ' %end; ';
put ' set &ds; ';
put ' %if &fmt=Y %then %do; ';
put ' set &fmtds; ';
put ' %end; ';
put ' %else %do; ';
put ' set &ds; ';
put ' %end; ';
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &cols; ';
put ' %if &&type&i=char %then %do; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
@@ -369,49 +381,72 @@ data _null_;
put ' %end; ';
put ' %end; ';
put ' run; ';
put ' ';
put ' /* write to temp loc to avoid _webout truncation ';
put ' - https://support.sas.com/kb/49/325.html */ ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; ';
put ' set &tempds; ';
put ' if _n_>1 then put "," @; put ';
put ' %if &action=ARR %then "[" ; %else "{" ; ';
put ' %do i=1 %to &cols; ';
put ' %do i=1 %to &numcols; ';
put ' %if &i>1 %then "," ; ';
put ' %if &action=OBJ %then """&&name&i"":" ; ';
put ' &&name&i ';
put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' /* now write the long strings to _webout 1 byte at a time */ ';
put ' data _null_; ';
put ' length filein 8 fileid 8; ';
put ' filein = fopen("_sjs",''I'',1,''B''); ';
put ' fileid = fopen("&jref",''A'',1,''B''); ';
put ' rec = ''20''x; ';
put ' filein=fopen("_sjs",''I'',1,''B''); ';
put ' fileid=fopen("&jref",''A'',1,''B''); ';
put ' rec=''20''x; ';
put ' do while(fread(filein)=0); ';
put ' rc = fget(filein,rec,1); ';
put ' rc = fput(fileid, rec); ';
put ' rc =fwrite(fileid); ';
put ' rc=fget(filein,rec,1); ';
put ' rc=fput(fileid, rec); ';
put ' rc=fwrite(fileid); ';
put ' end; ';
put ' rc = fclose(filein); ';
put ' rc = fclose(fileid); ';
put ' /* close out the table */ ';
put ' rc=fput(fileid, "]"); ';
put ' rc=fwrite(fileid); ';
put ' rc=fclose(filein); ';
put ' rc=fclose(fileid); ';
put ' run; ';
put ' filename _sjs clear; ';
put ' data _null_; file &jref mod encoding=''utf-8'' ; ';
put ' put "]"; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' drop view &tempds; ';
put ' drop table &colinfo; ';
put ' ';
put ' %if &showmeta=YES %then %do; ';
put ' data _null_; file &jref encoding=''utf-8'' mod; ';
put ' put ", ""$%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":{""vars"":{"; ';
put ' do i=1 to &numcols; ';
put ' name=quote(trim(symget(cats(''name'',i)))); ';
put ' format=quote(trim(symget(cats(''fmt'',i)))); ';
put ' label=quote(trim(symget(cats(''label'',i)))); ';
put ' length=quote(trim(symget(cats(''length'',i)))); ';
put ' type=quote(trim(symget(cats(''typelong'',i)))); ';
put ' if i>1 then put "," @@; ';
put ' put name '':{"format":'' format '',"label":'' label ';
put ' '',"length":'' length '',"type":'' type ''}''; ';
put ' end; ';
put ' put ''}}''; ';
put ' run; ';
put ' %end; ';
put '%end; ';
put ' ';
put '%else %if &action=CLOSE %then %do; ';
put ' data _null_;file &jref encoding=''utf-8'' mod ; ';
put ' data _null_; file &jref encoding=''utf-8'' mod ; ';
put ' put "}"; ';
put ' run; ';
put '%end; ';
put '%mend mp_jsonout; ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y); ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL ';
put ' ,showmeta=NO ';
put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI; ';
put '%if %index("&_debug",log) %then %let _debug=131; ';
@@ -533,12 +568,13 @@ data _null_;
put ' ';
put ' /* setup json */ ';
put ' data _null_;file &fref; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; ';
put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; ';
put '%end; ';
put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
put ' ,jref=&fref,engine=DATASTEP,dbg=%str(&_debug) ';
put ' ,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta ';
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
@@ -558,9 +594,6 @@ data _null_;
put ' data _null_; file &fref mod; put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; ';
put ' proc contents noprint data=&wt ';
put ' out=_data_ (keep=name type length format:); ';
put ' run;%let tempds=%scan(&syslast,2,.); ';
put ' data _null_; file &fref mod; ';
put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); ';
@@ -570,8 +603,7 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
put ' data _null_; file &fref mod;put "}"; ';
put ' %end; ';
put ' data _null_; file &fref mod;put "}";run; ';
@@ -596,6 +628,9 @@ data _null_;
put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
put ' memsize=quote(cats(memsize)); ';
put ' put '',"MEMSIZE" : '' memsize; ';
put ' put "}"; ';
put ' ';
put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; ';
@@ -629,8 +664,10 @@ data _null_;
put '%global __program _program;';
put '%let _program=%sysfunc(coalescec(&__program,&_program));';
put ' ';
put '%macro webout(action,ds,dslabel=,fmt=);';
put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt)';
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta';
put ' )';
put '%mend;';
run;

View File

@@ -20,13 +20,19 @@
%mv_webout(CLOSE)
@param action Either OPEN, ARR, OBJ or CLOSE
@param ds The dataset to send back to the frontend
@param _webout= fileref for returning the json
@param fref=(_mvwtemp) Temp fileref to which to write the output
@param dslabel= value to use instead of the real name for sending to JSON
@param fmt=(Y) change to N to strip formats from output
@param stream=(Y) Change to N if not streaming to _webout
@param [in] action Either OPEN, ARR, OBJ or CLOSE
@param [in] ds The dataset to send back to the frontend
@param [in] _webout= fileref for returning the json
@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 [in] fmt=(Y) change to N to strip formats from output
@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
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
@param [in] showmeta= (NO) Set to YES to output metadata alongside each table,
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,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@@ -36,7 +42,9 @@
@author Allan Bowe, source: https://github.com/sasjs/core
**/
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y);
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL
,showmeta=NO
);
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI;
%if %index("&_debug",log) %then %let _debug=131;
@@ -158,12 +166,13 @@
/* setup json */
data _null_;file &fref;
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"';
run;
%end;
%else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
,jref=&fref,engine=DATASTEP,dbg=%str(&_debug)
,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta
)
%end;
%else %if &action=CLOSE %then %do;
@@ -183,9 +192,6 @@
data _null_; file &fref mod; put ",""WORK"":{";
%do i=1 %to &wtcnt;
%let wt=&&wt&i;
proc contents noprint data=&wt
out=_data_ (keep=name type length format:);
run;%let tempds=%scan(&syslast,2,.);
data _null_; file &fref mod;
dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS');
@@ -195,8 +201,7 @@
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
data _null_; file &fref mod;put "}";
%end;
data _null_; file &fref mod;put "}";run;
@@ -221,6 +226,9 @@
put ',"SYSVLONG" : ' sysvlong;
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize;
put "}";
%if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;