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

Compare commits

...

11 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
13 changed files with 947 additions and 738 deletions

794
all.sas

File diff suppressed because it is too large Load Diff

View File

@@ -6,8 +6,7 @@
Credit for the rename approach: 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 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) %mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX)
@@ -23,12 +22,12 @@
X CMD) do please raise an issue! X CMD) do please raise an issue!
@param [in] path= for which to return contents @param [in] path= (%sysfunc(pathname(work))) Path for which to return contents
@param [in] fref= Provide a DISK engine fileref as an alternative to PATH @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 @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 subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
recursion, set to MAX. 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 @param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname
functions are used to scan all properties - any characters that are not 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 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. - OS SPECIFIC variables, if <code>getattrs=</code> is used.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_existds.sas
@li mf_getvarlist.sas
@li mf_wordsinstr1butnotstr2.sas
@li mp_dropmembers.sas @li mp_dropmembers.sas
<h4> Related Macros </h4> <h4> Related Macros </h4>
@li mp_dirlist.test.sas @li mp_dirlist.test.sas
@version 9.2 @version 9.2
@author Allan Bowe
**/ **/
%macro mp_dirlist(path=%sysfunc(pathname(work)) %macro mp_dirlist(path=%sysfunc(pathname(work))
@@ -193,9 +194,24 @@ data &out_ds;
set &out_ds(where=(filepath ne '')); set &out_ds(where=(filepath ne ''));
run; run;
/* update main table */ /**
proc append base=&outds data=&out_ds; * The above transpose can mean that some updates create additional columns.
run; * 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 */ /* recursive call */
%if &maxdepth>&level or &maxdepth=MAX %then %do; %if &maxdepth>&level or &maxdepth=MAX %then %do;

View File

@@ -19,11 +19,12 @@
%mp_jsonout(OPEN,jref=tmp) %mp_jsonout(OPEN,jref=tmp)
%mp_jsonout(OBJ,class,jref=tmp) %mp_jsonout(OBJ,class,jref=tmp)
%mp_jsonout(OBJ,class,dslabel=class2,jref=tmp,showmeta=YES)
%mp_jsonout(CLOSE,jref=tmp) %mp_jsonout(CLOSE,jref=tmp)
data _null_; data _null_;
infile tmp; infile tmp;
input;list; input;putlog _infile_;
run; run;
If you are building web apps with SAS then you are strongly encouraged to use If you are building web apps with SAS then you are strongly encouraged to use
@@ -45,6 +46,10 @@
@li DATASTEP (more reliable when data has non standard characters) @li DATASTEP (more reliable when data has non standard characters)
@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"`)
@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> <h4> Related Macros <h4>
@li mp_ds2fmtds.sas @li mp_ds2fmtds.sas
@@ -55,11 +60,15 @@
**/ **/
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP %macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
,engine=DATASTEP
,dbg=0 /* DEPRECATED */ ,dbg=0 /* DEPRECATED */
,missing=NULL ,missing=NULL
,showmeta=NO
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%put &sysmacroname: output location=&jref; %local tempds colinfo fmtds i numcols;
%let numcols=0;
%if &action=OPEN %then %do; %if &action=OPEN %then %do;
options nobomfile; options nobomfile;
data _null_;file &jref encoding='utf-8' ; data _null_;file &jref encoding='utf-8' ;
@@ -68,17 +77,61 @@
%end; %end;
%else %if (&action=ARR or &action=OBJ) %then %do; %else %if (&action=ARR or &action=OBJ) %then %do;
options validvarname=upcase; 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)))"":"; 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; %if &engine=PROCJSON %then %do;
%if &missing=STRING %then %do; %if &missing=STRING %then %do;
%put &sysmacroname: Special Missings are not supported in proc json.; %put &sysmacroname: Special Missings not supported in proc json.;
%put &sysmacroname: Switching to DATASTEP engine; %put &sysmacroname: Switching to DATASTEP engine;
%goto datastep; %goto datastep;
%end; %end;
data;run;%let tempds=&syslast;
proc sql;drop table &tempds;
data &tempds /view=&tempds;set &ds; data &tempds /view=&tempds;set &ds;
%if &fmt=N %then format _numeric_ best32.;; %if &fmt=N %then format _numeric_ best32.;;
/* PRETTY is necessary to avoid line truncation in large files */ /* PRETTY is necessary to avoid line truncation in large files */
@@ -86,91 +139,35 @@
%if &action=ARR %then nokeys ; %if &action=ARR %then nokeys ;
;export &tempds / nosastags fmtnumeric; ;export &tempds / nosastags fmtnumeric;
run; run;
proc sql;drop view &tempds;
%end; %end;
%else %if &engine=DATASTEP %then %do; %else %if &engine=DATASTEP %then %do;
%datastep: %datastep:
%local cols i tempds; %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1
%let cols=0; %then %do;
%if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do;
%put &sysmacroname: &ds NOT FOUND!!!; %put &sysmacroname: &ds NOT FOUND!!!;
%return; %return;
%end; %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'); %if &fmt=Y %then %do;
call symputx(cats('newname',_n_),newname,'l'); data _data_;
call symputx(cats('len',_n_),newlen,'l');
call symputx(cats('fmt',_n_),fmt,'l');
call symputx(cats('type',_n_),type,'l');
run;
data &fmtds;
/* rename on entry */ /* rename on entry */
set &ds(rename=( set &ds(rename=(
%local i; %do i=1 %to &numcols;
%do i=1 %to &nobs;
&&name&i=&&newname&i &&name&i=&&newname&i
%end; %end;
)); ));
%do i=1 %to &nobs; %do i=1 %to &numcols;
length &&name&i $&&len&i; length &&name&i $&&len&i;
&&name&i=left(put(&&newname&i,&&fmt&i)); &&name&i=left(put(&&newname&i,&&fmt&i));
drop &&newname&i; drop &&newname&i;
%end; %end;
if _error_ then call symputx('syscc',1012); if _error_ then call symputx('syscc',1012);
run; run;
%let ds=&fmtds; %let fmtds=&syslast;
%end; /* &fmt=Y */ %end;
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;
proc format; /* credit yabwon for special null removal */ proc format; /* credit yabwon for special null removal */
value bart value bart (default=40)
%if &missing=NULL %then %do; %if &missing=NULL %then %do;
._ - .z = null ._ - .z = null
%end; %end;
@@ -181,20 +178,23 @@
%end; %end;
other = [best.]; other = [best.];
data;run; %let tempds=&syslast; /* temp table for spesh char management */
proc sql; drop table &tempds;
data &tempds/view=&tempds; data &tempds/view=&tempds;
attrib _all_ label=''; attrib _all_ label='';
%do i=1 %to &cols; %do i=1 %to &numcols;
%if &&type&i=char %then %do; %if &&typelong&i=char or &fmt=Y %then %do;
length &&name&i $32767; length &&name&i $32767;
format &&name&i $32767.; format &&name&i $32767.;
%end; %end;
%end; %end;
set &ds; %if &fmt=Y %then %do;
set &fmtds;
%end;
%else %do;
set &ds;
%end;
format _numeric_ bart.; format _numeric_ bart.;
%do i=1 %to &cols; %do i=1 %to &numcols;
%if &&type&i=char %then %do; %if &&typelong&i=char or &fmt=Y %then %do;
&&name&i='"'!!trim(prxchange('s/"/\"/',-1, &&name&i='"'!!trim(prxchange('s/"/\"/',-1,
prxchange('s/'!!'0A'x!!'/\n/',-1, prxchange('s/'!!'0A'x!!'/\n/',-1,
prxchange('s/'!!'0D'x!!'/\r/',-1, prxchange('s/'!!'0D'x!!'/\r/',-1,
@@ -204,44 +204,65 @@
%end; %end;
%end; %end;
run; run;
/* write to temp loc to avoid _webout truncation /* write to temp loc to avoid _webout truncation
- https://support.sas.com/kb/49/325.html */ - https://support.sas.com/kb/49/325.html */
filename _sjs temp lrecl=131068 encoding='utf-8'; filename _sjs temp lrecl=131068 encoding='utf-8';
data _null_; file _sjs lrecl=131068 encoding='utf-8' mod ; data _null_; file _sjs lrecl=131068 encoding='utf-8' mod ;
if _n_=1 then put "[";
set &tempds; set &tempds;
if _n_>1 then put "," @; put if _n_>1 then put "," @; put
%if &action=ARR %then "[" ; %else "{" ; %if &action=ARR %then "[" ; %else "{" ;
%do i=1 %to &cols; %do i=1 %to &numcols;
%if &i>1 %then "," ; %if &i>1 %then "," ;
%if &action=OBJ %then """&&name&i"":" ; %if &action=OBJ %then """&&name&i"":" ;
&&name&i &&name&i
%end; %end;
%if &action=ARR %then "]" ; %else "}" ; ; %if &action=ARR %then "]" ; %else "}" ; ;
proc sql;
drop view &tempds;
/* now write the long strings to _webout 1 byte at a time */ /* now write the long strings to _webout 1 byte at a time */
data _null_; data _null_;
length filein 8 fileid 8; length filein 8 fileid 8;
filein = fopen("_sjs",'I',1,'B'); filein=fopen("_sjs",'I',1,'B');
fileid = fopen("&jref",'A',1,'B'); fileid=fopen("&jref",'A',1,'B');
rec = '20'x; rec='20'x;
do while(fread(filein)=0); do while(fread(filein)=0);
rc = fget(filein,rec,1); rc=fget(filein,rec,1);
rc = fput(fileid, rec); rc=fput(fileid, rec);
rc =fwrite(fileid); rc=fwrite(fileid);
end; end;
rc = fclose(filein); /* close out the table */
rc = fclose(fileid); rc=fput(fileid, "]");
rc=fwrite(fileid);
rc=fclose(filein);
rc=fclose(fileid);
run; run;
filename _sjs clear; filename _sjs clear;
data _null_; file &jref mod encoding='utf-8' ; %end;
put "]";
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; run;
%end; %end;
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
data _null_;file &jref encoding='utf-8' mod ; data _null_; file &jref encoding='utf-8' mod ;
put "}"; put "}";
run; run;
%end; %end;

View File

@@ -15,7 +15,7 @@
length is 200 characters. length is 200 characters.
@param [out] ctl_ds= (0) The control table which controls the actual locking. @param [out] ctl_ds= (0) The control table which controls the actual locking.
Should already be assigned and available. The definition is available by 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] loops= (25) Number of times to check for a lock.
@param [in] loop_secs= (1) Seconds to wait between each lock attempt @param [in] loop_secs= (1) Seconds to wait between each lock attempt

View File

@@ -39,14 +39,6 @@
,Server=SASApp ,Server=SASApp
,stptype=2) ,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 @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 the check to avoid creating multiple STPs in the same folder with the same
name does not work when the name contains spaces. name does not work when the name contains spaces.
@@ -77,6 +69,17 @@
- fileuri - fileuri
- texturi - 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 @version 9.2
@author Allan Bowe @author Allan Bowe

View File

@@ -12,6 +12,7 @@ Usage:
%* parmcards lets us write to a text file from open code ; %* parmcards lets us write to a text file from open code ;
filename ft15f001 temp; filename ft15f001 temp;
parmcards4; parmcards4;
%webout(FETCH)
%* do some sas, any inputs are now already WORK tables; %* do some sas, any inputs are now already WORK tables;
data example1 example2; data example1 example2;
set sashelp.class; set sashelp.class;
@@ -24,11 +25,8 @@ Usage:
;;;; ;;;;
%mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001) %mm_createwebservice(path=/Public/app/common,name=appInit,code=ft15f001)
<h4> SAS Macros </h4> For more examples of using these web services with the SASjs Adapter, see:
@li mm_createstp.sas https://github.com/sasjs/adapter#readme
@li mf_getuser.sas
@li mm_createfolder.sas
@li mm_deletestp.sas
@param path= The full path (in SAS Metadata) where the service will be created @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 @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 desc= The description of the service (optional)
@param precode= Space separated list of filerefs, pointing to the code that @param precode= Space separated list of filerefs, pointing to the code that
needs to be attached to the beginning of the service (optional) 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 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. is fine.
@param mDebug=(0) set to 1 to show debug messages in the log @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 replace= (YES) select NO to avoid replacing an existing service in that
location 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. 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 @version 9.2
@author Allan Bowe @author Allan Bowe
@@ -89,11 +93,15 @@ data _null_;
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
/* WEBOUT BEGIN */ /* WEBOUT BEGIN */
put ' '; put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP ';
put ' ,dbg=0 /* DEPRECATED */ '; put ' ,dbg=0 /* DEPRECATED */ ';
put ' ,missing=NULL '; put ' ,missing=NULL ';
put ' ,showmeta=NO ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%put &sysmacroname: output location=&jref; '; put '%local tempds colinfo fmtds i numcols; ';
put '%let numcols=0; ';
put ' ';
put '%if &action=OPEN %then %do; '; put '%if &action=OPEN %then %do; ';
put ' options nobomfile; '; put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8'' ; '; put ' data _null_;file &jref encoding=''utf-8'' ; ';
@@ -102,17 +110,61 @@ data _null_;
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' options validvarname=upcase; '; 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 ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' '; 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 ' %if &engine=PROCJSON %then %do; ';
put ' %if &missing=STRING %then %do; '; put ' %if &missing=STRING %then %do; ';
put ' %put &sysmacroname: Special Missings are not supported in proc json.; '; put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
put ' %put &sysmacroname: Switching to DATASTEP engine; '; put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; '; put ' %goto datastep; ';
put ' %end; '; put ' %end; ';
put ' data;run;%let tempds=&syslast; ';
put ' proc sql;drop table &tempds; ';
put ' data &tempds /view=&tempds;set &ds; '; put ' data &tempds /view=&tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
@@ -120,91 +172,35 @@ data _null_;
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' proc sql;drop view &tempds; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
put ' %local cols i tempds; '; put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
put ' %let cols=0; '; put ' %then %do; ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
put ' %put &sysmacroname: &ds NOT FOUND!!!; '; put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
put ' %return; '; put ' %return; ';
put ' %end; '; 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 ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' %if &fmt=Y %then %do; ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' data _data_; ';
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 ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %local i; '; put ' %do i=1 %to &numcols; ';
put ' %do i=1 %to &nobs; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' %do i=1 %to &nobs; '; put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; '; put ' length &&name&i $&&len&i; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
put ' drop &&newname&i; '; put ' drop &&newname&i; ';
put ' %end; '; put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); '; put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; '; put ' run; ';
put ' %let ds=&fmtds; '; put ' %let fmtds=&syslast; ';
put ' %end; /* &fmt=Y */ '; put ' %end; ';
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 ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
put ' value bart '; put ' value bart (default=40) ';
put ' %if &missing=NULL %then %do; '; put ' %if &missing=NULL %then %do; ';
put ' ._ - .z = null '; put ' ._ - .z = null ';
put ' %end; '; put ' %end; ';
@@ -215,20 +211,23 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; 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 ' data &tempds/view=&tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &cols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&type&i=char %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; '; put ' length &&name&i $32767; ';
put ' format &&name&i $32767.; '; put ' format &&name&i $32767.; ';
put ' %end; '; put ' %end; ';
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 ' format _numeric_ bart.; ';
put ' %do i=1 %to &cols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&type&i=char %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, '; put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, '; put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, '; put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
@@ -238,49 +237,72 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' /* write to temp loc to avoid _webout truncation ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' - https://support.sas.com/kb/49/325.html */ ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; '; put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
put ' %if &action=ARR %then "[" ; %else "{" ; '; 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 &i>1 %then "," ; ';
put ' %if &action=OBJ %then """&&name&i"":" ; '; put ' %if &action=OBJ %then """&&name&i"":" ; ';
put ' &&name&i '; put ' &&name&i ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; 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 ' /* now write the long strings to _webout 1 byte at a time */ ';
put ' data _null_; '; put ' data _null_; ';
put ' length filein 8 fileid 8; '; put ' length filein 8 fileid 8; ';
put ' filein = fopen("_sjs",''I'',1,''B''); '; put ' filein=fopen("_sjs",''I'',1,''B''); ';
put ' fileid = fopen("&jref",''A'',1,''B''); '; put ' fileid=fopen("&jref",''A'',1,''B''); ';
put ' rec = ''20''x; '; put ' rec=''20''x; ';
put ' do while(fread(filein)=0); '; put ' do while(fread(filein)=0); ';
put ' rc = fget(filein,rec,1); '; put ' rc=fget(filein,rec,1); ';
put ' rc = fput(fileid, rec); '; put ' rc=fput(fileid, rec); ';
put ' rc =fwrite(fileid); '; put ' rc=fwrite(fileid); ';
put ' end; '; put ' end; ';
put ' rc = fclose(filein); '; put ' /* close out the table */ ';
put ' rc = fclose(fileid); '; put ' rc=fput(fileid, "]"); ';
put ' rc=fwrite(fileid); ';
put ' rc=fclose(filein); ';
put ' rc=fclose(fileid); ';
put ' run; '; put ' run; ';
put ' filename _sjs clear; '; put ' filename _sjs clear; ';
put ' data _null_; file &jref mod encoding=''utf-8'' ; '; put ' %end; ';
put ' put "]"; '; 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 ' run; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';
put '%else %if &action=CLOSE %then %do; '; 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 ' put "}"; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%mend mp_jsonout; '; put '%mend mp_jsonout; ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL); '; 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 '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; '; put ' sasjs_tables; ';
put '%local i tempds jsonengine; '; put '%local i tempds jsonengine; ';
@@ -338,14 +360,15 @@ data _null_;
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&_debug) ge 131 %then %do; ';
put ' put ''>>weboutBEGIN<<''; '; put ' put ''>>weboutBEGIN<<''; ';
put ' %end; '; put ' %end; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; '; put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put '%end; '; put '%end; ';
put ' '; put ' ';
put '%else %if &action=ARR or &action=OBJ %then %do; '; put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref '; put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
put ' ,engine=&jsonengine,missing=&missing '; put ' ,engine=&jsonengine,missing=&missing,showmeta=&showmeta ';
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
@@ -366,9 +389,6 @@ data _null_;
put ' put ",""WORK"":{"; '; put ' put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; '; put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; '; 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 ' data _null_; file &fref mod encoding=''utf-8''; ';
put ' dsid=open("WORK.&wt",''is''); '; put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); '; put ' nlobs=attrn(dsid,''NLOBS''); ';
@@ -378,8 +398,7 @@ 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,&tempds,jref=&fref,dslabel=colattrs,engine=&jsonengine) '; put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=&jsonengine) ';
put ' data _null_; file &fref mod encoding=''utf-8''; '; put ' data _null_; file &fref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';
@@ -408,6 +427,9 @@ data _null_;
put ' put '',"SYSVLONG" : '' sysvlong; '; put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; '; put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; '; 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 "}" @; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&_debug) ge 131 %then %do; ';
put ' put ''>>weboutEND<<''; '; put ' put ''>>weboutEND<<''; ';
@@ -435,8 +457,10 @@ data _null_;
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
/* WEBOUT END */ /* WEBOUT END */
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL);'; put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing)'; put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta';
put ' )';
put '%mend;'; put '%mend;';
run; run;

View File

@@ -30,12 +30,18 @@
@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"`)
@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 @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=Y,missing=NULL
,showmeta=NO
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables; sasjs_tables;
%local i tempds jsonengine; %local i tempds jsonengine;
@@ -93,14 +99,15 @@
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 131 %then %do;
put '>>weboutBEGIN<<'; put '>>weboutBEGIN<<';
%end; %end;
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"'; put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"';
run; run;
%end; %end;
%else %if &action=ARR or &action=OBJ %then %do; %else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,engine=&jsonengine,missing=&missing ,engine=&jsonengine,missing=&missing,showmeta=&showmeta
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
@@ -121,9 +128,6 @@
put ",""WORK"":{"; put ",""WORK"":{";
%do i=1 %to &wtcnt; %do i=1 %to &wtcnt;
%let wt=&&wt&i; %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'; data _null_; file &fref mod encoding='utf-8';
dsid=open("WORK.&wt",'is'); dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS'); nlobs=attrn(dsid,'NLOBS');
@@ -133,8 +137,7 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=&jsonengine) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=&jsonengine)
data _null_; file &fref mod encoding='utf-8'; data _null_; file &fref mod encoding='utf-8';
put "}"; put "}";
%end; %end;
@@ -163,6 +166,9 @@
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize;
put "}" @; put "}" @;
%if %str(&_debug) ge 131 %then %do; %if %str(&_debug) ge 131 %then %do;
put '>>weboutEND<<'; put '>>weboutEND<<';

90
package-lock.json generated
View File

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

View File

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

View File

@@ -27,6 +27,10 @@
@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"`)
@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> <h4> SAS Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@@ -41,7 +45,9 @@
**/ **/
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL); %macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
,showmeta=NO
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables; sasjs_tables;
@@ -93,7 +99,7 @@
%else %if &action=ARR or &action=OBJ %then %do; %else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,engine=DATASTEP,missing=&missing ,engine=DATASTEP,missing=&missing,showmeta=&showmeta
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
@@ -114,9 +120,6 @@
put ",""WORK"":{"; put ",""WORK"":{";
%do i=1 %to &wtcnt; %do i=1 %to &wtcnt;
%let wt=&&wt&i; %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; data _null_; file &fref mod encoding='utf-8' termstr=lf;
dsid=open("WORK.&wt",'is'); dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS'); nlobs=attrn(dsid,'NLOBS');
@@ -126,8 +129,7 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
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

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

View File

@@ -237,11 +237,15 @@ data _null_;
put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */"; put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */";
/* WEBOUT BEGIN */ /* WEBOUT BEGIN */
put ' '; put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP ';
put ' ,dbg=0 /* DEPRECATED */ '; put ' ,dbg=0 /* DEPRECATED */ ';
put ' ,missing=NULL '; put ' ,missing=NULL ';
put ' ,showmeta=NO ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%put &sysmacroname: output location=&jref; '; put '%local tempds colinfo fmtds i numcols; ';
put '%let numcols=0; ';
put ' ';
put '%if &action=OPEN %then %do; '; put '%if &action=OPEN %then %do; ';
put ' options nobomfile; '; put ' options nobomfile; ';
put ' data _null_;file &jref encoding=''utf-8'' ; '; put ' data _null_;file &jref encoding=''utf-8'' ; ';
@@ -250,17 +254,61 @@ data _null_;
put '%end; '; put '%end; ';
put '%else %if (&action=ARR or &action=OBJ) %then %do; '; put '%else %if (&action=ARR or &action=OBJ) %then %do; ';
put ' options validvarname=upcase; '; 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 ", ""%lowcase(%sysfunc(coalescec(&dslabel,&ds)))"":"; ';
put ' '; 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 ' %if &engine=PROCJSON %then %do; ';
put ' %if &missing=STRING %then %do; '; put ' %if &missing=STRING %then %do; ';
put ' %put &sysmacroname: Special Missings are not supported in proc json.; '; put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
put ' %put &sysmacroname: Switching to DATASTEP engine; '; put ' %put &sysmacroname: Switching to DATASTEP engine; ';
put ' %goto datastep; '; put ' %goto datastep; ';
put ' %end; '; put ' %end; ';
put ' data;run;%let tempds=&syslast; ';
put ' proc sql;drop table &tempds; ';
put ' data &tempds /view=&tempds;set &ds; '; put ' data &tempds /view=&tempds;set &ds; ';
put ' %if &fmt=N %then format _numeric_ best32.;; '; put ' %if &fmt=N %then format _numeric_ best32.;; ';
put ' /* PRETTY is necessary to avoid line truncation in large files */ '; put ' /* PRETTY is necessary to avoid line truncation in large files */ ';
@@ -268,91 +316,35 @@ data _null_;
put ' %if &action=ARR %then nokeys ; '; put ' %if &action=ARR %then nokeys ; ';
put ' ;export &tempds / nosastags fmtnumeric; '; put ' ;export &tempds / nosastags fmtnumeric; ';
put ' run; '; put ' run; ';
put ' proc sql;drop view &tempds; ';
put ' %end; '; put ' %end; ';
put ' %else %if &engine=DATASTEP %then %do; '; put ' %else %if &engine=DATASTEP %then %do; ';
put ' %datastep: '; put ' %datastep: ';
put ' %local cols i tempds; '; put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
put ' %let cols=0; '; put ' %then %do; ';
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
put ' %put &sysmacroname: &ds NOT FOUND!!!; '; put ' %put &sysmacroname: &ds NOT FOUND!!!; ';
put ' %return; '; put ' %return; ';
put ' %end; '; 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 ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' %if &fmt=Y %then %do; ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' data _data_; ';
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 ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %local i; '; put ' %do i=1 %to &numcols; ';
put ' %do i=1 %to &nobs; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' %do i=1 %to &nobs; '; put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; '; put ' length &&name&i $&&len&i; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; put ' &&name&i=left(put(&&newname&i,&&fmt&i)); ';
put ' drop &&newname&i; '; put ' drop &&newname&i; ';
put ' %end; '; put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); '; put ' if _error_ then call symputx(''syscc'',1012); ';
put ' run; '; put ' run; ';
put ' %let ds=&fmtds; '; put ' %let fmtds=&syslast; ';
put ' %end; /* &fmt=Y */ '; put ' %end; ';
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 ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
put ' value bart '; put ' value bart (default=40) ';
put ' %if &missing=NULL %then %do; '; put ' %if &missing=NULL %then %do; ';
put ' ._ - .z = null '; put ' ._ - .z = null ';
put ' %end; '; put ' %end; ';
@@ -363,20 +355,23 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' other = [best.]; '; put ' other = [best.]; ';
put ' '; 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 ' data &tempds/view=&tempds; ';
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &cols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&type&i=char %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; '; put ' length &&name&i $32767; ';
put ' format &&name&i $32767.; '; put ' format &&name&i $32767.; ';
put ' %end; '; put ' %end; ';
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 ' format _numeric_ bart.; ';
put ' %do i=1 %to &cols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&type&i=char %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, '; put ' &&name&i=''"''!!trim(prxchange(''s/"/\"/'',-1, ';
put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, '; put ' prxchange(''s/''!!''0A''x!!''/\n/'',-1, ';
put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, '; put ' prxchange(''s/''!!''0D''x!!''/\r/'',-1, ';
@@ -386,49 +381,72 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' run; '; put ' run; ';
put ' ';
put ' /* write to temp loc to avoid _webout truncation '; put ' /* write to temp loc to avoid _webout truncation ';
put ' - https://support.sas.com/kb/49/325.html */ '; put ' - https://support.sas.com/kb/49/325.html */ ';
put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; '; put ' filename _sjs temp lrecl=131068 encoding=''utf-8''; ';
put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; '; put ' data _null_; file _sjs lrecl=131068 encoding=''utf-8'' mod ; ';
put ' if _n_=1 then put "["; ';
put ' set &tempds; '; put ' set &tempds; ';
put ' if _n_>1 then put "," @; put '; put ' if _n_>1 then put "," @; put ';
put ' %if &action=ARR %then "[" ; %else "{" ; '; 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 &i>1 %then "," ; ';
put ' %if &action=OBJ %then """&&name&i"":" ; '; put ' %if &action=OBJ %then """&&name&i"":" ; ';
put ' &&name&i '; put ' &&name&i ';
put ' %end; '; put ' %end; ';
put ' %if &action=ARR %then "]" ; %else "}" ; ; '; 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 ' /* now write the long strings to _webout 1 byte at a time */ ';
put ' data _null_; '; put ' data _null_; ';
put ' length filein 8 fileid 8; '; put ' length filein 8 fileid 8; ';
put ' filein = fopen("_sjs",''I'',1,''B''); '; put ' filein=fopen("_sjs",''I'',1,''B''); ';
put ' fileid = fopen("&jref",''A'',1,''B''); '; put ' fileid=fopen("&jref",''A'',1,''B''); ';
put ' rec = ''20''x; '; put ' rec=''20''x; ';
put ' do while(fread(filein)=0); '; put ' do while(fread(filein)=0); ';
put ' rc = fget(filein,rec,1); '; put ' rc=fget(filein,rec,1); ';
put ' rc = fput(fileid, rec); '; put ' rc=fput(fileid, rec); ';
put ' rc =fwrite(fileid); '; put ' rc=fwrite(fileid); ';
put ' end; '; put ' end; ';
put ' rc = fclose(filein); '; put ' /* close out the table */ ';
put ' rc = fclose(fileid); '; put ' rc=fput(fileid, "]"); ';
put ' rc=fwrite(fileid); ';
put ' rc=fclose(filein); ';
put ' rc=fclose(fileid); ';
put ' run; '; put ' run; ';
put ' filename _sjs clear; '; put ' filename _sjs clear; ';
put ' data _null_; file &jref mod encoding=''utf-8'' ; '; put ' %end; ';
put ' put "]"; '; 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 ' run; ';
put ' %end; '; put ' %end; ';
put '%end; '; put '%end; ';
put ' '; put ' ';
put '%else %if &action=CLOSE %then %do; '; 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 ' put "}"; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%mend mp_jsonout; '; put '%mend mp_jsonout; ';
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=Y,stream=Y,missing=NULL ';
put ' ,showmeta=NO ';
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; ';
put '%if %index("&_debug",log) %then %let _debug=131; '; put '%if %index("&_debug",log) %then %let _debug=131; ';
@@ -550,12 +568,13 @@ data _null_;
put ' '; put ' ';
put ' /* setup json */ '; put ' /* setup json */ ';
put ' data _null_;file &fref; '; put ' data _null_;file &fref; ';
put ' put ''{"START_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''"''; '; put ' put ''{"SYSDATE" : "'' "&SYSDATE" ''"''; ';
put ' put '',"SYSTIME" : "'' "&SYSTIME" ''"''; ';
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if &action=ARR or &action=OBJ %then %do; '; put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt '; put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt ';
put ' ,jref=&fref,engine=DATASTEP,missing=&missing '; put ' ,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta ';
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
@@ -575,9 +594,6 @@ data _null_;
put ' data _null_; file &fref mod; put ",""WORK"":{"; '; put ' data _null_; file &fref mod; put ",""WORK"":{"; ';
put ' %do i=1 %to &wtcnt; '; put ' %do i=1 %to &wtcnt; ';
put ' %let wt=&&wt&i; '; 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 ' data _null_; file &fref mod; ';
put ' dsid=open("WORK.&wt",''is''); '; put ' dsid=open("WORK.&wt",''is''); ';
put ' nlobs=attrn(dsid,''NLOBS''); '; put ' nlobs=attrn(dsid,''NLOBS''); ';
@@ -587,8 +603,7 @@ 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,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) '; put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP) ';
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; ';
@@ -613,6 +628,9 @@ data _null_;
put ' put '',"SYSVLONG" : '' sysvlong; '; put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; '; put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; '; 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 "}"; ';
put ' '; put ' ';
put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; '; put ' %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; ';
@@ -646,8 +664,10 @@ data _null_;
put '%global __program _program;'; put '%global __program _program;';
put '%let _program=%sysfunc(coalescec(&__program,&_program));'; put '%let _program=%sysfunc(coalescec(&__program,&_program));';
put ' '; put ' ';
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL);'; put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing)'; put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
put ' ,showmeta=&showmeta';
put ' )';
put '%mend;'; put '%mend;';
run; run;

View File

@@ -29,6 +29,10 @@
@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"`)
@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> <h4> SAS Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@@ -38,7 +42,9 @@
@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=Y,stream=Y,missing=NULL
,showmeta=NO
);
%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;
%if %index("&_debug",log) %then %let _debug=131; %if %index("&_debug",log) %then %let _debug=131;
@@ -160,12 +166,13 @@
/* setup json */ /* setup json */
data _null_;file &fref; data _null_;file &fref;
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"'; put '{"SYSDATE" : "' "&SYSDATE" '"';
put ',"SYSTIME" : "' "&SYSTIME" '"';
run; run;
%end; %end;
%else %if &action=ARR or &action=OBJ %then %do; %else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt
,jref=&fref,engine=DATASTEP,missing=&missing ,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
@@ -185,9 +192,6 @@
data _null_; file &fref mod; put ",""WORK"":{"; data _null_; file &fref mod; put ",""WORK"":{";
%do i=1 %to &wtcnt; %do i=1 %to &wtcnt;
%let wt=&&wt&i; %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; data _null_; file &fref mod;
dsid=open("WORK.&wt",'is'); dsid=open("WORK.&wt",'is');
nlobs=attrn(dsid,'NLOBS'); nlobs=attrn(dsid,'NLOBS');
@@ -197,8 +201,7 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; put ',"nvars":' nvars;
%mp_jsonout(OBJ,&tempds,jref=&fref,dslabel=colattrs,engine=DATASTEP) %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=YES)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,engine=DATASTEP)
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;
@@ -223,6 +226,9 @@
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
memsize=quote(cats(memsize));
put ',"MEMSIZE" : ' memsize;
put "}"; put "}";
%if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do; %if %upcase(&fref) ne _WEBOUT and &stream=Y %then %do;