mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
chore(merge): merging with v4
Merge branch 'main' into issue144
This commit is contained in:
10
README.md
10
README.md
@@ -125,6 +125,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
- macro names must be lowercase
|
||||
- one macro per file
|
||||
- prefixes:
|
||||
- _mcf_ for macro compiled functions (proc fcmp)
|
||||
- _mf_ for macro functions (can be used in open code).
|
||||
- _ml_ for macros that are used to compile LUA modules
|
||||
- _mm_ for metadata macros (interface with the metadata server).
|
||||
@@ -166,7 +167,7 @@ SAS code can contain one of two types of dependency - SAS Macros, and SAS Includ
|
||||
@li someprogram.sas FREFTWO
|
||||
```
|
||||
|
||||
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Includes) when creating SAS Jobs and Services.
|
||||
The CLI can then extract all the dependencies and insert as precode (SAS Macros) or in a temp engine fileref (SAS Includes) when creating SAS Jobs and Services (and Tests).
|
||||
|
||||
When contributing to this library, it is therefore important to ensure that all dependencies are listed in the header in this format.
|
||||
|
||||
@@ -182,7 +183,9 @@ When contributing to this library, it is therefore important to ensure that all
|
||||
- Mandatory parameters should be positional, all optional parameters should be keyword (var=) style.
|
||||
- All dataset references must be 2 level (eg `work.blah`, not `blah`). This is to avoid contention when options [DATASTMTCHK](https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a000279064.htm)=ALLKEYWORDS is in effect.
|
||||
- Avoid naming collisions! All macro variables should be local scope. Use system generated work tables where possible - eg `data ; set sashelp.class; run; data &output; set &syslast; run;`
|
||||
- Where global macro variables are absolutely necessary, they should make use of `&sasjs_prefix` - see mp_init.sas
|
||||
- The use of `quit;` for `proc sql` is optional unless you are looking to benefit from the timing statistics.
|
||||
- Use [sasjs lint](https://github.com/sasjs/lint)!
|
||||
|
||||
## General Notes
|
||||
|
||||
@@ -190,10 +193,9 @@ When contributing to this library, it is therefore important to ensure that all
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
We are currently on major release v3. The following changes are planned when the next major (breaking) release becomes necessary:
|
||||
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. 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
|
||||
* (None as yet)
|
||||
|
||||
## Star Gazing
|
||||
|
||||
|
||||
387
all.sas
387
all.sas
@@ -29,7 +29,7 @@ options noquotelenmax;
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_abort(mac=mf_abort.sas, type=deprecated, msg=, iftrue=%str(1=1)
|
||||
%macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
@@ -2198,7 +2198,8 @@ Usage:
|
||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||
else debug='""';
|
||||
put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
put ' ,"MAC": "' "&mac" '"}]';
|
||||
@@ -2227,7 +2228,7 @@ Usage:
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
syswarningtext=quote(trim(symget('syswarningtext')));
|
||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
put "}" @;
|
||||
put '>>weboutEND<<';
|
||||
run;
|
||||
@@ -3035,7 +3036,7 @@ run;
|
||||
@li COMPARE - compare the current macro variables against previous values
|
||||
@param [in] scope= (GLOBAL) The scope of the variables to be checked. This
|
||||
corresponds to the values in the SCOPE column in `sashelp.vmacro`.
|
||||
@param [in] desc= (Testing variable scope) The user provided test description
|
||||
@param [in] desc= (Testing scope leakage) The user provided test description
|
||||
@param [in,out] scopeds= (work.mp_assertscope) The dataset to contain the
|
||||
scope snapshot
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
@@ -3057,7 +3058,7 @@ run;
|
||||
**/
|
||||
|
||||
%macro mp_assertscope(action,
|
||||
desc=0,
|
||||
desc=Testing Scope Leakage,
|
||||
scope=GLOBAL,
|
||||
scopeds=work.mp_assertscope,
|
||||
outds=work.test_results
|
||||
@@ -5027,6 +5028,124 @@ run;
|
||||
%end;
|
||||
|
||||
%mend mp_ds2md;/**
|
||||
@file
|
||||
@brief Create a smaller version of a dataset, without data loss
|
||||
@details This macro will scan the input dataset and create a new one, that
|
||||
has the minimum variable lengths needed to store the data without data loss.
|
||||
|
||||
Inspiration was taken from [How to Reduce the Disk Space Required by a
|
||||
SAS® Data Set](https://www.lexjansen.com/nesug/nesug06/io/io18.pdf) by
|
||||
Selvaratnam Sridharma. The end of the referenced paper presents a macro named
|
||||
"squeeze", hence the nomenclature.
|
||||
|
||||
Usage:
|
||||
|
||||
data big;
|
||||
length my big $32000;
|
||||
do i=1 to 1e4;
|
||||
my=repeat('oh my',100);
|
||||
big='dawg';
|
||||
special=._;
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mp_ds2squeeze(work.big,outds=work.smaller)
|
||||
|
||||
The following will also be printed to the log (exact values may differ
|
||||
depending on your OS and COMPRESS settings):
|
||||
|
||||
> MP_DS2SQUEEZE: work.big was 625MB
|
||||
|
||||
> MP_DS2SQUEEZE: work.smaller is 5MB
|
||||
|
||||
@param [in] libds The library.dataset to be squeezed
|
||||
@param [out] outds= (work.mp_ds2squeeze) The squeezed dataset to create
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getfilesize.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_getmaxvarlengths.sas
|
||||
|
||||
<h4> Related Programs </h4>
|
||||
@li mp_ds2squeeze.test.sas
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_ds2squeeze(
|
||||
libds,
|
||||
outds=work.work.mp_ds2squeeze,
|
||||
mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dbg source;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %do;
|
||||
%let dbg=*;
|
||||
%let source=/source2;
|
||||
%end;
|
||||
|
||||
%local optval ds fref;
|
||||
%let ds=%mf_getuniquename();
|
||||
%let fref=%mf_getuniquefileref();
|
||||
|
||||
%mp_getmaxvarlengths(&libds,outds=&ds)
|
||||
|
||||
data _null_;
|
||||
set &ds end=last;
|
||||
file &fref;
|
||||
/* grab the types */
|
||||
retain dsid;
|
||||
if _n_=1 then dsid=open("&libds",'is');
|
||||
if dsid le 0 then do;
|
||||
msg=sysmsg();
|
||||
put msg=;
|
||||
stop;
|
||||
end;
|
||||
type=vartype(dsid,varnum(dsid, name));
|
||||
if last then rc=close(dsid);
|
||||
/* write out the length statement */
|
||||
if _n_=1 then put 'length ';
|
||||
length len $6;
|
||||
if type='C' then do;
|
||||
if maxlen=0 then len='$1';
|
||||
else len=cats('$',maxlen);
|
||||
end;
|
||||
else do;
|
||||
if maxlen=0 then len='3';
|
||||
else len=cats(maxlen);
|
||||
end;
|
||||
put ' ' name ' ' len;
|
||||
if last then put ';';
|
||||
run;
|
||||
|
||||
/* configure varlenchk - as we are explicitly shortening the variables */
|
||||
%let optval=%sysfunc(getoption(varlenchk));
|
||||
options varlenchk=NOWARN;
|
||||
|
||||
data &outds;
|
||||
%inc &fref &source;
|
||||
set &libds;
|
||||
run;
|
||||
|
||||
options varlenchk=&optval;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
filename &fref clear;
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: &libds was %mf_getfilesize(libds=&libds,format=yes);
|
||||
%put &sysmacroname: &outds is %mf_getfilesize(libds=&outds,format=yes);
|
||||
|
||||
%mend mp_ds2squeeze;/**
|
||||
@file
|
||||
@brief Checks an input filter table for validity
|
||||
@details Performs checks on the input table to ensure it arrives in the
|
||||
@@ -6702,30 +6821,48 @@ create table &outsummary as
|
||||
%end;
|
||||
|
||||
%mend mp_getformats;/**
|
||||
@file mp_getmaxvarlengths.sas
|
||||
@file
|
||||
@brief Scans a dataset to find the max length of the variable values
|
||||
@details
|
||||
This macro will scan a base dataset and produce an output dataset with two
|
||||
columns:
|
||||
|
||||
- NAME Name of the base dataset column
|
||||
- MAXLEN Maximum length of the data contained therein.
|
||||
- MAXLEN Maximum length of the data contained therein.
|
||||
|
||||
Character fields may be allocated very large widths (eg 32000) of which the
|
||||
maximum value is likely to be much narrower. This macro was designed to
|
||||
enable a HTML table to be appropriately sized however this could be used as
|
||||
part of a data audit to ensure we aren't over-sizing our tables in relation to
|
||||
the data therein.
|
||||
Character fields are often allocated very large widths (eg 32000) of which the
|
||||
maximum value is likely to be much narrower. Identifying such cases can be
|
||||
helpful in the following scenarios:
|
||||
|
||||
@li Enabling a HTML table to be appropriately sized (`num2char=YES`)
|
||||
@li Reducing the size of a dataset to save on storage (mp_ds2squeeze.sas)
|
||||
@li Identifying columns containing nothing but missing values (`MAXLEN=0` in
|
||||
the output table)
|
||||
|
||||
If the entire column is made up of (non-special) missing values then a value
|
||||
of 0 is returned.
|
||||
|
||||
Numeric fields are converted using the relevant format to determine the width.
|
||||
Usage:
|
||||
|
||||
%mp_getmaxvarlengths(sashelp.class,outds=work.myds)
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param outds= The output dataset to create
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] num2char= (NO) When set to NO, numeric fields are sized according
|
||||
to the number of bytes used (or set to zero in the case of non-special
|
||||
missings). When YES, the numeric field is converted to character (using the
|
||||
format, if available), and that is sized instead, using `lengthn()`.
|
||||
@param [out] outds= The output dataset to create, eg:
|
||||
|NAME:$8.|MAXLEN:best.|
|
||||
|---|---|
|
||||
|`Name `|`7 `|
|
||||
|`Sex `|`1 `|
|
||||
|`Age `|`3 `|
|
||||
|`Height `|`8 `|
|
||||
|`Weight `|`3 `|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mcf_length.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
@li mf_getvarformat.sas
|
||||
@@ -6733,20 +6870,32 @@ create table &outsummary as
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_ds2squeeze.sas
|
||||
@li mp_getmaxvarlengths.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_getmaxvarlengths(
|
||||
libds /* libref.dataset to analyse */
|
||||
,outds=work.mp_getmaxvarlengths /* name of output dataset to create */
|
||||
libds
|
||||
,num2char=NO
|
||||
,outds=work.mp_getmaxvarlengths
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local vars x var fmt;
|
||||
%local vars prefix x var fmt;
|
||||
%let vars=%mf_getvarlist(libds=&libds);
|
||||
%let prefix=%substr(%mf_getuniquename(),1,25);
|
||||
%let num2char=%upcase(&num2char);
|
||||
|
||||
%if &num2char=NO %then %do;
|
||||
/* compile length function for numeric fields */
|
||||
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||
%end;
|
||||
|
||||
proc sql;
|
||||
create table &outds (rename=(
|
||||
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||
________&x=%scan(&vars,&x)
|
||||
&prefix.&x=%scan(&vars,&x)
|
||||
%end;
|
||||
))
|
||||
as select
|
||||
@@ -6754,18 +6903,21 @@ create table &outds (rename=(
|
||||
%let var=%scan(&vars,&x);
|
||||
%if &x>1 %then ,;
|
||||
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||
max(length(&var)) as ________&x
|
||||
max(lengthn(&var)) as &prefix.&x
|
||||
%end;
|
||||
%else %do;
|
||||
%else %if &num2char=YES %then %do;
|
||||
%let fmt=%mf_getvarformat(&libds,&var);
|
||||
%put fmt=&fmt;
|
||||
%if %str(&fmt)=%str() %then %do;
|
||||
max(length(cats(&var))) as ________&x
|
||||
max(lengthn(cats(&var))) as &prefix.&x
|
||||
%end;
|
||||
%else %do;
|
||||
max(length(put(&var,&fmt))) as ________&x
|
||||
max(lengthn(put(&var,&fmt))) as &prefix.&x
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
max(mcf_length(&var)) as &prefix.&x
|
||||
%end;
|
||||
%end;
|
||||
from &libds;
|
||||
|
||||
@@ -7615,38 +7767,42 @@ filename &tempref clear;
|
||||
%macro mp_init(prefix=SASJS
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global
|
||||
&prefix._INIT_NUM /* initialisation time as numeric */
|
||||
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
|
||||
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
|
||||
;
|
||||
%if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */
|
||||
%if %symexist(SASJS_PREFIX) %then %return; /* only run once */
|
||||
|
||||
data _null_;
|
||||
dttm=datetime();
|
||||
call symputx("&prefix._init_num",dttm,'g');
|
||||
call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6),'g');
|
||||
call symputx("&prefix.work",pathname('WORK'),'g');
|
||||
run;
|
||||
%global
|
||||
SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
|
||||
&prefix._INIT_NUM /* initialisation time as numeric */
|
||||
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
|
||||
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
|
||||
;
|
||||
|
||||
options
|
||||
noautocorrect /* disallow misspelled procedure names */
|
||||
compress=CHAR /* default is none so ensure we have something! */
|
||||
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
||||
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
||||
fmterr /* ensure err when a format cannot be found */
|
||||
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
|
||||
missing=. /* changing this can cause hard to detect errs */
|
||||
noquotelenmax /* avoid warnings for long strings */
|
||||
noreplace /* avoid overwriting permanent datasets */
|
||||
ps=max /* reduce log size slightly */
|
||||
ls=max /* reduce log even more and avoid word truncation */
|
||||
validmemname=COMPATIBLE /* avoid special characters etc in table names */
|
||||
validvarname=V7 /* avoid special characters etc in variable names */
|
||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||
;
|
||||
%let sasjs_prefix=&prefix;
|
||||
|
||||
data _null_;
|
||||
dttm=datetime();
|
||||
call symputx("&sasjs_prefix._init_num",dttm,'g');
|
||||
call symputx("&sasjs_prefix._init_dttm",put(dttm,E8601DT26.6),'g');
|
||||
call symputx("&sasjs_prefix.work",pathname('WORK'),'g');
|
||||
run;
|
||||
|
||||
options
|
||||
noautocorrect /* disallow misspelled procedure names */
|
||||
compress=CHAR /* default is none so ensure we have something! */
|
||||
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
||||
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
||||
fmterr /* ensure err when a format cannot be found */
|
||||
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
|
||||
missing=. /* changing this can cause hard to detect errs */
|
||||
noquotelenmax /* avoid warnings for long strings */
|
||||
noreplace /* avoid overwriting permanent datasets */
|
||||
ps=max /* reduce log size slightly */
|
||||
ls=max /* reduce log even more and avoid word truncation */
|
||||
validmemname=COMPATIBLE /* avoid special characters etc in table names */
|
||||
validvarname=V7 /* avoid special characters etc in variable names */
|
||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||
;
|
||||
|
||||
%mend mp_init;/**
|
||||
@file mp_jsonout.sas
|
||||
@@ -7712,7 +7868,6 @@ filename &tempref clear;
|
||||
|
||||
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
|
||||
,engine=DATASTEP
|
||||
,dbg=0 /* DEPRECATED */
|
||||
,missing=NULL
|
||||
,showmeta=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
@@ -12844,7 +12999,6 @@ data _null_;
|
||||
put ' ';
|
||||
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*/; ';
|
||||
@@ -13175,7 +13329,7 @@ data _null_;
|
||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||
put ' length memsize $32; ';
|
||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||
put ' memsize=quote(cats(memsize)); ';
|
||||
@@ -16390,9 +16544,6 @@ run;
|
||||
@param [in] stpcode= the source file (or fileref) containing the SAS code to load
|
||||
into the stp. For multiple files, they should simply be concatenated first.
|
||||
@param [in] minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
|
||||
@param frefin= deprecated - a unique fileref is now always used
|
||||
@param frefout= deprecated - a unique fileref is now always used
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.3
|
||||
@@ -16407,16 +16558,8 @@ run;
|
||||
,stpcode=
|
||||
,minify=NO
|
||||
,mdebug=0
|
||||
/* deprecated */
|
||||
,frefin=inmeta
|
||||
,frefout=outmeta
|
||||
);
|
||||
|
||||
%if &frefin ne inmeta or &frefout ne outmeta %then %do;
|
||||
%put %str(WARN)ING: the frefin and frefout parameters will be deprecated in
|
||||
an upcoming release.;
|
||||
%end;
|
||||
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
@@ -16681,7 +16824,7 @@ run;
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length memsize $32;
|
||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||
memsize=quote(cats(memsize));
|
||||
@@ -16988,7 +17131,7 @@ run;
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length autoexec $512;
|
||||
autoexec=quote(urlencode(trim(getoption('autoexec'))));
|
||||
put ',"AUTOEXEC" : ' autoexec;
|
||||
@@ -17980,7 +18123,6 @@ data _null_;
|
||||
put ' ';
|
||||
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*/; ';
|
||||
@@ -18368,7 +18510,7 @@ data _null_;
|
||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||
put ' length memsize $32; ';
|
||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||
put ' memsize=quote(cats(memsize)); ';
|
||||
@@ -18673,6 +18815,7 @@ libname &libref1a JSON fileref=&fname1a;
|
||||
%let found=0;
|
||||
%put Getting object uri from &libref1a..items;
|
||||
data _null_;
|
||||
length contenttype name $1000;
|
||||
set &libref1a..items;
|
||||
if contenttype="&contenttype" and upcase(name)="%upcase(&name)" then do;
|
||||
call symputx('uri',uri,'l');
|
||||
@@ -18820,6 +18963,7 @@ libname &libref1a JSON fileref=&fname1a;
|
||||
%let found=0;
|
||||
%put Getting object uri from &libref1a..items;
|
||||
data _null_;
|
||||
length contenttype name $1000;
|
||||
set &libref1a..items;
|
||||
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
|
||||
call symputx('uri',cats("&base_uri",uri),'l');
|
||||
@@ -18992,59 +19136,6 @@ filename &fname2 clear;
|
||||
libname &libref1 clear;
|
||||
|
||||
%mend mv_deleteviyafolder;/**
|
||||
@file mv_getaccesstoken.sas
|
||||
@brief deprecated - replaced by mv_tokenrefresh.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mv_tokenrefresh.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getaccesstoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
,code=
|
||||
,user=
|
||||
,pass=
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,refresh_token_var=REFRESH_TOKEN
|
||||
);
|
||||
|
||||
%mv_tokenrefresh(client_id=&client_id
|
||||
,client_secret=&client_secret
|
||||
,grant_type=&grant_type
|
||||
,user=&user
|
||||
,pass=&pass
|
||||
,access_token_var=&access_token_var
|
||||
,refresh_token_var=&refresh_token_var
|
||||
)
|
||||
|
||||
%mend mv_getaccesstoken;/**
|
||||
@file
|
||||
@brief deprecated - replaced by mv_registerclient.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mv_registerclient.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getapptoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
);
|
||||
|
||||
%mv_registerclient(client_id=&client_id
|
||||
,client_secret=&client_secret
|
||||
,grant_type=&grant_type
|
||||
)
|
||||
|
||||
%mend mv_getapptoken;/**
|
||||
@file mv_getclients.sas
|
||||
@brief Get a list of Viya Clients
|
||||
@details First, be sure you have an access token (which requires an app token).
|
||||
@@ -20371,38 +20462,6 @@ filename &fname0 clear;
|
||||
|
||||
%mend mv_getjobstate;
|
||||
/**
|
||||
@file mv_getrefreshtoken.sas
|
||||
@brief deprecated - replaced by mv_tokenauth.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mv_tokenauth.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getrefreshtoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
,code=
|
||||
,user=
|
||||
,pass=
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,refresh_token_var=REFRESH_TOKEN
|
||||
);
|
||||
|
||||
%mv_tokenauth(client_id=&client_id
|
||||
,client_secret=&client_secret
|
||||
,grant_type=&grant_type
|
||||
,code=&code
|
||||
,user=&user
|
||||
,pass=&pass
|
||||
,access_token_var=&access_token_var
|
||||
,refresh_token_var=&refresh_token_var
|
||||
)
|
||||
|
||||
%mend mv_getrefreshtoken;/**
|
||||
@file mv_getusergroups.sas
|
||||
@brief Creates a dataset with a list of groups for a particular user
|
||||
@details If using outside of Viya SPRE, then an access token is needed.
|
||||
@@ -22227,7 +22286,7 @@ filename &fref1 clear;
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length memsize $32;
|
||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||
memsize=quote(cats(memsize));
|
||||
@@ -22720,6 +22779,9 @@ run;
|
||||
@param [out] pkg= (utils) The output package in which to create the function.
|
||||
Uses a 3 part format: libref.catalog.package
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfunction.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mcf_length.test.sas
|
||||
|
||||
@@ -22732,13 +22794,15 @@ run;
|
||||
,pkg=UTILS
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %mf_existfunction(mcf_length)=1 %then %return;
|
||||
|
||||
%if &wrap=YES %then %do;
|
||||
proc fcmp outlib=&lib..&cat..&pkg;
|
||||
%end;
|
||||
|
||||
function mcf_length(var);
|
||||
if missing(var) then len=0;
|
||||
else if trunc(var,3)=var then len=3;
|
||||
if var=. then len=0;
|
||||
else if missing(var) or trunc(var,3)=var then len=3;
|
||||
else if trunc(var,4)=var then len=4;
|
||||
else if trunc(var,5)=var then len=5;
|
||||
else if trunc(var,6)=var then len=6;
|
||||
@@ -22894,6 +22958,9 @@ endsub;
|
||||
@param [out] pkg= (utils) The output package in which to create the function.
|
||||
Uses a 3 part format: libref.catalog.package
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfunction.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mcf_string2file(wrap=NO
|
||||
@@ -22903,6 +22970,8 @@ endsub;
|
||||
,pkg=UTILS
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %mf_existfunction(mcf_string2file)=1 %then %return;
|
||||
|
||||
%if &wrap=YES %then %do;
|
||||
proc fcmp outlib=&lib..&cat..&pkg;
|
||||
%end;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@cond
|
||||
**/
|
||||
|
||||
%macro mf_abort(mac=mf_abort.sas, type=deprecated, msg=, iftrue=%str(1=1)
|
||||
%macro mf_abort(mac=mf_abort.sas, msg=, iftrue=%str(1=1)
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
@@ -173,7 +173,8 @@
|
||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||
else debug='""';
|
||||
put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
put ' ,"MAC": "' "&mac" '"}]';
|
||||
@@ -202,7 +203,7 @@
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
syswarningtext=quote(trim(symget('syswarningtext')));
|
||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
put "}" @;
|
||||
put '>>weboutEND<<';
|
||||
run;
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
@li COMPARE - compare the current macro variables against previous values
|
||||
@param [in] scope= (GLOBAL) The scope of the variables to be checked. This
|
||||
corresponds to the values in the SCOPE column in `sashelp.vmacro`.
|
||||
@param [in] desc= (Testing variable scope) The user provided test description
|
||||
@param [in] desc= (Testing scope leakage) The user provided test description
|
||||
@param [in,out] scopeds= (work.mp_assertscope) The dataset to contain the
|
||||
scope snapshot
|
||||
@param [out] outds= (work.test_results) The output dataset to contain the
|
||||
@@ -54,7 +54,7 @@
|
||||
**/
|
||||
|
||||
%macro mp_assertscope(action,
|
||||
desc=0,
|
||||
desc=Testing Scope Leakage,
|
||||
scope=GLOBAL,
|
||||
scopeds=work.mp_assertscope,
|
||||
outds=work.test_results
|
||||
|
||||
119
base/mp_ds2squeeze.sas
Normal file
119
base/mp_ds2squeeze.sas
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
@file
|
||||
@brief Create a smaller version of a dataset, without data loss
|
||||
@details This macro will scan the input dataset and create a new one, that
|
||||
has the minimum variable lengths needed to store the data without data loss.
|
||||
|
||||
Inspiration was taken from [How to Reduce the Disk Space Required by a
|
||||
SAS® Data Set](https://www.lexjansen.com/nesug/nesug06/io/io18.pdf) by
|
||||
Selvaratnam Sridharma. The end of the referenced paper presents a macro named
|
||||
"squeeze", hence the nomenclature.
|
||||
|
||||
Usage:
|
||||
|
||||
data big;
|
||||
length my big $32000;
|
||||
do i=1 to 1e4;
|
||||
my=repeat('oh my',100);
|
||||
big='dawg';
|
||||
special=._;
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mp_ds2squeeze(work.big,outds=work.smaller)
|
||||
|
||||
The following will also be printed to the log (exact values may differ
|
||||
depending on your OS and COMPRESS settings):
|
||||
|
||||
> MP_DS2SQUEEZE: work.big was 625MB
|
||||
|
||||
> MP_DS2SQUEEZE: work.smaller is 5MB
|
||||
|
||||
@param [in] libds The library.dataset to be squeezed
|
||||
@param [out] outds= (work.mp_ds2squeeze) The squeezed dataset to create
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getfilesize.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_getmaxvarlengths.sas
|
||||
|
||||
<h4> Related Programs </h4>
|
||||
@li mp_ds2squeeze.test.sas
|
||||
|
||||
@version 9.3
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mp_ds2squeeze(
|
||||
libds,
|
||||
outds=work.work.mp_ds2squeeze,
|
||||
mdebug=0
|
||||
)/*/STORE SOURCE*/;
|
||||
%local dbg source;
|
||||
%if &mdebug=1 %then %do;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
%end;
|
||||
%else %do;
|
||||
%let dbg=*;
|
||||
%let source=/source2;
|
||||
%end;
|
||||
|
||||
%local optval ds fref;
|
||||
%let ds=%mf_getuniquename();
|
||||
%let fref=%mf_getuniquefileref();
|
||||
|
||||
%mp_getmaxvarlengths(&libds,outds=&ds)
|
||||
|
||||
data _null_;
|
||||
set &ds end=last;
|
||||
file &fref;
|
||||
/* grab the types */
|
||||
retain dsid;
|
||||
if _n_=1 then dsid=open("&libds",'is');
|
||||
if dsid le 0 then do;
|
||||
msg=sysmsg();
|
||||
put msg=;
|
||||
stop;
|
||||
end;
|
||||
type=vartype(dsid,varnum(dsid, name));
|
||||
if last then rc=close(dsid);
|
||||
/* write out the length statement */
|
||||
if _n_=1 then put 'length ';
|
||||
length len $6;
|
||||
if type='C' then do;
|
||||
if maxlen=0 then len='$1';
|
||||
else len=cats('$',maxlen);
|
||||
end;
|
||||
else do;
|
||||
if maxlen=0 then len='3';
|
||||
else len=cats(maxlen);
|
||||
end;
|
||||
put ' ' name ' ' len;
|
||||
if last then put ';';
|
||||
run;
|
||||
|
||||
/* configure varlenchk - as we are explicitly shortening the variables */
|
||||
%let optval=%sysfunc(getoption(varlenchk));
|
||||
options varlenchk=NOWARN;
|
||||
|
||||
data &outds;
|
||||
%inc &fref &source;
|
||||
set &libds;
|
||||
run;
|
||||
|
||||
options varlenchk=&optval;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
proc sql;
|
||||
drop table &ds;
|
||||
filename &fref clear;
|
||||
%end;
|
||||
|
||||
%put &sysmacroname: &libds was %mf_getfilesize(libds=&libds,format=yes);
|
||||
%put &sysmacroname: &outds is %mf_getfilesize(libds=&outds,format=yes);
|
||||
|
||||
%mend mp_ds2squeeze;
|
||||
@@ -1,28 +1,46 @@
|
||||
/**
|
||||
@file mp_getmaxvarlengths.sas
|
||||
@file
|
||||
@brief Scans a dataset to find the max length of the variable values
|
||||
@details
|
||||
This macro will scan a base dataset and produce an output dataset with two
|
||||
columns:
|
||||
|
||||
- NAME Name of the base dataset column
|
||||
- MAXLEN Maximum length of the data contained therein.
|
||||
- MAXLEN Maximum length of the data contained therein.
|
||||
|
||||
Character fields may be allocated very large widths (eg 32000) of which the
|
||||
maximum value is likely to be much narrower. This macro was designed to
|
||||
enable a HTML table to be appropriately sized however this could be used as
|
||||
part of a data audit to ensure we aren't over-sizing our tables in relation to
|
||||
the data therein.
|
||||
Character fields are often allocated very large widths (eg 32000) of which the
|
||||
maximum value is likely to be much narrower. Identifying such cases can be
|
||||
helpful in the following scenarios:
|
||||
|
||||
@li Enabling a HTML table to be appropriately sized (`num2char=YES`)
|
||||
@li Reducing the size of a dataset to save on storage (mp_ds2squeeze.sas)
|
||||
@li Identifying columns containing nothing but missing values (`MAXLEN=0` in
|
||||
the output table)
|
||||
|
||||
If the entire column is made up of (non-special) missing values then a value
|
||||
of 0 is returned.
|
||||
|
||||
Numeric fields are converted using the relevant format to determine the width.
|
||||
Usage:
|
||||
|
||||
%mp_getmaxvarlengths(sashelp.class,outds=work.myds)
|
||||
|
||||
@param libds Two part dataset (or view) reference.
|
||||
@param outds= The output dataset to create
|
||||
@param [in] libds Two part dataset (or view) reference.
|
||||
@param [in] num2char= (NO) When set to NO, numeric fields are sized according
|
||||
to the number of bytes used (or set to zero in the case of non-special
|
||||
missings). When YES, the numeric field is converted to character (using the
|
||||
format, if available), and that is sized instead, using `lengthn()`.
|
||||
@param [out] outds= The output dataset to create, eg:
|
||||
|NAME:$8.|MAXLEN:best.|
|
||||
|---|---|
|
||||
|`Name `|`7 `|
|
||||
|`Sex `|`1 `|
|
||||
|`Age `|`3 `|
|
||||
|`Height `|`8 `|
|
||||
|`Weight `|`3 `|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mcf_length.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvarlist.sas
|
||||
@li mf_getvartype.sas
|
||||
@li mf_getvarformat.sas
|
||||
@@ -30,20 +48,32 @@
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_ds2squeeze.sas
|
||||
@li mp_getmaxvarlengths.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_getmaxvarlengths(
|
||||
libds /* libref.dataset to analyse */
|
||||
,outds=work.mp_getmaxvarlengths /* name of output dataset to create */
|
||||
libds
|
||||
,num2char=NO
|
||||
,outds=work.mp_getmaxvarlengths
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local vars x var fmt;
|
||||
%local vars prefix x var fmt;
|
||||
%let vars=%mf_getvarlist(libds=&libds);
|
||||
%let prefix=%substr(%mf_getuniquename(),1,25);
|
||||
%let num2char=%upcase(&num2char);
|
||||
|
||||
%if &num2char=NO %then %do;
|
||||
/* compile length function for numeric fields */
|
||||
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||
%end;
|
||||
|
||||
proc sql;
|
||||
create table &outds (rename=(
|
||||
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||
________&x=%scan(&vars,&x)
|
||||
&prefix.&x=%scan(&vars,&x)
|
||||
%end;
|
||||
))
|
||||
as select
|
||||
@@ -51,18 +81,21 @@ create table &outds (rename=(
|
||||
%let var=%scan(&vars,&x);
|
||||
%if &x>1 %then ,;
|
||||
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||
max(length(&var)) as ________&x
|
||||
max(lengthn(&var)) as &prefix.&x
|
||||
%end;
|
||||
%else %do;
|
||||
%else %if &num2char=YES %then %do;
|
||||
%let fmt=%mf_getvarformat(&libds,&var);
|
||||
%put fmt=&fmt;
|
||||
%if %str(&fmt)=%str() %then %do;
|
||||
max(length(cats(&var))) as ________&x
|
||||
max(lengthn(cats(&var))) as &prefix.&x
|
||||
%end;
|
||||
%else %do;
|
||||
max(length(put(&var,&fmt))) as ________&x
|
||||
max(lengthn(put(&var,&fmt))) as &prefix.&x
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
max(mcf_length(&var)) as &prefix.&x
|
||||
%end;
|
||||
%end;
|
||||
from &libds;
|
||||
|
||||
|
||||
@@ -33,37 +33,41 @@
|
||||
%macro mp_init(prefix=SASJS
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%global
|
||||
&prefix._INIT_NUM /* initialisation time as numeric */
|
||||
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
|
||||
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
|
||||
;
|
||||
%if %eval(&&&prefix._INIT_NUM>0) %then %return; /* only run once */
|
||||
%if %symexist(SASJS_PREFIX) %then %return; /* only run once */
|
||||
|
||||
data _null_;
|
||||
dttm=datetime();
|
||||
call symputx("&prefix._init_num",dttm,'g');
|
||||
call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6),'g');
|
||||
call symputx("&prefix.work",pathname('WORK'),'g');
|
||||
run;
|
||||
%global
|
||||
SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
|
||||
&prefix._INIT_NUM /* initialisation time as numeric */
|
||||
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
|
||||
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
|
||||
;
|
||||
|
||||
options
|
||||
noautocorrect /* disallow misspelled procedure names */
|
||||
compress=CHAR /* default is none so ensure we have something! */
|
||||
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
||||
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
||||
fmterr /* ensure err when a format cannot be found */
|
||||
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
|
||||
missing=. /* changing this can cause hard to detect errs */
|
||||
noquotelenmax /* avoid warnings for long strings */
|
||||
noreplace /* avoid overwriting permanent datasets */
|
||||
ps=max /* reduce log size slightly */
|
||||
ls=max /* reduce log even more and avoid word truncation */
|
||||
validmemname=COMPATIBLE /* avoid special characters etc in table names */
|
||||
validvarname=V7 /* avoid special characters etc in variable names */
|
||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||
;
|
||||
%let sasjs_prefix=&prefix;
|
||||
|
||||
data _null_;
|
||||
dttm=datetime();
|
||||
call symputx("&sasjs_prefix._init_num",dttm,'g');
|
||||
call symputx("&sasjs_prefix._init_dttm",put(dttm,E8601DT26.6),'g');
|
||||
call symputx("&sasjs_prefix.work",pathname('WORK'),'g');
|
||||
run;
|
||||
|
||||
options
|
||||
noautocorrect /* disallow misspelled procedure names */
|
||||
compress=CHAR /* default is none so ensure we have something! */
|
||||
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
||||
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
||||
fmterr /* ensure err when a format cannot be found */
|
||||
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
|
||||
missing=. /* changing this can cause hard to detect errs */
|
||||
noquotelenmax /* avoid warnings for long strings */
|
||||
noreplace /* avoid overwriting permanent datasets */
|
||||
ps=max /* reduce log size slightly */
|
||||
ls=max /* reduce log even more and avoid word truncation */
|
||||
validmemname=COMPATIBLE /* avoid special characters etc in table names */
|
||||
validvarname=V7 /* avoid special characters etc in variable names */
|
||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
||||
;
|
||||
|
||||
%mend mp_init;
|
||||
@@ -62,7 +62,6 @@
|
||||
|
||||
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
|
||||
,engine=DATASTEP
|
||||
,dbg=0 /* DEPRECATED */
|
||||
,missing=NULL
|
||||
,showmeta=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
@param [out] pkg= (utils) The output package in which to create the function.
|
||||
Uses a 3 part format: libref.catalog.package
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfunction.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mcf_length.test.sas
|
||||
|
||||
@@ -51,13 +54,15 @@
|
||||
,pkg=UTILS
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %mf_existfunction(mcf_length)=1 %then %return;
|
||||
|
||||
%if &wrap=YES %then %do;
|
||||
proc fcmp outlib=&lib..&cat..&pkg;
|
||||
%end;
|
||||
|
||||
function mcf_length(var);
|
||||
if missing(var) then len=0;
|
||||
else if trunc(var,3)=var then len=3;
|
||||
if var=. then len=0;
|
||||
else if missing(var) or trunc(var,3)=var then len=3;
|
||||
else if trunc(var,4)=var then len=4;
|
||||
else if trunc(var,5)=var then len=5;
|
||||
else if trunc(var,6)=var then len=6;
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
@param [out] pkg= (utils) The output package in which to create the function.
|
||||
Uses a 3 part format: libref.catalog.package
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existfunction.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mcf_string2file(wrap=NO
|
||||
@@ -48,6 +51,8 @@
|
||||
,pkg=UTILS
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if %mf_existfunction(mcf_string2file)=1 %then %return;
|
||||
|
||||
%if &wrap=YES %then %do;
|
||||
proc fcmp outlib=&lib..&cat..&pkg;
|
||||
%end;
|
||||
|
||||
@@ -95,7 +95,6 @@ data _null_;
|
||||
put ' ';
|
||||
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*/; ';
|
||||
@@ -426,7 +425,7 @@ data _null_;
|
||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||
put ' length memsize $32; ';
|
||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||
put ' memsize=quote(cats(memsize)); ';
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
@param [in] stpcode= the source file (or fileref) containing the SAS code to load
|
||||
into the stp. For multiple files, they should simply be concatenated first.
|
||||
@param [in] minify= set to YES in order to strip comments, blank lines, and CRLFs.
|
||||
|
||||
@param frefin= deprecated - a unique fileref is now always used
|
||||
@param frefout= deprecated - a unique fileref is now always used
|
||||
@param mDebug= set to 1 to show debug messages in the log
|
||||
|
||||
@version 9.3
|
||||
@@ -30,16 +27,8 @@
|
||||
,stpcode=
|
||||
,minify=NO
|
||||
,mdebug=0
|
||||
/* deprecated */
|
||||
,frefin=inmeta
|
||||
,frefout=outmeta
|
||||
);
|
||||
|
||||
%if &frefin ne inmeta or &frefout ne outmeta %then %do;
|
||||
%put %str(WARN)ING: the frefin and frefout parameters will be deprecated in
|
||||
an upcoming release.;
|
||||
%end;
|
||||
|
||||
/* first, check if STP exists */
|
||||
%local tsuri;
|
||||
%let tsuri=stopifempty ;
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length memsize $32;
|
||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||
memsize=quote(cats(memsize));
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length autoexec $512;
|
||||
autoexec=quote(urlencode(trim(getoption('autoexec'))));
|
||||
put ',"AUTOEXEC" : ' autoexec;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
data test;
|
||||
call symputx('null',mcf_length(.));
|
||||
call symputx('special',mcf_length(._));
|
||||
call symputx('three',mcf_length(1));
|
||||
call symputx('four',mcf_length(10000000));
|
||||
call symputx('five',mcf_length(12345678));
|
||||
@@ -24,6 +25,10 @@ run;
|
||||
iftrue=(%str(&null)=%str(0)),
|
||||
desc=Check if NULL returns 0
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%str(&special)=%str(3)),
|
||||
desc=Check if special missing ._ returns 3
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%str(&three)=%str(3)),
|
||||
desc=Check for length 3
|
||||
@@ -47,4 +52,16 @@ run;
|
||||
%mp_assert(
|
||||
iftrue=(%str(&eight)=%str(8)),
|
||||
desc=Check for length 8
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Check syscc=0 before re-initialisation
|
||||
)
|
||||
|
||||
/* test 2 - compile again test for warnings */
|
||||
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Check syscc=0 after re-initialisation
|
||||
)
|
||||
44
tests/crossplatform/mp_ds2squeeze.test.sas
Normal file
44
tests/crossplatform/mp_ds2squeeze.test.sas
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_ds2squeeze.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getvarlen.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mp_ds2squeeze.sas
|
||||
|
||||
**/
|
||||
|
||||
data big;
|
||||
length my big $32000;
|
||||
do i=1 to 1e4;
|
||||
my=repeat('oh my',100);
|
||||
big='dawg';
|
||||
special=._;
|
||||
missn=.;
|
||||
missc='';
|
||||
output;
|
||||
end;
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_ds2squeeze(work.big,outds=work.smaller)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking syscc
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarlen(work.smaller,missn)=3),
|
||||
desc=Check missing numeric is 3
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarlen(work.smaller,special)=3),
|
||||
desc=Check missing special numeric is 3
|
||||
)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_getvarlen(work.smaller,missc)=1),
|
||||
desc=Check missing char is 1
|
||||
)
|
||||
80
tests/crossplatform/mp_getmaxvarlengths.test.sas
Normal file
80
tests/crossplatform/mp_getmaxvarlengths.test.sas
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_getmaxvarlengths macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_getmaxvarlengths.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
/* regular usage */
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_getmaxvarlengths(sashelp.class,outds=work.myds)
|
||||
%mp_assertscope(COMPARE,desc=checking scope leakage on mp_getmaxvarlengths)
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=No errs
|
||||
)
|
||||
%mp_assertdsobs(work.myds,
|
||||
desc=Has 5 records,
|
||||
test=EQUALS 5
|
||||
)
|
||||
data work.errs;
|
||||
set work.myds;
|
||||
if name='Name' and maxlen ne 7 then output;
|
||||
if name='Sex' and maxlen ne 1 then output;
|
||||
if name='Age' and maxlen ne 3 then output;
|
||||
if name='Height' and maxlen ne 8 then output;
|
||||
if name='Weight' and maxlen ne 3 then output;
|
||||
run;
|
||||
data _null_;
|
||||
set work.errs;
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mp_assertdsobs(work.errs,
|
||||
desc=Err table has 0 records,
|
||||
test=EQUALS 0
|
||||
)
|
||||
|
||||
/* test2 */
|
||||
data work.test2;
|
||||
length a 3 b 5;
|
||||
a=1/3;
|
||||
b=1/3;
|
||||
c=1/3;
|
||||
d=._;
|
||||
e=.;
|
||||
output;
|
||||
output;
|
||||
run;
|
||||
%mp_getmaxvarlengths(work.test2,outds=work.myds2)
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=No errs in second test (with nulls)
|
||||
)
|
||||
%mp_assertdsobs(work.myds2,
|
||||
desc=Has 5 records,
|
||||
test=EQUALS 5
|
||||
)
|
||||
data work.errs2;
|
||||
set work.myds2;
|
||||
if name='a' and maxlen ne 3 then output;
|
||||
if name='b' and maxlen ne 5 then output;
|
||||
if name='c' and maxlen ne 8 then output;
|
||||
if name='d' and maxlen ne 3 then output;
|
||||
if name='e' and maxlen ne 0 then output;
|
||||
run;
|
||||
data _null_;
|
||||
set work.errs2;
|
||||
putlog (_all_)(=);
|
||||
run;
|
||||
|
||||
%mp_assertdsobs(work.errs2,
|
||||
desc=Err table has 0 records,
|
||||
test=EQUALS 0
|
||||
)
|
||||
@@ -239,7 +239,6 @@ data _null_;
|
||||
put ' ';
|
||||
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*/; ';
|
||||
@@ -627,7 +626,7 @@ data _null_;
|
||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||
put ' put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" "; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),datetime20.3)" ''" ''; ';
|
||||
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
|
||||
put ' length memsize $32; ';
|
||||
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
|
||||
put ' memsize=quote(cats(memsize)); ';
|
||||
|
||||
@@ -117,6 +117,7 @@ libname &libref1a JSON fileref=&fname1a;
|
||||
%let found=0;
|
||||
%put Getting object uri from &libref1a..items;
|
||||
data _null_;
|
||||
length contenttype name $1000;
|
||||
set &libref1a..items;
|
||||
if contenttype="&contenttype" and upcase(name)="%upcase(&name)" then do;
|
||||
call symputx('uri',uri,'l');
|
||||
|
||||
@@ -114,6 +114,7 @@ libname &libref1a JSON fileref=&fname1a;
|
||||
%let found=0;
|
||||
%put Getting object uri from &libref1a..items;
|
||||
data _null_;
|
||||
length contenttype name $1000;
|
||||
set &libref1a..items;
|
||||
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
|
||||
call symputx('uri',cats("&base_uri",uri),'l');
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
@file mv_getaccesstoken.sas
|
||||
@brief deprecated - replaced by mv_tokenrefresh.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mv_tokenrefresh.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getaccesstoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
,code=
|
||||
,user=
|
||||
,pass=
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,refresh_token_var=REFRESH_TOKEN
|
||||
);
|
||||
|
||||
%mv_tokenrefresh(client_id=&client_id
|
||||
,client_secret=&client_secret
|
||||
,grant_type=&grant_type
|
||||
,user=&user
|
||||
,pass=&pass
|
||||
,access_token_var=&access_token_var
|
||||
,refresh_token_var=&refresh_token_var
|
||||
)
|
||||
|
||||
%mend mv_getaccesstoken;
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
@file
|
||||
@brief deprecated - replaced by mv_registerclient.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mv_registerclient.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getapptoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
);
|
||||
|
||||
%mv_registerclient(client_id=&client_id
|
||||
,client_secret=&client_secret
|
||||
,grant_type=&grant_type
|
||||
)
|
||||
|
||||
%mend mv_getapptoken;
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
@file mv_getrefreshtoken.sas
|
||||
@brief deprecated - replaced by mv_tokenauth.sas
|
||||
|
||||
@version VIYA V.03.04
|
||||
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mv_tokenauth.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mv_getrefreshtoken(client_id=someclient
|
||||
,client_secret=somesecret
|
||||
,grant_type=authorization_code
|
||||
,code=
|
||||
,user=
|
||||
,pass=
|
||||
,access_token_var=ACCESS_TOKEN
|
||||
,refresh_token_var=REFRESH_TOKEN
|
||||
);
|
||||
|
||||
%mv_tokenauth(client_id=&client_id
|
||||
,client_secret=&client_secret
|
||||
,grant_type=&grant_type
|
||||
,code=&code
|
||||
,user=&user
|
||||
,pass=&pass
|
||||
,access_token_var=&access_token_var
|
||||
,refresh_token_var=&refresh_token_var
|
||||
)
|
||||
|
||||
%mend mv_getrefreshtoken;
|
||||
@@ -225,7 +225,7 @@
|
||||
sysvlong=quote(trim(symget('sysvlong')));
|
||||
put ',"SYSVLONG" : ' sysvlong;
|
||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||
length memsize $32;
|
||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||
memsize=quote(cats(memsize));
|
||||
|
||||
Reference in New Issue
Block a user