mirror of
https://github.com/sasjs/core.git
synced 2025-12-12 15:04:36 +00:00
Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4c2be7411 | ||
|
|
16489a9494 | ||
|
|
0e03b06a4b | ||
|
|
c3b89c7f7d | ||
|
|
142b46570d | ||
|
|
f7fac50108 | ||
|
|
ae5fbcf857 | ||
|
|
2579b4c929 | ||
|
|
90a831f59b | ||
|
|
9fb218f0be | ||
|
|
ccc9dfa4aa | ||
|
|
a37a72b7db | ||
|
|
c6dcf919e2 | ||
|
|
42541373af | ||
|
|
208c88f5a4 | ||
|
|
5605bc74df | ||
|
|
4bec574011 | ||
|
|
8cfa37ce8b | ||
|
|
351ceeb357 | ||
|
|
259bcc0173 | ||
|
|
db195a8311 | ||
|
|
4307bfb1b5 | ||
|
|
df46ee6939 | ||
|
|
70b9b71104 | ||
|
|
cd33355418 | ||
|
|
77d1cdb753 | ||
|
|
545218e3b9 | ||
|
|
cb07305a87 | ||
|
|
76a39cad20 | ||
|
|
ebd567af48 | ||
|
|
a9c418e3f2 | ||
|
|
e143acd67d | ||
|
|
84eb2f1845 | ||
|
|
b075e5d5d5 | ||
|
|
a08f6aeea2 | ||
|
|
469bd574ac | ||
|
|
c41918c0a8 | ||
|
|
0361ca574d | ||
|
|
c75c169b80 | ||
|
|
eac47bd5db | ||
|
|
d302ef266d | ||
|
|
fdfe9b8250 | ||
|
|
9b1f0d7bcb | ||
|
|
98b1c44283 | ||
|
|
ce026f19b5 | ||
|
|
8e723d06b0 | ||
|
|
a6d84cc65a | ||
|
|
536ce8e95d | ||
|
|
bc1d9e619b | ||
|
|
1062a97cfe | ||
|
|
51db64c90a | ||
|
|
7c4278c3f9 | ||
|
|
6c6b55dcea | ||
|
|
66b0c9e77e | ||
|
|
caf3b95269 | ||
|
|
3866b97416 | ||
|
|
d687658687 | ||
|
|
9f815c73e9 | ||
|
|
a13c782074 | ||
|
|
f2991cfd63 | ||
|
|
8eb4f0844c | ||
|
|
f90dc069dc | ||
|
|
436b430389 | ||
|
|
6667b91ced | ||
|
|
bce56d8105 | ||
|
|
2ec440b321 | ||
|
|
3d2ad531cf | ||
|
|
09136cfdbb | ||
|
|
1e72f13f2d |
11
README.md
11
README.md
@@ -125,6 +125,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
|||||||
- macro names must be lowercase
|
- macro names must be lowercase
|
||||||
- one macro per file
|
- one macro per file
|
||||||
- prefixes:
|
- prefixes:
|
||||||
|
- _mcf_ for macro compiled functions (proc fcmp)
|
||||||
- _mf_ for macro functions (can be used in open code).
|
- _mf_ for macro functions (can be used in open code).
|
||||||
- _ml_ for macros that are used to compile LUA modules
|
- _ml_ for macros that are used to compile LUA modules
|
||||||
- _mm_ for metadata macros (interface with the metadata server).
|
- _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
|
@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.
|
When contributing to this library, it is therefore important to ensure that all dependencies are listed in the header in this format.
|
||||||
|
|
||||||
@@ -182,12 +183,20 @@ 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.
|
- 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.
|
- 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;`
|
- 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.
|
- 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
|
## General Notes
|
||||||
|
|
||||||
- All macros should be compatible with SAS versions from support level B and above (so currently 9.2 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully (eg `%if %sysevalf(&sysver<9.3) %then %return`).
|
- All macros should be compatible with SAS versions from support level B and above (so currently 9.2 and later). If an earlier version is not supported, then the macro should say as such in the header documentation, and exit gracefully (eg `%if %sysevalf(&sysver<9.3) %then %return`).
|
||||||
|
|
||||||
|
## Breaking Changes
|
||||||
|
|
||||||
|
We are currently on major release 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:
|
||||||
|
|
||||||
|
* (None as yet)
|
||||||
|
|
||||||
## Star Gazing
|
## Star Gazing
|
||||||
|
|
||||||
If you find this library useful, please leave a [star](https://github.com/sasjs/core/stargazers) and help us grow our star graph!
|
If you find this library useful, please leave a [star](https://github.com/sasjs/core/stargazers) and help us grow our star graph!
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
@cond
|
@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*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||||
|
|||||||
@@ -7,8 +7,12 @@
|
|||||||
|
|
||||||
%put %mf_existvar(work.someds, somevar)
|
%put %mf_existvar(work.someds, somevar)
|
||||||
|
|
||||||
@param libds (positional) - 2 part dataset or view reference
|
@param [in] libds 2 part dataset or view reference
|
||||||
@param var (positional) - variable name
|
@param [in] var variable name
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_existvar.test.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
|||||||
@@ -12,9 +12,10 @@
|
|||||||
contributors of Chris Hemedingers blog [post](
|
contributors of Chris Hemedingers blog [post](
|
||||||
http://blogs.sas.com/content/sasdummy/2013/06/04/find-a-sas-library-engine/)
|
http://blogs.sas.com/content/sasdummy/2013/06/04/find-a-sas-library-engine/)
|
||||||
|
|
||||||
@param libref Library reference (also accepts a 2 level libds ref).
|
@param [in] libref Library reference (also accepts a 2 level libds ref).
|
||||||
|
|
||||||
@return output returns the library engine for the FIRST library encountered.
|
@return output returns the library engine (uppercase) for the FIRST library
|
||||||
|
encountered.
|
||||||
|
|
||||||
@warning will only return the FIRST library engine - for concatenated
|
@warning will only return the FIRST library engine - for concatenated
|
||||||
libraries, with different engines, inconsistent results may be encountered.
|
libraries, with different engines, inconsistent results may be encountered.
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
%let rc= %sysfunc(close(&dsid));
|
%let rc= %sysfunc(close(&dsid));
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
&engine
|
%upcase(&engine)
|
||||||
|
|
||||||
%mend mf_getengine;
|
%mend mf_getengine;
|
||||||
|
|
||||||
|
|||||||
40
base/mf_islibds.sas
Normal file
40
base/mf_islibds.sas
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Checks whether a string follows correct library.dataset format
|
||||||
|
@details Many macros in the core library accept a library.dataset parameter
|
||||||
|
referred to as 'libds'. This macro validates the structure of that parameter,
|
||||||
|
eg:
|
||||||
|
|
||||||
|
@li 8 character libref?
|
||||||
|
@li 32 character dataset?
|
||||||
|
@li contains a period?
|
||||||
|
|
||||||
|
It does NOT check whether the dataset exists, or if the library is assigned.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%put %mf_islibds(work.something)=1;
|
||||||
|
%put %mf_islibds(nolib)=0;
|
||||||
|
%put %mf_islibds(badlibref.ds)=0;
|
||||||
|
%put %mf_islibds(w.t.f)=0;
|
||||||
|
|
||||||
|
@param [in] libds The string to be checked
|
||||||
|
|
||||||
|
@return output Returns 1 if libds is valid, 0 if it is not
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_islibds.test.sas
|
||||||
|
@li mp_validatecol.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_islibds(libds
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local regex;
|
||||||
|
%let regex=%sysfunc(prxparse(%str(/^[_a-z]\w{0,7}\.[_a-z]\w{0,31}$/i)));
|
||||||
|
|
||||||
|
%sysfunc(prxmatch(®ex,&libds))
|
||||||
|
|
||||||
|
%mend mf_islibds;
|
||||||
54
base/mf_wordsinstr1andstr2.sas
Normal file
54
base/mf_wordsinstr1andstr2.sas
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Returns words that are in both string 1 and string 2
|
||||||
|
@details Compares two space separated strings and returns the words that are
|
||||||
|
in both.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%put %mf_wordsInStr1andStr2(
|
||||||
|
Str1=blah sss blaaah brah bram boo
|
||||||
|
,Str2= blah blaaah brah ssss
|
||||||
|
);
|
||||||
|
|
||||||
|
returns:
|
||||||
|
> blah blaaah brah
|
||||||
|
|
||||||
|
@param str1= string containing words to extract
|
||||||
|
@param str2= used to compare with the extract string
|
||||||
|
|
||||||
|
@warning CASE SENSITIVE!
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mf_wordsInStr1andStr2(
|
||||||
|
Str1= /* string containing words to extract */
|
||||||
|
,Str2= /* used to compare with the extract string */
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%local count_base count_extr i i2 extr_word base_word match outvar;
|
||||||
|
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
|
||||||
|
%put %str(WARN)ING: empty string provided!;
|
||||||
|
%put base string (str1)= &str1;
|
||||||
|
%put compare string (str2) = &str2;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
%let count_base=%sysfunc(countw(&Str2));
|
||||||
|
%let count_extr=%sysfunc(countw(&Str1));
|
||||||
|
|
||||||
|
%do i=1 %to &count_extr;
|
||||||
|
%let extr_word=%scan(&Str1,&i,%str( ));
|
||||||
|
%let match=0;
|
||||||
|
%do i2=1 %to &count_base;
|
||||||
|
%let base_word=%scan(&Str2,&i2,%str( ));
|
||||||
|
%if &extr_word=&base_word %then %let match=1;
|
||||||
|
%end;
|
||||||
|
%if &match=1 %then %let outvar=&outvar &extr_word;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
&outvar
|
||||||
|
|
||||||
|
%mend mf_wordsInStr1andStr2;
|
||||||
|
|
||||||
67
base/mf_writefile.sas
Normal file
67
base/mf_writefile.sas
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Creates a text file using pure macro
|
||||||
|
@details Creates a text file of up to 10 lines. If further lines are
|
||||||
|
desired, feel free to [create an issue](
|
||||||
|
https://github.com/sasjs/core/issues/new), or make a pull request!
|
||||||
|
|
||||||
|
The use of PARMBUFF was considered for this macro, but it would have made
|
||||||
|
things problematic for writing lines containing commas.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mf_writefile(&sasjswork/myfile.txt,l1=some content,l2=more content)
|
||||||
|
data _null_;
|
||||||
|
infile "&sasjswork/myfile.txt";
|
||||||
|
input;
|
||||||
|
list;
|
||||||
|
run;
|
||||||
|
|
||||||
|
@param [in] fpath Full path to file to be created or appended to
|
||||||
|
@param [in] mode= (O) Available options are A or O as follows:
|
||||||
|
@li A APPEND mode, writes new records after the current end of the file.
|
||||||
|
@li O OUTPUT mode, writes new records from the beginning of the file.
|
||||||
|
@param [in] l1= First line
|
||||||
|
@param [in] l2= Second line (etc through to l10)
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_writefile.test.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
/** @cond */
|
||||||
|
|
||||||
|
%macro mf_writefile(fpath,mode=O,l1=,l2=,l3=,l4=,l5=,l6=,l7=,l8=,l9=,l10=
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local fref rc fid i total_lines;
|
||||||
|
|
||||||
|
/* find number of lines by reference to first non-blank param */
|
||||||
|
%do i=10 %to 1 %by -1;
|
||||||
|
%if %str(&&l&i) ne %str() %then %goto continue;
|
||||||
|
%end;
|
||||||
|
%continue:
|
||||||
|
%let total_lines=&i;
|
||||||
|
|
||||||
|
%if %sysfunc(filename(fref,&fpath)) ne 0 %then %do;
|
||||||
|
%put &=fref &=fpath;
|
||||||
|
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%let fid=%sysfunc(fopen(&fref,&mode));
|
||||||
|
|
||||||
|
%if &fid=0 %then %do;
|
||||||
|
%put %str(ERR)OR: %sysfunc(sysmsg());
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%do i=1 %to &total_lines;
|
||||||
|
%let rc=%sysfunc(fput(&fid, &&l&i));
|
||||||
|
%let rc=%sysfunc(fwrite(&fid));
|
||||||
|
%end;
|
||||||
|
%let rc=%sysfunc(fclose(&fid));
|
||||||
|
%let rc=%sysfunc(filename(&fref));
|
||||||
|
|
||||||
|
%mend mf_writefile;
|
||||||
|
/** @endcond */
|
||||||
@@ -110,8 +110,8 @@
|
|||||||
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
%if %symexist(SYSPRINTTOLOG) %then %let logloc=&SYSPRINTTOLOG;
|
||||||
%else %let logloc=%qsysfunc(getoption(LOG));
|
%else %let logloc=%qsysfunc(getoption(LOG));
|
||||||
proc printto log=log;run;
|
proc printto log=log;run;
|
||||||
|
%let logline=0;
|
||||||
%if %length(&logloc)>0 %then %do;
|
%if %length(&logloc)>0 %then %do;
|
||||||
%let logline=0;
|
|
||||||
data _null_;
|
data _null_;
|
||||||
infile &logloc lrecl=5000;
|
infile &logloc lrecl=5000;
|
||||||
input; putlog _infile_;
|
input; putlog _infile_;
|
||||||
@@ -160,7 +160,10 @@
|
|||||||
file _webout mod lrecl=32000 encoding='utf-8';
|
file _webout mod lrecl=32000 encoding='utf-8';
|
||||||
length msg $32767 ;
|
length msg $32767 ;
|
||||||
sasdatetime=datetime();
|
sasdatetime=datetime();
|
||||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
msg=symget('msg');
|
||||||
|
%if &logline>0 %then %do;
|
||||||
|
msg=cats(msg,'\n\nLog Extract:\n',symget('logmsg'));
|
||||||
|
%end;
|
||||||
/* escape the quotes */
|
/* escape the quotes */
|
||||||
msg=tranwrd(msg,'"','\"');
|
msg=tranwrd(msg,'"','\"');
|
||||||
/* ditch the CRLFs as chrome complains */
|
/* ditch the CRLFs as chrome complains */
|
||||||
@@ -170,7 +173,8 @@
|
|||||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||||
else debug='""';
|
else debug='""';
|
||||||
put '>>weboutBEGIN<<';
|
put '>>weboutBEGIN<<';
|
||||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
put '{"SYSDATE" : "' "&SYSDATE" '"';
|
||||||
|
put ',"SYSTIME" : "' "&SYSTIME" '"';
|
||||||
put ',"sasjsAbort" : [{';
|
put ',"sasjsAbort" : [{';
|
||||||
put ' "MSG":' msg ;
|
put ' "MSG":' msg ;
|
||||||
put ' ,"MAC": "' "&mac" '"}]';
|
put ' ,"MAC": "' "&mac" '"}]';
|
||||||
@@ -199,7 +203,7 @@
|
|||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
syswarningtext=quote(trim(symget('syswarningtext')));
|
syswarningtext=quote(trim(symget('syswarningtext')));
|
||||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||||
put "}" @;
|
put "}" @;
|
||||||
put '>>weboutEND<<';
|
put '>>weboutEND<<';
|
||||||
run;
|
run;
|
||||||
|
|||||||
181
base/mp_applyformats.sas
Normal file
181
base/mp_applyformats.sas
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Apply a set of formats to a table
|
||||||
|
@details Applies a set of formats to the metadata of one or more SAS datasets.
|
||||||
|
Can be used to migrate formats from one table to another. The input table
|
||||||
|
must contain the following columns:
|
||||||
|
|
||||||
|
@li lib - the libref of the table to be updated
|
||||||
|
@li ds - the dataset to be updated
|
||||||
|
@li var - the variable to be updated
|
||||||
|
@li fmt - the format to apply. Missing or default ($CHAR, 8.) formats are
|
||||||
|
ignored.
|
||||||
|
|
||||||
|
The macro will abort in the following scenarios:
|
||||||
|
|
||||||
|
@li Libref not assigned
|
||||||
|
@li Dataset does not exist
|
||||||
|
@li Input table contains null or invalid values
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
data work.example;
|
||||||
|
set sashelp.prdsale;
|
||||||
|
format _all_ clear;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_getcols(sashelp.prdsale,outds=work.cols)
|
||||||
|
|
||||||
|
data work.cols2;
|
||||||
|
set work.cols;
|
||||||
|
lib='WORK';
|
||||||
|
ds='EXAMPLE';
|
||||||
|
var=name;
|
||||||
|
fmt=format;
|
||||||
|
keep lib ds var fmt;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_applyformats(work.cols2)
|
||||||
|
|
||||||
|
@param [in] inds The input dataset containing the formats to apply (and where
|
||||||
|
to apply them). Example structure:
|
||||||
|
|LIB:$8.|DS:$32.|VAR:$32.|FMT:$49.|
|
||||||
|
|---|---|---|---|
|
||||||
|
|`WORK `|`EXAMPLE `|`ACTUAL `|`DOLLAR12.2 `|
|
||||||
|
|`WORK `|`EXAMPLE `|`COUNTRY `|`$CHAR10. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`DIVISION `|`$CHAR10. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`MONTH `|`MONNAME3. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`PREDICT `|`DOLLAR12.2 `|
|
||||||
|
|`WORK `|`EXAMPLE `|`PRODTYPE `|`$CHAR10. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`PRODUCT `|`$CHAR10. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`QUARTER `|`8. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`REGION `|`$CHAR10. `|
|
||||||
|
|`WORK `|`EXAMPLE `|`YEAR `|`8. `|
|
||||||
|
|
||||||
|
@param [out] errds= (0) Provide a libds reference here to export the
|
||||||
|
error messages to a table. In this case, they will not be printed to the
|
||||||
|
log.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getengine.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_validatecol.sas
|
||||||
|
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_getformats.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_applyformats(inds,errds=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local outds liblist i engine lib msg ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validations
|
||||||
|
*/
|
||||||
|
proc sort data=&inds;
|
||||||
|
by lib ds var fmt;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &errds=0 %then %let outds=%mf_getuniquename(prefix=mp_applyformats);
|
||||||
|
%else %let outds=&errds;
|
||||||
|
|
||||||
|
data &outds;
|
||||||
|
set &inds;
|
||||||
|
where fmt not in ('','.', '$', '$CHAR.','8.');
|
||||||
|
length msg $128;
|
||||||
|
by lib ds var fmt;
|
||||||
|
if libref(lib) ne 0 then do;
|
||||||
|
msg=catx(' ','libref',lib,'is not assigned!');
|
||||||
|
%if &errds=0 %then %do;
|
||||||
|
putlog "%str(ERR)OR: " msg;
|
||||||
|
%end;
|
||||||
|
output;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
if exist(cats(lib,'.',ds)) ne 1 then do;
|
||||||
|
msg=catx(' ','libds',lib,'.',ds,'does not exist!');
|
||||||
|
%if &errds=0 %then %do;
|
||||||
|
putlog "%str(ERR)OR: " msg;
|
||||||
|
%end;
|
||||||
|
output;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
%mp_validatecol(fmt,FORMAT,is_fmt)
|
||||||
|
if is_fmt=0 then do;
|
||||||
|
msg=catx(' ','format',fmt,'on libds',lib,'.',ds,'.',var,'is not valid!');
|
||||||
|
%if &errds=0 %then %do;
|
||||||
|
putlog "%str(ERR)OR: " msg;
|
||||||
|
%end;
|
||||||
|
output;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if first.ds then do;
|
||||||
|
retain dsid;
|
||||||
|
dsid=open(cats(lib,'.',ds));
|
||||||
|
if dsid=0 then do;
|
||||||
|
msg=catx(' ','libds',lib,'.',ds,' could not be opened!');
|
||||||
|
%if &errds=0 %then %do;
|
||||||
|
putlog "%str(ERR)OR: " msg;
|
||||||
|
%end;
|
||||||
|
output;
|
||||||
|
return;
|
||||||
|
end;
|
||||||
|
if varnum(dsid,var)<1 then do;
|
||||||
|
msg=catx(' ','Variable',lib,'.',ds,'.',var,' was not found!');
|
||||||
|
%if &errds=0 %then %do;
|
||||||
|
putlog "%str(ERR)OR: " msg;
|
||||||
|
%end;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
if last.ds then rc=close(dsid);
|
||||||
|
run;
|
||||||
|
|
||||||
|
proc sql noprint;
|
||||||
|
select distinct lib into: liblist separated by ' ' from &inds;
|
||||||
|
%put &=liblist;
|
||||||
|
%if %length(&liblist)>0 %then %do i=1 %to %sysfunc(countw(&liblist));
|
||||||
|
%let lib=%scan(&liblist,1);
|
||||||
|
%let engine=%mf_getengine(&lib);
|
||||||
|
%if &engine ne V9 and &engine ne BASE %then %do;
|
||||||
|
%let msg=&lib has &engine engine - formats cannot be applied;
|
||||||
|
insert into &outds set lib="&lib",ds="_all_",var="_all", msg="&msg" ;
|
||||||
|
%if &errds=0 %then %put %str(ERR)OR: &msg;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
quit;
|
||||||
|
|
||||||
|
%if %mf_nobs(&outds)>0 %then %return;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validations complete - now apply the actual formats!
|
||||||
|
*/
|
||||||
|
%let fref=%mf_getuniquefileref();
|
||||||
|
data _null_;
|
||||||
|
set &inds;
|
||||||
|
by lib ds var fmt;
|
||||||
|
where fmt not in ('','.', '$', '$CHAR.','8.');
|
||||||
|
file &fref;
|
||||||
|
if first.lib then put 'proc datasets nolist lib=' lib ';';
|
||||||
|
if first.ds then put ' modify ' ds ';';
|
||||||
|
put ' format ' var fmt ';';
|
||||||
|
if last.ds then put ' run;';
|
||||||
|
if last.lib then put 'quit;';
|
||||||
|
run;
|
||||||
|
|
||||||
|
%inc &fref/source2;
|
||||||
|
|
||||||
|
%if &errds=0 %then %do;
|
||||||
|
proc sql;
|
||||||
|
drop table &outds;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mp_applyformats;
|
||||||
119
base/mp_assertscope.sas
Normal file
119
base/mp_assertscope.sas
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Used to capture scope leakage of macro variables
|
||||||
|
@details A common 'difficult to detect' bug in macros is where a nested
|
||||||
|
macro over-writes variables in a higher level macro.
|
||||||
|
|
||||||
|
This assertion takes a snapshot of the macro variables before and after
|
||||||
|
a macro invocation. This makes it easy to detect whether any macro
|
||||||
|
variables were modified or changed.
|
||||||
|
|
||||||
|
Currently, the macro only checks for global scope variables. In the future
|
||||||
|
it may be extended to work at multiple levels of nesting.
|
||||||
|
|
||||||
|
If you would like this feature, feel free to contribute / raise an issue /
|
||||||
|
engage the SASjs team directly.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
|
||||||
|
%let oops=I did it again;
|
||||||
|
|
||||||
|
%mp_assertscope(COMPARE,
|
||||||
|
desc=Checking macro variables against previous snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
@param [in] action (SNAPSHOT) The action to take. Valid values:
|
||||||
|
@li SNAPSHOT - take a copy of the current macro variables
|
||||||
|
@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 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
|
||||||
|
results. If it does not exist, it will be created, with the following format:
|
||||||
|
|TEST_DESCRIPTION:$256|TEST_RESULT:$4|TEST_COMMENTS:$256|
|
||||||
|
|---|---|---|
|
||||||
|
|User Provided description|PASS|No out of scope variables created or modified|
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertcols.sas
|
||||||
|
@li mp_assertcolvals.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
@li mp_assertscope.test.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_assertscope(action,
|
||||||
|
desc=Testing Scope Leakage,
|
||||||
|
scope=GLOBAL,
|
||||||
|
scopeds=work.mp_assertscope,
|
||||||
|
outds=work.test_results
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local ds test_result test_comments del add mod;
|
||||||
|
|
||||||
|
/* get current variables */
|
||||||
|
%if &action=SNAPSHOT %then %do;
|
||||||
|
proc sql;
|
||||||
|
create table &scopeds as
|
||||||
|
select name,offset,value
|
||||||
|
from dictionary.macros
|
||||||
|
where scope="&scope"
|
||||||
|
order by name,offset;
|
||||||
|
%end;
|
||||||
|
%else %if &action=COMPARE %then %do;
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table _data_ as
|
||||||
|
select name,offset,value
|
||||||
|
from dictionary.macros
|
||||||
|
where scope="&scope"
|
||||||
|
order by name,offset;
|
||||||
|
|
||||||
|
%let ds=&syslast;
|
||||||
|
|
||||||
|
proc compare base=&scopeds compare=&ds;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%if &sysinfo=0 %then %do;
|
||||||
|
%let test_result=PASS;
|
||||||
|
%let test_comments=&scope Variables Unmodified;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
proc sql noprint undo_policy=none;
|
||||||
|
select distinct name into: del separated by ' ' from &scopeds
|
||||||
|
where name not in (select name from &ds);
|
||||||
|
select distinct name into: add separated by ' ' from &ds
|
||||||
|
where name not in (select name from &scopeds);
|
||||||
|
select distinct a.name into: mod separated by ' '
|
||||||
|
from &scopeds a
|
||||||
|
inner join &ds b
|
||||||
|
on a.name=b.name
|
||||||
|
and a.offset=b.offset
|
||||||
|
where a.value ne b.value;
|
||||||
|
%let test_result=FAIL;
|
||||||
|
%let test_comments=%str(Mod:(&mod) Add:(&add) Del:(&del));
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
data ;
|
||||||
|
length test_description $256 test_result $4 test_comments $256;
|
||||||
|
test_description=symget('desc');
|
||||||
|
test_comments=symget('test_comments');
|
||||||
|
test_result=symget('test_result');
|
||||||
|
run;
|
||||||
|
|
||||||
|
%let ds=&syslast;
|
||||||
|
proc append base=&outds data=&ds;
|
||||||
|
run;
|
||||||
|
proc sql;
|
||||||
|
drop table &ds;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mp_assertscope;
|
||||||
92
base/mp_coretable.sas
Normal file
92
base/mp_coretable.sas
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Create the permanent Core tables
|
||||||
|
@details Several macros in the [core](https://github.com/sasjs/core) library
|
||||||
|
make use of permanent tables. To avoid duplication in definitions, this
|
||||||
|
macro provides a central location for managing the corresponding DDL.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
%mp_coretable(LOCKTABLE,libds=work.locktable)
|
||||||
|
|
||||||
|
@param [in] table_ref The type of table to create. Example values:
|
||||||
|
@li FILTER_DETAIL - For storing detailed filter values. Used by
|
||||||
|
mp_filterstore.sas.
|
||||||
|
@li FILTER_SUMMARY - For storing summary filter values. Used by
|
||||||
|
mp_filterstore.sas.
|
||||||
|
@li LOCKANYTABLE - For "locking" tables prior to multipass loads. Used by
|
||||||
|
mp_lockanytable.sas
|
||||||
|
@li MAXKEYTABLE - For storing the maximum retained key information. Used
|
||||||
|
by mp_retainedkey.sas
|
||||||
|
@param [in] libds= (0) The library.dataset reference used to create the table.
|
||||||
|
If not provided, then the DDL is simply printed to the log.
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_filterstore.sas
|
||||||
|
@li mp_lockanytable.sas
|
||||||
|
@li mp_retainedkey.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_coretable(table_ref,libds=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
%local outds ;
|
||||||
|
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
|
||||||
|
proc sql;
|
||||||
|
%if &table_ref=LOCKTABLE %then %do;
|
||||||
|
create table &outds(
|
||||||
|
lock_lib char(8),
|
||||||
|
lock_ds char(32),
|
||||||
|
lock_status_cd char(10) not null,
|
||||||
|
lock_user_nm char(100) not null ,
|
||||||
|
lock_ref char(200),
|
||||||
|
lock_pid char(10),
|
||||||
|
lock_start_dttm num format=E8601DT26.6,
|
||||||
|
lock_end_dttm num format=E8601DT26.6,
|
||||||
|
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds));
|
||||||
|
%end;
|
||||||
|
%else %if &table_ref=FILTER_SUMMARY %then %do;
|
||||||
|
create table &outds(
|
||||||
|
filter_rk num not null,
|
||||||
|
filter_hash char(32) not null,
|
||||||
|
filter_table char(41) not null,
|
||||||
|
processed_dttm num not null format=E8601DT26.6,
|
||||||
|
constraint pk_mpe_filteranytable
|
||||||
|
primary key(filter_rk));
|
||||||
|
%end;
|
||||||
|
%else %if &table_ref=FILTER_DETAIL %then %do;
|
||||||
|
create table &outds(
|
||||||
|
filter_hash char(32) not null,
|
||||||
|
filter_line num not null,
|
||||||
|
group_logic char(3) not null,
|
||||||
|
subgroup_logic char(3) not null,
|
||||||
|
subgroup_id num not null,
|
||||||
|
variable_nm varchar(32) not null,
|
||||||
|
operator_nm varchar(12) not null,
|
||||||
|
raw_value varchar(4000) not null,
|
||||||
|
processed_dttm num not null format=E8601DT26.6,
|
||||||
|
constraint pk_mpe_filteranytable
|
||||||
|
primary key(filter_hash,filter_line));
|
||||||
|
%end;
|
||||||
|
%else %if &table_ref=MAXKEYTABLE %then %do;
|
||||||
|
create table &outds(
|
||||||
|
keytable varchar(41) label='Base table in libref.dataset format',
|
||||||
|
keycolumn char(32) format=$32.
|
||||||
|
label='The Retained key field containing the key values.',
|
||||||
|
max_key num label=
|
||||||
|
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||||
|
processed_dttm num format=E8601DT26.6
|
||||||
|
label='Datetime this value was last updated',
|
||||||
|
constraint pk_mpe_maxkeyvalues
|
||||||
|
primary key(keytable));
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
%if &libds=0 %then %do;
|
||||||
|
describe table &syslast;
|
||||||
|
drop table &syslast;
|
||||||
|
%end;
|
||||||
|
%mend mp_coretable;
|
||||||
@@ -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,29 @@ 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.
|
||||||
|
*/
|
||||||
|
%if %mf_existds(&outds) %then %do;
|
||||||
|
%local basevars appvars newvars;
|
||||||
|
%let basevars=%mf_getvarlist(&outds);
|
||||||
|
%let appvars=%mf_getvarlist(&out_ds);
|
||||||
|
%let newvars=%length(%mf_wordsinstr1butnotstr2(Str1=&appvars,Str2=&basevars));
|
||||||
|
%if &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;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
proc append base=&outds data=&out_ds;
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
/* recursive call */
|
/* recursive call */
|
||||||
%if &maxdepth>&level or &maxdepth=MAX %then %do;
|
%if &maxdepth>&level or &maxdepth=MAX %then %do;
|
||||||
|
|||||||
@@ -139,8 +139,9 @@ create table datalines1 as
|
|||||||
/**
|
/**
|
||||||
Due to long decimals cannot use best. format
|
Due to long decimals cannot use best. format
|
||||||
So - use bestd. format and then use character functions to strip trailing
|
So - use bestd. format and then use character functions to strip trailing
|
||||||
zeros, if NOT an integer!!
|
zeros, if NOT an integer or missing!! Cannot use int() as it upsets
|
||||||
resolved code = ifc(int(VARIABLE)=VARIABLE
|
note2err when there are missings.
|
||||||
|
resolved code = ifc( mod(coalesce(VARIABLE,0),1)=0
|
||||||
,put(VARIABLE,best32.)
|
,put(VARIABLE,best32.)
|
||||||
,substrn(put(VARIABLE,bestd32.),1
|
,substrn(put(VARIABLE,bestd32.),1
|
||||||
,findc(put(VARIABLE,bestd32.),'0','TBK')));
|
,findc(put(VARIABLE,bestd32.),'0','TBK')));
|
||||||
@@ -151,7 +152,7 @@ data datalines_2;
|
|||||||
set datalines1 (where=(upcase(name) not in
|
set datalines1 (where=(upcase(name) not in
|
||||||
('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM')));
|
('PROCESSED_DTTM','VALID_FROM_DTTM','VALID_TO_DTTM')));
|
||||||
if type='num' then dataline=
|
if type='num' then dataline=
|
||||||
cats('ifc(int(',name,')=',name,'
|
cats('ifc(mod(coalesce(',name,',0),1)=0
|
||||||
,put(',name,',best32.-l)
|
,put(',name,',best32.-l)
|
||||||
,substrn(put(',name,',bestd32.-l),1
|
,substrn(put(',name,',bestd32.-l),1
|
||||||
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ data _null_;
|
|||||||
dsid=open("&ds.","i");
|
dsid=open("&ds.","i");
|
||||||
num=attrn(dsid,"nvars");
|
num=attrn(dsid,"nvars");
|
||||||
do i=1 to num;
|
do i=1 to num;
|
||||||
header = trim(left(coalescec(varlabel(dsid,i),varname(dsid,i))));
|
header = cats(coalescec(varlabel(dsid,i),varname(dsid,i)));
|
||||||
put header @;
|
put header @;
|
||||||
end;
|
end;
|
||||||
rc=close(dsid);
|
rc=close(dsid);
|
||||||
|
|||||||
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;
|
||||||
@@ -109,7 +109,7 @@ data &outds;
|
|||||||
output;
|
output;
|
||||||
end;
|
end;
|
||||||
if mod(SUBGROUP_ID,1) ne 0 then do;
|
if mod(SUBGROUP_ID,1) ne 0 then do;
|
||||||
REASON_CD='SUBGROUP_ID should be integer, not '!!left(subgroup_id);
|
REASON_CD='SUBGROUP_ID should be integer, not '!!cats(subgroup_id);
|
||||||
putlog REASON_CD= SUBGROUP_ID=;
|
putlog REASON_CD= SUBGROUP_ID=;
|
||||||
call symputx('reason_cd',reason_cd,'l');
|
call symputx('reason_cd',reason_cd,'l');
|
||||||
call symputx('nobs',_n_,'l');
|
call symputx('nobs',_n_,'l');
|
||||||
@@ -127,7 +127,7 @@ data &outds;
|
|||||||
if OPERATOR_NM not in
|
if OPERATOR_NM not in
|
||||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
|
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
|
||||||
then do;
|
then do;
|
||||||
REASON_CD='Invalid OPERATOR_NM: '!!left(OPERATOR_NM);
|
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||||
putlog REASON_CD= OPERATOR_NM=;
|
putlog REASON_CD= OPERATOR_NM=;
|
||||||
call symputx('reason_cd',reason_cd,'l');
|
call symputx('reason_cd',reason_cd,'l');
|
||||||
call symputx('nobs',_n_,'l');
|
call symputx('nobs',_n_,'l');
|
||||||
|
|||||||
230
base/mp_filterstore.sas
Normal file
230
base/mp_filterstore.sas
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Checks & Stores an input filter table and returns the Filter Key
|
||||||
|
@details Used to generate a FILTER_RK from an input query dataset. This
|
||||||
|
process requires several permanent tables (names are configurable). The
|
||||||
|
benefit of storing query values at backend is to enable stored 'views' of
|
||||||
|
filtered tables at frontend (ie, when building [SAS-Powered Apps](
|
||||||
|
https://sasapps.io)). This macro is also used in [Data Controller for SAS](
|
||||||
|
https://datacontroller.io).
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] libds= The target dataset to be filtered (lib should be assigned)
|
||||||
|
@param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
|
||||||
|
be validated. Has the following format:
|
||||||
|
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
|AND|AND|1|SOME_BESTNUM|>|1|
|
||||||
|
|AND|AND|1|SOME_TIME|=|77333|
|
||||||
|
@param [in] filter_summary= (PERM.FILTER_SUMMARY) Permanent table containing
|
||||||
|
summary filter values. The definition is available by running
|
||||||
|
mp_coretable.sas as follows: `mp_coretable(FILTER_SUMMARY)`. Example
|
||||||
|
values:
|
||||||
|
|FILTER_RK:best.|FILTER_HASH:$32.|FILTER_TABLE:$41.|PROCESSED_DTTM:datetime19.|
|
||||||
|
|---|---|---|---|
|
||||||
|
|`1 `|`540E96F566D194AB58DD4C413C99C9DB `|`VIYA6014.MPE_TABLES `|`1956084246 `|
|
||||||
|
|`2 `|`87737DB9EEE2650F5C89956CEAD0A14F `|`VIYA6014.MPE_X_TEST `|`1956084452.1`|
|
||||||
|
|`3 `|`8048BD908DBBD83D013560734E90D394 `|`VIYA6014.MPE_TABLES `|`1956093620.6`|
|
||||||
|
@param [in] filter_detail= (PERM.FILTER_DETAIL) Permanent table containing
|
||||||
|
detailed (raw) filter values. The definition is available by running
|
||||||
|
mp_coretable.sas as follows: `mp_coretable(FILTER_DETAIL)`. Example
|
||||||
|
values:
|
||||||
|
|FILTER_HASH:$32.|FILTER_LINE:best.|GROUP_LOGIC:$3.|SUBGROUP_LOGIC:$3.|SUBGROUP_ID:best.|VARIABLE_NM:$32.|OPERATOR_NM:$12.|RAW_VALUE:$4000.|PROCESSED_DTTM:datetime19.|
|
||||||
|
|---|---|---|---|---|---|---|---|---|
|
||||||
|
|`540E96F566D194AB58DD4C413C99C9DB `|`1 `|`AND `|`AND `|`1 `|`LIBREF `|`CONTAINS `|`DC`|`1956084245.8 `|
|
||||||
|
|`540E96F566D194AB58DD4C413C99C9DB `|`2 `|`AND `|`OR `|`2 `|`DSN `|`= `|` MPE_LOCK_ANYTABLE `|`1956084245.8 `|
|
||||||
|
|`87737DB9EEE2650F5C89956CEAD0A14F `|`1 `|`AND `|`AND `|`1 `|`PRIMARY_KEY_FIELD `|`IN `|`(1,2,3) `|`1956084451.9 `|
|
||||||
|
@param [in] lock_table= (PERM.LOCK_TABLE) Permanent locking table. Used to
|
||||||
|
manage concurrent access. The definition is available by running
|
||||||
|
mp_coretable.sas as follows: `mp_coretable(LOCKTABLE)`.
|
||||||
|
@param [in] maxkeytable= (0) Optional permanent reference table used for
|
||||||
|
retained key tracking. Described in mp_retainedkey.sas.
|
||||||
|
@param [in] mdebug= set to 1 to enable DEBUG messages
|
||||||
|
@param [out] outresult= The result table with the FILTER_RK
|
||||||
|
@param [out] outquery= The original query, taken as extract after table load
|
||||||
|
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_getvalue.sas
|
||||||
|
@li mf_islibds.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_filtercheck.sas
|
||||||
|
@li mp_hashdataset.sas
|
||||||
|
@li mp_retainedkey.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_filtercheck.sas
|
||||||
|
@li mp_filtergenerate.sas
|
||||||
|
@li mp_filtervalidate.sas
|
||||||
|
@li mp_filterstore.test.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author [Allan Bowe](https://www.linkedin.com/in/allanbowe)
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_filterstore(libds=,
|
||||||
|
queryds=work.filterquery,
|
||||||
|
filter_summary=PERM.FILTER_SUMMARY,
|
||||||
|
filter_detail=PERM.FILTER_DETAIL,
|
||||||
|
lock_table=PERM.LOCK_TABLE,
|
||||||
|
maxkeytable=PERM.MAXKEYTABLE,
|
||||||
|
outresult=work.result,
|
||||||
|
outquery=work.query,
|
||||||
|
mdebug=1
|
||||||
|
);
|
||||||
|
%put &sysmacroname entry vars:;
|
||||||
|
%put _local_;
|
||||||
|
|
||||||
|
%local ds1 ds2 ds3 ds4 filter_hash;
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(syscc=&syscc on macro entry)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue= (%mf_islibds(&filter_summary)=0)
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(Invalid filter_summary value: &filter_summary)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue= (%mf_islibds(&filter_detail)=0)
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(Invalid filter_detail value: &filter_detail)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue= (%mf_islibds(&lock_table)=0)
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(Invalid lock_table value: &lock_table)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* validate query */
|
||||||
|
%mp_filtercheck(&queryds,targetds=&libds,abort=YES)
|
||||||
|
|
||||||
|
/* hash the result */
|
||||||
|
%let ds1=%mf_getuniquename(prefix=hashds);
|
||||||
|
%mp_hashdataset(&queryds,outds=&ds1,salt=&libds)
|
||||||
|
%let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
|
||||||
|
%if &mdebug=1 %then %do;
|
||||||
|
data _null_;
|
||||||
|
putlog "filter_hash=&filter_hash";
|
||||||
|
set &ds1;
|
||||||
|
putlog (_all_)(=);
|
||||||
|
run;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* check if data already exists for this hash */
|
||||||
|
data &outresult;
|
||||||
|
set &filter_summary;
|
||||||
|
where filter_hash="&filter_hash";
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(syscc=&syscc after hash check)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue= ("&filter_hash "=" ")
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(problem with filter_hash generation)
|
||||||
|
)
|
||||||
|
|
||||||
|
%if %mf_nobs(&outresult)=0 %then %do;
|
||||||
|
|
||||||
|
/* first update summary table */
|
||||||
|
%let ds3=%mf_getuniquename(prefix=filtersum);
|
||||||
|
data work.&ds3;
|
||||||
|
if 0 then set &filter_summary;
|
||||||
|
filter_table=symget('libds');
|
||||||
|
filter_hash="&filter_hash";
|
||||||
|
PROCESSED_DTTM=%sysfunc(datetime());
|
||||||
|
output;
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_lockanytable(LOCK,
|
||||||
|
lib=%scan(&filter_summary,1,.)
|
||||||
|
,ds=%scan(&filter_summary,2,.)
|
||||||
|
,ref=MP_FILTERSTORE summary update - &filter_hash
|
||||||
|
,ctl_ds=&lock_table
|
||||||
|
)
|
||||||
|
|
||||||
|
%let ds4=%mf_getuniquename(prefix=filtersumappend);
|
||||||
|
%mp_retainedkey(
|
||||||
|
base_lib=%scan(&filter_summary,1,.)
|
||||||
|
,base_dsn=%scan(&filter_summary,2,.)
|
||||||
|
,append_lib=work
|
||||||
|
,append_dsn=&ds3
|
||||||
|
,retained_key=filter_rk
|
||||||
|
,business_key=filter_hash
|
||||||
|
,maxkeytable=&maxkeytable
|
||||||
|
,locktable=&lock_table
|
||||||
|
,outds=work.&ds4
|
||||||
|
)
|
||||||
|
proc append base=&filter_summary data=&ds4;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_lockanytable(UNLOCK,
|
||||||
|
lib=%scan(&filter_summary,1,.)
|
||||||
|
,ds=%scan(&filter_summary,2,.)
|
||||||
|
,ref=MP_FILTERSTORE summary update - &filter_hash
|
||||||
|
,ctl_ds=&lock_table
|
||||||
|
)
|
||||||
|
|
||||||
|
%if &syscc ne 0 %then %do;
|
||||||
|
data _null_;
|
||||||
|
set &ds4;
|
||||||
|
putlog (_all_)(=);
|
||||||
|
run;
|
||||||
|
%goto err;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
data &outresult;
|
||||||
|
set &filter_summary;
|
||||||
|
where filter_hash="&filter_hash";
|
||||||
|
run;
|
||||||
|
|
||||||
|
/* Next, update detail table */
|
||||||
|
%let ds2=%mf_getuniquename(prefix=filterdetail);
|
||||||
|
data &ds2;
|
||||||
|
if 0 then set &filter_detail;
|
||||||
|
set &queryds;
|
||||||
|
format filter_hash $hex32. filter_line 8.;
|
||||||
|
filter_hash="&filter_hash";
|
||||||
|
filter_line=_n_;
|
||||||
|
PROCESSED_DTTM=%sysfunc(datetime());
|
||||||
|
run;
|
||||||
|
%mp_lockanytable(LOCK,
|
||||||
|
lib=%scan(&filter_detail,1,.)
|
||||||
|
,ds=%scan(&filter_detail,2,.)
|
||||||
|
,ref=MP_FILTERSTORE update - &filter_hash
|
||||||
|
,ctl_ds=&lock_table
|
||||||
|
)
|
||||||
|
proc append base=&filter_detail data=&ds2;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_lockanytable(UNLOCK,
|
||||||
|
lib=%scan(&filter_detail,1,.)
|
||||||
|
,ds=%scan(&filter_detail,2,.)
|
||||||
|
,ref=MP_FILTERSTORE detail update &filter_hash
|
||||||
|
,ctl_ds=&lock_table
|
||||||
|
)
|
||||||
|
|
||||||
|
%if &syscc ne 0 %then %do;
|
||||||
|
data _null_;
|
||||||
|
set &ds2;
|
||||||
|
putlog (_all_)(=);
|
||||||
|
run;
|
||||||
|
%goto err;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%end;
|
||||||
|
|
||||||
|
proc sort data=&filter_detail(where=(filter_hash="&filter_hash")) out=&outquery;
|
||||||
|
by filter_line;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%err:
|
||||||
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
|
,mac=mp_filterstore
|
||||||
|
,msg=%str(syscc=&syscc on macro exit)
|
||||||
|
)
|
||||||
|
|
||||||
|
%mend mp_filterstore;
|
||||||
@@ -14,11 +14,11 @@
|
|||||||
|
|
||||||
@param ds The dataset from which to obtain column metadata
|
@param ds The dataset from which to obtain column metadata
|
||||||
@param outds= (work.cols) The output dataset to create. Sample data:
|
@param outds= (work.cols) The output dataset to create. Sample data:
|
||||||
|NAME $|LENGTH 8|VARNUM 8|LABEL $|FORMAT $49|TYPE $1 |DDTYPE $|
|
|NAME:$32.|LENGTH:best.|VARNUM:best.|LABEL:$256.|FMTNAME:$32.|FORMAT:$49.|TYPE:$1.|DDTYPE:$9.|
|
||||||
|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|
|
||||||
|AIR|8|2|international airline travel (thousands)|8.|N|NUMERIC|
|
|`AIR `|`8 `|`2 `|`international airline travel (thousands) `|` `|`8. `|`N `|`NUMERIC `|
|
||||||
|DATE|8|1|DATE|MONYY.|N|DATE|
|
|`DATE `|`8 `|`1 `|`DATE `|`MONYY `|`MONYY. `|`N `|`DATE `|
|
||||||
|REGION|3|3|REGION|$3.|C|CHARACTER|
|
|`REGION `|`3 `|`3 `|`REGION `|` `|`$3. `|`C `|`CHARACTER `|
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
<h4> Related Macros </h4>
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@@ -30,26 +30,27 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_getcols(ds, outds=work.cols);
|
%macro mp_getcols(ds, outds=work.cols);
|
||||||
|
%local dropds;
|
||||||
proc contents noprint data=&ds
|
proc contents noprint data=&ds
|
||||||
out=_data_ (keep=name type length label varnum format:);
|
out=_data_ (keep=name type length label varnum format:);
|
||||||
run;
|
run;
|
||||||
data &outds(keep=name type length varnum format label ddtype);
|
%let dropds=&syslast;
|
||||||
set &syslast(rename=(format=format2 type=type2));
|
data &outds(keep=name type length varnum format label ddtype fmtname);
|
||||||
|
set &dropds(rename=(format=fmtname type=type2));
|
||||||
name=upcase(name);
|
name=upcase(name);
|
||||||
if type2=2 then do;
|
if type2=2 then do;
|
||||||
length format $49.;
|
length format $49.;
|
||||||
if format2='' then format=cats('$',length,'.');
|
if fmtname='' then format=cats('$',length,'.');
|
||||||
else if formatl=0 then format=cats(format2,'.');
|
else if formatl=0 then format=cats(fmtname,'.');
|
||||||
else format=cats(format2,formatl,'.');
|
else format=cats(fmtname,formatl,'.');
|
||||||
type='C';
|
type='C';
|
||||||
ddtype='CHARACTER';
|
ddtype='CHARACTER';
|
||||||
end;
|
end;
|
||||||
else do;
|
else do;
|
||||||
if format2='' then format=cats(length,'.');
|
if fmtname='' then format=cats(length,'.');
|
||||||
else if formatl=0 then format=cats(format2,'.');
|
else if formatl=0 then format=cats(fmtname,'.');
|
||||||
else if formatd=0 then format=cats(format2,formatl,'.');
|
else if formatd=0 then format=cats(fmtname,formatl,'.');
|
||||||
else format=cats(format2,formatl,'.',formatd);
|
else format=cats(fmtname,formatl,'.',formatd);
|
||||||
type='N';
|
type='N';
|
||||||
if format=:'DATETIME' or format=:'E8601DT' then ddtype='DATETIME';
|
if format=:'DATETIME' or format=:'E8601DT' then ddtype='DATETIME';
|
||||||
else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
|
else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
|
||||||
@@ -61,5 +62,6 @@ data &outds(keep=name type length varnum format label ddtype);
|
|||||||
end;
|
end;
|
||||||
if label='' then label=name;
|
if label='' then label=name;
|
||||||
run;
|
run;
|
||||||
|
proc sql;
|
||||||
|
drop table &dropds;
|
||||||
%mend mp_getcols;
|
%mend mp_getcols;
|
||||||
@@ -158,7 +158,7 @@ run;
|
|||||||
lab=" label="!!cats("'",tranwrd(label,"'","''"),"'");
|
lab=" label="!!cats("'",tranwrd(label,"'","''"),"'");
|
||||||
if notnull='yes' then notnul=' not null';
|
if notnull='yes' then notnul=' not null';
|
||||||
if type='char' then typ=cats('char(',length,')');
|
if type='char' then typ=cats('char(',length,')');
|
||||||
else if length ne 8 then typ='num length='!!left(length);
|
else if length ne 8 then typ='num length='!!cats(length);
|
||||||
else typ='num';
|
else typ='num';
|
||||||
put name typ fmt notnul lab;
|
put name typ fmt notnul lab;
|
||||||
run;
|
run;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ https://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#
|
|||||||
|
|
||||||
|
|
||||||
<h4> Related Macros </h4>
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_applyformats.sas
|
||||||
@li mp_getformats.test.sas
|
@li mp_getformats.test.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
|
|||||||
@@ -1,28 +1,46 @@
|
|||||||
/**
|
/**
|
||||||
@file mp_getmaxvarlengths.sas
|
@file
|
||||||
@brief Scans a dataset to find the max length of the variable values
|
@brief Scans a dataset to find the max length of the variable values
|
||||||
@details
|
@details
|
||||||
This macro will scan a base dataset and produce an output dataset with two
|
This macro will scan a base dataset and produce an output dataset with two
|
||||||
columns:
|
columns:
|
||||||
|
|
||||||
- NAME Name of the base dataset column
|
- 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
|
Character fields are often allocated very large widths (eg 32000) of which the
|
||||||
maximum value is likely to be much narrower. This macro was designed to
|
maximum value is likely to be much narrower. Identifying such cases can be
|
||||||
enable a HTML table to be appropriately sized however this could be used as
|
helpful in the following scenarios:
|
||||||
part of a data audit to ensure we aren't over-sizing our tables in relation to
|
|
||||||
the data therein.
|
@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:
|
Usage:
|
||||||
|
|
||||||
%mp_getmaxvarlengths(sashelp.class,outds=work.myds)
|
%mp_getmaxvarlengths(sashelp.class,outds=work.myds)
|
||||||
|
|
||||||
@param libds Two part dataset (or view) reference.
|
@param [in] libds Two part dataset (or view) reference.
|
||||||
@param outds= The output dataset to create
|
@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>
|
<h4> SAS Macros </h4>
|
||||||
|
@li mcf_length.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
@li mf_getvarlist.sas
|
@li mf_getvarlist.sas
|
||||||
@li mf_getvartype.sas
|
@li mf_getvartype.sas
|
||||||
@li mf_getvarformat.sas
|
@li mf_getvarformat.sas
|
||||||
@@ -30,20 +48,32 @@
|
|||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_ds2squeeze.sas
|
||||||
|
@li mp_getmaxvarlengths.test.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_getmaxvarlengths(
|
%macro mp_getmaxvarlengths(
|
||||||
libds /* libref.dataset to analyse */
|
libds
|
||||||
,outds=work.mp_getmaxvarlengths /* name of output dataset to create */
|
,num2char=NO
|
||||||
|
,outds=work.mp_getmaxvarlengths
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local vars x var fmt;
|
%local vars prefix x var fmt;
|
||||||
%let vars=%mf_getvarlist(libds=&libds);
|
%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;
|
proc sql;
|
||||||
create table &outds (rename=(
|
create table &outds (rename=(
|
||||||
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
%do x=1 %to %sysfunc(countw(&vars,%str( )));
|
||||||
________&x=%scan(&vars,&x)
|
&prefix.&x=%scan(&vars,&x)
|
||||||
%end;
|
%end;
|
||||||
))
|
))
|
||||||
as select
|
as select
|
||||||
@@ -51,18 +81,21 @@ create table &outds (rename=(
|
|||||||
%let var=%scan(&vars,&x);
|
%let var=%scan(&vars,&x);
|
||||||
%if &x>1 %then ,;
|
%if &x>1 %then ,;
|
||||||
%if %mf_getvartype(&libds,&var)=C %then %do;
|
%if %mf_getvartype(&libds,&var)=C %then %do;
|
||||||
max(length(&var)) as ________&x
|
max(lengthn(&var)) as &prefix.&x
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %if &num2char=YES %then %do;
|
||||||
%let fmt=%mf_getvarformat(&libds,&var);
|
%let fmt=%mf_getvarformat(&libds,&var);
|
||||||
%put fmt=&fmt;
|
%put fmt=&fmt;
|
||||||
%if %str(&fmt)=%str() %then %do;
|
%if %str(&fmt)=%str() %then %do;
|
||||||
max(length(cats(&var))) as ________&x
|
max(lengthn(cats(&var))) as &prefix.&x
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
max(length(put(&var,&fmt))) as ________&x
|
max(lengthn(put(&var,&fmt))) as &prefix.&x
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
|
%else %do;
|
||||||
|
max(mcf_length(&var)) as &prefix.&x
|
||||||
|
%end;
|
||||||
%end;
|
%end;
|
||||||
from &libds;
|
from &libds;
|
||||||
|
|
||||||
|
|||||||
@@ -33,36 +33,41 @@
|
|||||||
%macro mp_init(prefix=SASJS
|
%macro mp_init(prefix=SASJS
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%global
|
%if %symexist(SASJS_PREFIX) %then %return; /* only run once */
|
||||||
&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 */
|
|
||||||
|
|
||||||
data _null_;
|
%global
|
||||||
dttm=datetime();
|
SASJS_PREFIX /* the ONLY hard-coded global macro variable in SASjs */
|
||||||
call symputx("&prefix._init_num",dttm,'g');
|
&prefix._INIT_NUM /* initialisation time as numeric */
|
||||||
call symputx("&prefix._init_dttm",put(dttm,E8601DT26.6),'g');
|
&prefix._INIT_DTTM /* initialisation time in E8601DT26.6 format */
|
||||||
call symputx("&prefix.work",pathname('WORK'),'g');
|
&prefix.WORK /* avoid typing %sysfunc(pathname(work)) every time */
|
||||||
run;
|
;
|
||||||
|
|
||||||
options
|
%let sasjs_prefix=&prefix;
|
||||||
noautocorrect /* disallow misspelled procedure names */
|
|
||||||
compress=CHAR /* default is none so ensure we have something! */
|
data _null_;
|
||||||
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
dttm=datetime();
|
||||||
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
call symputx("&sasjs_prefix._init_num",dttm,'g');
|
||||||
fmterr /* ensure err when a format cannot be found */
|
call symputx("&sasjs_prefix._init_dttm",put(dttm,E8601DT26.6),'g');
|
||||||
mergenoby=%str(ERR)OR /* throw err when a merge has no BY variables */
|
call symputx("&sasjs_prefix.work",pathname('WORK'),'g');
|
||||||
missing=. /* changing this can cause hard to detect errs */
|
run;
|
||||||
noquotelenmax /* avoid warnings for long strings */
|
|
||||||
noreplace /* avoid overwriting permanent datasets */
|
options
|
||||||
ps=max /* reduce log size slightly */
|
noautocorrect /* disallow misspelled procedure names */
|
||||||
ls=max /* reduce log even more and avoid word truncation */
|
compress=CHAR /* default is none so ensure we have something! */
|
||||||
validmemname=COMPATIBLE /* avoid special characters etc in table names */
|
datastmtchk=ALLKEYWORDS /* protection from overwriting input datasets */
|
||||||
validvarname=V7 /* avoid special characters etc in variable names */
|
dsoptions=note2err /* undocumented - convert bad NOTEs to ERRs */
|
||||||
varinitchk=%str(ERR)OR /* avoid data mistakes from variable name typos */
|
%str(err)orcheck=STRICT /* catch errs in libname/filename statements */
|
||||||
varlenchk=%str(ERR)OR /* fail hard if truncation (data loss) can result */
|
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;
|
%mend mp_init;
|
||||||
@@ -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
|
||||||
@@ -31,22 +32,24 @@
|
|||||||
[sasjs adapter](https://github.com/sasjs/adapter).
|
[sasjs adapter](https://github.com/sasjs/adapter).
|
||||||
For more information see https://sasjs.io
|
For more information see https://sasjs.io
|
||||||
|
|
||||||
@param action Valid values:
|
@param [in] action Valid values:
|
||||||
@li OPEN - opens the JSON
|
@li OPEN - opens the JSON
|
||||||
@li OBJ - sends a table with each row as an object
|
@li OBJ - sends a table with each row as an object
|
||||||
@li ARR - sends a table with each row in an array
|
@li ARR - sends a table with each row in an array
|
||||||
@li CLOSE - closes the JSON
|
@li CLOSE - closes the JSON
|
||||||
|
@param [in] ds The dataset to send. Must be a work table.
|
||||||
@param ds the dataset to send. Must be a work table.
|
@param [out] jref= (_webout) The fileref to which to send the JSON
|
||||||
@param jref= the fileref to which to send the JSON
|
@param [out] dslabel= The name to give the table in the exported JSON
|
||||||
@param dslabel= the name to give the table in the exported JSON
|
@param [in] fmt= (Y) Whether to keep (Y) or strip (N) formats from the table
|
||||||
@param fmt= Whether to keep or strip formats from the table
|
@param [in] engine= (DATASTEP) Which engine to use to send the JSON. Options:
|
||||||
@param engine= Which engine to use to send the JSON, valid options are:
|
|
||||||
@li PROCJSON (default)
|
@li PROCJSON (default)
|
||||||
@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 dbg= DEPRECATED - was used to conditionally add PRETTY to
|
(eg `null`) or as STRING values (eg `".a"` or `".b"`)
|
||||||
proc json but this can cause line truncation in large files.
|
@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
|
||||||
@@ -57,129 +60,140 @@
|
|||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y,engine=DATASTEP,dbg=0
|
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
|
||||||
|
,engine=DATASTEP
|
||||||
|
,missing=NULL
|
||||||
|
,showmeta=NO
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
%put 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' ;
|
||||||
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
|
put '{"PROCESSED_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '"';
|
||||||
run;
|
run;
|
||||||
%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;
|
||||||
data;run;%let tempds=&syslast;
|
%if &missing=STRING %then %do;
|
||||||
proc sql;drop table &tempds;
|
%put &sysmacroname: Special Missings not supported in proc json.;
|
||||||
|
%put &sysmacroname: Switching to DATASTEP engine;
|
||||||
|
%goto datastep;
|
||||||
|
%end;
|
||||||
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 */
|
||||||
proc json out=&jref pretty
|
proc json out=&jref pretty
|
||||||
%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;
|
||||||
%local cols i tempds;
|
%datastep:
|
||||||
%let cols=0;
|
%if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1
|
||||||
%if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do;
|
%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 ._ - .z = null
|
value bart (default=40)
|
||||||
|
%if &missing=NULL %then %do;
|
||||||
|
._ - .z = null
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
._ = [quote()]
|
||||||
|
. = null
|
||||||
|
.a - .z = [quote()]
|
||||||
|
%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,
|
||||||
@@ -189,44 +203,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;
|
||||||
|
|||||||
@@ -5,19 +5,18 @@
|
|||||||
Only useful if every update uses the macro! Used heavily within
|
Only useful if every update uses the macro! Used heavily within
|
||||||
[Data Controller for SAS](https://datacontroller.io).
|
[Data Controller for SAS](https://datacontroller.io).
|
||||||
|
|
||||||
The underlying table is structured as per the MAKETABLE action.
|
|
||||||
|
|
||||||
@param [in] action The action to be performed. Valid values:
|
@param [in] action The action to be performed. Valid values:
|
||||||
@li LOCK - Sets the lock flag, also confirms if a SAS lock is available
|
@li LOCK - Sets the lock flag, also confirms if a SAS lock is available
|
||||||
@li UNLOCK - Unlocks the table
|
@li UNLOCK - Unlocks the table
|
||||||
@li MAKETABLE - creates the control table (ctl_ds)
|
|
||||||
@param [in] lib= (WORK) The libref of the table to lock. Should already be
|
@param [in] lib= (WORK) The libref of the table to lock. Should already be
|
||||||
assigned.
|
assigned.
|
||||||
@param [in] ds= The dataset to lock
|
@param [in] ds= The dataset to lock
|
||||||
@param [in] ref= A meaningful reference to enable the lock to be traced. Max
|
@param [in] ref= A meaningful reference to enable the lock to be traced. Max
|
||||||
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.
|
Should already be assigned and available. The definition is available by
|
||||||
|
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
|
||||||
|
|
||||||
@@ -221,19 +220,6 @@ run;
|
|||||||
%let abortme=1;
|
%let abortme=1;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=MAKETABLE %then %do;
|
|
||||||
proc sql;
|
|
||||||
create table &ctl_ds(
|
|
||||||
lock_lib char(8),
|
|
||||||
lock_ds char(32),
|
|
||||||
lock_status_cd char(10) not null,
|
|
||||||
lock_user_nm char(100) not null ,
|
|
||||||
lock_ref char(200),
|
|
||||||
lock_pid char(10),
|
|
||||||
lock_start_dttm num format=E8601DT26.6,
|
|
||||||
lock_end_dttm num format=E8601DT26.6,
|
|
||||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds));
|
|
||||||
%end;
|
|
||||||
%else %do;
|
%else %do;
|
||||||
%let msg=lock_anytable given unsupported action (&action);
|
%let msg=lock_anytable given unsupported action (&action);
|
||||||
%let abortme=1;
|
%let abortme=1;
|
||||||
|
|||||||
@@ -10,8 +10,6 @@
|
|||||||
according to the variable types and formats.
|
according to the variable types and formats.
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
@li Respect PKs
|
|
||||||
@li Respect NOT NULLs
|
|
||||||
@li Consider dates, datetimes, times, integers etc
|
@li Consider dates, datetimes, times, integers etc
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@@ -27,16 +25,23 @@
|
|||||||
);
|
);
|
||||||
%mp_makedata(work.example)
|
%mp_makedata(work.example)
|
||||||
|
|
||||||
@param [in] libds The empty table in which to create data
|
@param [in] libds The empty table (libref.dataset) in which to create data
|
||||||
@param [out] obs= (500) The number of records to create.
|
@param [out] obs= (500) The maximum number of records to create. The table
|
||||||
|
is sorted with nodup on the primary key, so the actual number of records may
|
||||||
|
be lower than this.
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
@li mf_getvarlen.sas
|
@li mf_getvarlen.sas
|
||||||
|
@li mf_getvarlist.sas
|
||||||
|
@li mf_islibds.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mp_getcols.sas
|
@li mp_getcols.sas
|
||||||
@li mp_getpk.sas
|
@li mp_getpk.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_makedata.test.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|
||||||
@@ -44,44 +49,59 @@
|
|||||||
|
|
||||||
%macro mp_makedata(libds
|
%macro mp_makedata(libds
|
||||||
,obs=500
|
,obs=500
|
||||||
|
,seed=1
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
%local ds1 c1 n1 i col charvars numvars;
|
%local ds1 ds2 lib ds pk_fields i col charvars numvars ispk;
|
||||||
|
|
||||||
%if %mf_nobs(&libds)>0 %then %do;
|
%if %mf_islibds(&libds)=0 %then %do;
|
||||||
|
%put &sysmacroname: Invalid libds (&libds) - should be library.dataset format;
|
||||||
|
%return;
|
||||||
|
%end;
|
||||||
|
%else %if %mf_nobs(&libds)>0 %then %do;
|
||||||
%put &sysmacroname: &libds has data, it will not be recreated;
|
%put &sysmacroname: &libds has data, it will not be recreated;
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%local ds1 c1 n1;
|
/* set up temporary vars */
|
||||||
%let ds1=%mf_getuniquename(prefix=mp_makedata);
|
%let ds1=%mf_getuniquename(prefix=mp_makedatads1);
|
||||||
%let c1=%mf_getuniquename(prefix=mp_makedatacol);
|
%let ds2=%mf_getuniquename(prefix=mp_makedatads2);
|
||||||
%let n1=%mf_getuniquename(prefix=mp_makedatacol);
|
%let lib=%scan(&libds,1,.);
|
||||||
data &ds1;
|
%let ds=%scan(&libds,2,.);
|
||||||
|
|
||||||
|
/* grab the primary key vars */
|
||||||
|
%mp_getpk(&lib,ds=&ds,outds=&ds1)
|
||||||
|
|
||||||
|
proc sql noprint;
|
||||||
|
select coalescec(pk_fields,'_all_') into: pk_fields from &ds1;
|
||||||
|
|
||||||
|
data &ds2;
|
||||||
if 0 then set &libds;
|
if 0 then set &libds;
|
||||||
do _n_=1 to &obs;
|
do _n_=1 to &obs;
|
||||||
&c1=repeat(uuidgen(),10);
|
|
||||||
&n1=ranuni(1)*5000000;
|
|
||||||
drop &c1 &n1;
|
|
||||||
%let charvars=%mf_getvarlist(&libds,typefilter=C);
|
%let charvars=%mf_getvarlist(&libds,typefilter=C);
|
||||||
%if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
|
%if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
|
||||||
%let col=%scan(&charvars,&i);
|
%let col=%scan(&charvars,&i);
|
||||||
&col=subpad(&c1,1,%mf_getvarlen(&libds,&col));
|
/* create random value based on observation number and colum length */
|
||||||
|
&col=repeat(put(md5(cats(_n_)),$hex32.),%mf_getvarlen(&libds,&col)/32);
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
%let numvars=%mf_getvarlist(&libds,typefilter=N);
|
%let numvars=%mf_getvarlist(&libds,typefilter=N);
|
||||||
%if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
|
%if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
|
||||||
%let col=%scan(&numvars,&i);
|
%let col=%scan(&numvars,&i);
|
||||||
&col=&n1;
|
&col=_n_;
|
||||||
%end;
|
%end;
|
||||||
output;
|
output;
|
||||||
end;
|
end;
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
proc sort data=&ds2 nodupkey;
|
||||||
|
by &pk_fields;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
proc append base=&libds data=&ds1;
|
proc append base=&libds data=&ds2;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
proc sql;
|
proc sql;
|
||||||
drop table &ds1;
|
drop table &ds1, &ds2;
|
||||||
|
|
||||||
%mend mp_makedata;
|
%mend mp_makedata;
|
||||||
@@ -3,13 +3,15 @@
|
|||||||
@brief Reset an option to original value
|
@brief Reset an option to original value
|
||||||
@details Inspired by the SAS Jedi -
|
@details Inspired by the SAS Jedi -
|
||||||
https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-system-options
|
https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-system-options
|
||||||
Called as follows:
|
|
||||||
|
|
||||||
options obs=30;
|
Called as follows:
|
||||||
%mp_resetoption(OBS)
|
|
||||||
|
options obs=30 ps=max;
|
||||||
|
%mp_resetoption(OBS)
|
||||||
|
%mp_resetoption(PS)
|
||||||
|
|
||||||
|
|
||||||
@param option the option to reset
|
@param [in] option the option to reset
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
|
|||||||
246
base/mp_retainedkey.sas
Normal file
246
base/mp_retainedkey.sas
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Generate and apply retained key values to a staging table
|
||||||
|
@details This macro will populate a staging table with a Retained Key based on
|
||||||
|
a business key and a base (target) table.
|
||||||
|
|
||||||
|
Definition of retained key ([source](
|
||||||
|
http://bukhantsov.org/2012/04/what-is-data-vault/)):
|
||||||
|
|
||||||
|
> The retained key is a key which is mapped to business key one-to-one. In
|
||||||
|
> comparison, the surrogate key includes time and there can be many surrogate
|
||||||
|
> keys corresponding to one business key. This explains the name of the key,
|
||||||
|
> it is retained with insertion of a new version of a row while surrogate key
|
||||||
|
> is increasing.
|
||||||
|
|
||||||
|
This macro is designed to be used as part of a wider load / ETL process (such
|
||||||
|
as the one in [Data Controller for SAS](https://datacontroller.io)).
|
||||||
|
|
||||||
|
Specifically, the macro assumes that the base table has already been 'locked'
|
||||||
|
(eg with the mp_lockanytable.sas macro) prior to invocation. Also, several
|
||||||
|
tables are assumed to exist (names are configurable):
|
||||||
|
|
||||||
|
@li work.staging_table - the staged data, minus the retained key element
|
||||||
|
@li permlib.base_table - the target table to be loaded (**not** loaded by this
|
||||||
|
macro)
|
||||||
|
@li permlib.maxkeytable - optional, used to store load metaadata.
|
||||||
|
The definition is available by running mp_coretable.sas as follows:
|
||||||
|
`mp_coretable(MAXKEYTABLE)`.
|
||||||
|
@li permlib.locktable - Necessary if maxkeytable is being populated. The
|
||||||
|
definition is available by running mp_coretable.sas as follows:
|
||||||
|
`mp_coretable(LOCKTABLE)`.
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] base_lib= (WORK) Libref of the base (target) table.
|
||||||
|
@param [in] base_dsn= (BASETABLE) Name of the base (target) table.
|
||||||
|
@param [in] append_lib= (WORK) Libref of the staging table
|
||||||
|
@param [in] append_dsn= (APPENDTABLE) Name of the staging table
|
||||||
|
@param [in] retained_key= (DEFAULT_RK) Name of RK to generate (should exist on
|
||||||
|
base table)
|
||||||
|
@param [in] business_key= (PK1 PK2) Business key against which to generate
|
||||||
|
RK values. Should be unique and not null on the staging table.
|
||||||
|
@param [in] check_uniqueness=(NO) Set to yes to perform a uniqueness check.
|
||||||
|
Recommended if there is a chance that the staging data is not unique on the
|
||||||
|
business key.
|
||||||
|
@param [in] maxkeytable= (0) Provide a maxkeytable libds reference here, to
|
||||||
|
store load metadata (maxkey val, load time). Set to zero if metadata is not
|
||||||
|
required, eg, when preparing a 'dummy' load. Structure is described above.
|
||||||
|
See below for sample data.
|
||||||
|
|KEYTABLE:$32.|KEYCOLUMN:$32.|MAX_KEY:best.|PROCESSED_DTTM:E8601DT26.6|
|
||||||
|
|---|---|---|---|
|
||||||
|
|`DC487173.MPE_SELECTBOX `|`SELECTBOX_RK `|`55 `|`1950427787.8 `|
|
||||||
|
|`DC487173.MPE_FILTERANYTABLE `|`filter_rk `|`14 `|`1951053886.8 `|
|
||||||
|
@param [in] locktable= (0) If updating the maxkeytable, provide the libds
|
||||||
|
reference to the lock table (per mp_lockanytable.sas macro)
|
||||||
|
@param [in] filter_str= Apply a filter - useful for SCD2 or BITEMPORAL loads.
|
||||||
|
Example: `filter_str=%str( (where=( &now < &tech_to)) )`
|
||||||
|
@param [out] outds= (WORK.APPEND) Output table (staging table + retained key)
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existvar.sas
|
||||||
|
@li mf_getquotedstr.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_lockanytable.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_filterstore.sas
|
||||||
|
@li mp_retainedkey.test.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_retainedkey(
|
||||||
|
base_lib=WORK
|
||||||
|
,base_dsn=BASETABLE
|
||||||
|
,append_lib=WORK
|
||||||
|
,append_dsn=APPENDTABLE
|
||||||
|
,retained_key=DEFAULT_RK
|
||||||
|
,business_key= PK1 PK2
|
||||||
|
,check_uniqueness=NO
|
||||||
|
,maxkeytable=0
|
||||||
|
,locktable=0
|
||||||
|
,outds=WORK.APPEND
|
||||||
|
,filter_str=
|
||||||
|
);
|
||||||
|
%put &sysmacroname entry vars:;
|
||||||
|
%put _local_;
|
||||||
|
|
||||||
|
%local base_libds app_libds key_field check maxkey idx_pk newkey_cnt iserr
|
||||||
|
msg x tempds1 tempds2 comma_pk appnobs checknobs dropvar tempvar idx_val;
|
||||||
|
%let base_libds=%upcase(&base_lib..&base_dsn);
|
||||||
|
%let app_libds=%upcase(&append_lib..&append_dsn);
|
||||||
|
%let tempds1=%mf_getuniquename();
|
||||||
|
%let tempds2=%mf_getuniquename();
|
||||||
|
%let comma_pk=%mf_getquotedstr(in_str=%str(&business_key),dlm=%str(,),quote=);
|
||||||
|
%let outds=%sysfunc(ifc(%index(&outds,.)=0,work.&outds,&outds));
|
||||||
|
/* validation checks */
|
||||||
|
%let iserr=0;
|
||||||
|
%if &syscc>0 %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=%str(SYSCC=&syscc on macro entry);
|
||||||
|
%end;
|
||||||
|
%else %if %sysfunc(exist(&base_libds))=0 %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=%str(Base LIBDS (&base_libds) expected but NOT FOUND);
|
||||||
|
%end;
|
||||||
|
%else %if %sysfunc(exist(&app_libds))=0 %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=%str(Append LIBDS (&app_libds) expected but NOT FOUND);
|
||||||
|
%end;
|
||||||
|
%else %if &maxkeytable ne 0 and %sysfunc(exist(&maxkeytable))=0 %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=%str(Maxkeytable (&maxkeytable) expected but NOT FOUND);
|
||||||
|
%end;
|
||||||
|
%else %if &maxkeytable ne 0 and %sysfunc(exist(&locktable))=0 %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=%str(Locktable (&locktable) expected but NOT FOUND);
|
||||||
|
%end;
|
||||||
|
%else %if %length(&business_key)=0 %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=%str(Business key (&business_key) expected but NOT FOUND);
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%do x=1 %to %sysfunc(countw(&business_key));
|
||||||
|
/* check business key values exist */
|
||||||
|
%let key_field=%scan(&business_key,&x,%str( ));
|
||||||
|
%if not %mf_existvar(&app_libds,&key_field) %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=Business key (&key_field) not found on &app_libds!;
|
||||||
|
%goto err;
|
||||||
|
%end;
|
||||||
|
%else %if not %mf_existvar(&base_libds,&key_field) %then %do;
|
||||||
|
%let iserr=1;
|
||||||
|
%let msg=Business key (&key_field) not found on &base_libds!;
|
||||||
|
%goto err;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
%err:
|
||||||
|
%if &iserr=1 %then %do;
|
||||||
|
/* err case so first perform an unlock of the base table before exiting */
|
||||||
|
%mp_lockanytable(
|
||||||
|
UNLOCK,lib=&base_lib,ds=&base_dsn,ref=%superq(msg),ctl_ds=&locktable
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%mp_abort(iftrue=(&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
|
||||||
|
|
||||||
|
proc sql noprint;
|
||||||
|
select sum(max(&retained_key),0) into: maxkey from &base_libds;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get base table RK and bus field values for lookup
|
||||||
|
*/
|
||||||
|
proc sql noprint;
|
||||||
|
create table &tempds1 as
|
||||||
|
select distinct &comma_pk,&retained_key
|
||||||
|
from &base_libds &filter_str
|
||||||
|
order by &comma_pk,&retained_key;
|
||||||
|
|
||||||
|
%if &check_uniqueness=YES %then %do;
|
||||||
|
select count(*) into:checknobs
|
||||||
|
from (select distinct &comma_pk from &app_libds);
|
||||||
|
select count(*) into: appnobs from &app_libds; /* might be view */
|
||||||
|
%if &checknobs ne &appnobs %then %do;
|
||||||
|
%let msg=Source table &app_libds is not unique on (&business_key);
|
||||||
|
%let iserr=1;
|
||||||
|
%end;
|
||||||
|
%end;
|
||||||
|
%if &iserr=1 %then %do;
|
||||||
|
/* err case so first perform an unlock of the base table before exiting */
|
||||||
|
%mp_lockanytable(
|
||||||
|
UNLOCK,lib=&base_lib,ds=&base_dsn,ref=%superq(msg),ctl_ds=&locktable
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
%mp_abort(iftrue= (&iserr=1),mac=mp_retainedkey,msg=%superq(msg))
|
||||||
|
|
||||||
|
%if %mf_existvar(&app_libds,&retained_key)
|
||||||
|
%then %let dropvar=(drop=&retained_key);
|
||||||
|
|
||||||
|
/* prepare interim table with retained key populated for matching keys */
|
||||||
|
proc sql noprint;
|
||||||
|
create table &tempds2 as
|
||||||
|
select b.&retained_key, a.*
|
||||||
|
from &app_libds &dropvar a
|
||||||
|
left join &tempds1 b
|
||||||
|
on 1
|
||||||
|
%do idx_pk=1 %to %sysfunc(countw(&business_key));
|
||||||
|
%let idx_val=%scan(&business_key,&idx_pk);
|
||||||
|
and a.&idx_val=b.&idx_val
|
||||||
|
%end;
|
||||||
|
order by &retained_key;
|
||||||
|
|
||||||
|
/* identify the number of entries without retained keys (new records) */
|
||||||
|
select count(*) into: newkey_cnt
|
||||||
|
from &tempds2
|
||||||
|
where missing(&retained_key);
|
||||||
|
quit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update maxkey table if link provided
|
||||||
|
*/
|
||||||
|
%if &maxkeytable ne 0 %then %do;
|
||||||
|
proc sql noprint;
|
||||||
|
select count(*) into: check from &maxkeytable
|
||||||
|
where upcase(keytable)="&base_libds";
|
||||||
|
|
||||||
|
%mp_lockanytable(LOCK
|
||||||
|
,lib=%scan(&maxkeytable,1,.)
|
||||||
|
,ds=%scan(&maxkeytable,2,.)
|
||||||
|
,ref=Updating maxkeyvalues with mp_retainedkey
|
||||||
|
,ctl_ds=&locktable
|
||||||
|
)
|
||||||
|
proc sql;
|
||||||
|
%if &check=0 %then %do;
|
||||||
|
insert into &maxkeytable
|
||||||
|
set keytable="&base_libds"
|
||||||
|
,keycolumn="&retained_key"
|
||||||
|
,max_key=%eval(&maxkey+&newkey_cnt)
|
||||||
|
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||||
|
%end;
|
||||||
|
%else %do;
|
||||||
|
update &maxkeytable
|
||||||
|
set max_key=%eval(&maxkey+&newkey_cnt)
|
||||||
|
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||||
|
where keytable="&base_libds";
|
||||||
|
%end;
|
||||||
|
%mp_lockanytable(UNLOCK
|
||||||
|
,lib=%scan(&maxkeytable,1,.)
|
||||||
|
,ds=%scan(&maxkeytable,2,.)
|
||||||
|
,ref=Updating maxkeyvalues with maxkey=%eval(&maxkey+&newkey_cnt)
|
||||||
|
,ctl_ds=&locktable
|
||||||
|
)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
/* fill in the missing retained key values */
|
||||||
|
%let tempvar=%mf_getuniquename();
|
||||||
|
data &outds(drop=&tempvar);
|
||||||
|
retain &tempvar %eval(&maxkey+1);
|
||||||
|
set &tempds2;
|
||||||
|
if &retained_key =. then &retained_key=&tempvar;
|
||||||
|
&tempvar=&tempvar+1;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend mp_retainedkey;
|
||||||
|
|
||||||
@@ -11,9 +11,7 @@
|
|||||||
|
|
||||||
This macro will only work for BASE (V9) engine libraries. It works by
|
This macro will only work for BASE (V9) engine libraries. It works by
|
||||||
creating a copy of the dataset (without data, WITH constraints) in the same
|
creating a copy of the dataset (without data, WITH constraints) in the same
|
||||||
library, appending a sorted view into it, and finally - renaming it. By
|
library, appending a sorted view into it, and finally - renaming it.
|
||||||
default, COMPRESS=CHAR and REUSE=YES will be applied, this behaviour can
|
|
||||||
be adjusted using the `dsoptions=` parameter.
|
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
@@ -41,7 +39,6 @@
|
|||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@source https://github.com/sasjs/core
|
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
else do;
|
else do;
|
||||||
x+1;
|
x+1;
|
||||||
call symputx(name,quote(cats(value)),'l');
|
call symputx(name,quote(cats(value)),'l');
|
||||||
call symputx('pval'!!left(x),name,'l');
|
call symputx(cats('pval',x),name,'l');
|
||||||
call symputx('pcnt',x,'l');
|
call symputx('pcnt',x,'l');
|
||||||
end;
|
end;
|
||||||
run;
|
run;
|
||||||
|
|||||||
@@ -20,15 +20,24 @@
|
|||||||
;;;;
|
;;;;
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
For more examples, see mp_validatecol.test.sas
|
||||||
|
|
||||||
|
Tip - when contributing, use https://regex101.com to test the regex validity!
|
||||||
|
|
||||||
@param [in] incol The column to be validated
|
@param [in] incol The column to be validated
|
||||||
@param [in] rule The rule to apply. Current rules:
|
@param [in] rule The rule to apply. Current rules:
|
||||||
|
@li ISINT - checks if the variable is an integer
|
||||||
@li ISNUM - checks if the variable is numeric
|
@li ISNUM - checks if the variable is numeric
|
||||||
@li LIBDS - matches LIBREF.DATASET format
|
@li LIBDS - matches LIBREF.DATASET format
|
||||||
|
@li FORMAT - checks if the provided format is syntactically valid
|
||||||
@param [out] outcol The variable to create, with the results of the match
|
@param [out] outcol The variable to create, with the results of the match
|
||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_getuniquename.sas
|
@li mf_getuniquename.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_validatecol.test.sas
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
**/
|
**/
|
||||||
|
|
||||||
@@ -38,7 +47,15 @@
|
|||||||
%local tempcol;
|
%local tempcol;
|
||||||
%let tempcol=%mf_getuniquename();
|
%let tempcol=%mf_getuniquename();
|
||||||
|
|
||||||
%if &rule=ISNUM %then %do;
|
%if &rule=ISINT %then %do;
|
||||||
|
&outcol=0;
|
||||||
|
if not missing(&incol) then do;
|
||||||
|
&tempcol=input(&incol,?? best32.);
|
||||||
|
if not missing(&tempcol) then if mod(&tempcol,1)=0 then &outcol=1;
|
||||||
|
end;
|
||||||
|
drop &tempcol;
|
||||||
|
%end;
|
||||||
|
%else %if &rule=ISNUM %then %do;
|
||||||
/*
|
/*
|
||||||
credit SØREN LASSEN
|
credit SØREN LASSEN
|
||||||
https://sasmacro.blogspot.com/2009/06/welcome-isnum-macro.html
|
https://sasmacro.blogspot.com/2009/06/welcome-isnum-macro.html
|
||||||
@@ -62,5 +79,19 @@
|
|||||||
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
|
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
|
||||||
else &outcol=0;
|
else &outcol=0;
|
||||||
%end;
|
%end;
|
||||||
|
%else %if &rule=FORMAT %then %do;
|
||||||
|
/* match valid format - regex could probably be improved */
|
||||||
|
if _n_=1 then do;
|
||||||
|
retain &tempcol;
|
||||||
|
&tempcol=prxparse('/^[_a-z\$]\w{0,31}\.[0-9]*$/i');
|
||||||
|
if missing(&tempcol) then do;
|
||||||
|
putlog "%str(ERR)OR: Invalid expression for FORMAT";
|
||||||
|
stop;
|
||||||
|
end;
|
||||||
|
drop &tempcol;
|
||||||
|
end;
|
||||||
|
if prxmatch(&tempcol, trim(&incol)) then &outcol=1;
|
||||||
|
else &outcol=0;
|
||||||
|
%end;
|
||||||
|
|
||||||
%mend mp_validatecol;
|
%mend mp_validatecol;
|
||||||
|
|||||||
82
fcmp/mcf_length.sas
Normal file
82
fcmp/mcf_length.sas
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Returns the length of a numeric value
|
||||||
|
@details
|
||||||
|
Returns the length, in bytes, of a numeric value. If the value is
|
||||||
|
missing, then 0 is returned.
|
||||||
|
|
||||||
|
The function itself takes the following (positional) parameters:
|
||||||
|
|
||||||
|
| PARAMETER | DESCRIPTION |
|
||||||
|
|---|---|
|
||||||
|
| var | variable (or value) to be tested|
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
ina=1;
|
||||||
|
inb=10000000;
|
||||||
|
inc=12345678;
|
||||||
|
ind=.;
|
||||||
|
outa=mcf_length(ina);
|
||||||
|
outb=mcf_length(inb);
|
||||||
|
outc=mcf_length(inc);
|
||||||
|
outd=mcf_length(ind);
|
||||||
|
put (out:)(=);
|
||||||
|
run;
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
> outa=3 outb=4 outc=5 outd=0
|
||||||
|
|
||||||
|
@param [out] wrap= (NO) Choose YES to add the proc fcmp wrapper.
|
||||||
|
@param [out] insert_cmplib= (NO) Choose YES to insert the package into the
|
||||||
|
CMPLIB reference.
|
||||||
|
@param [out] lib= (work) The output library in which to create the catalog.
|
||||||
|
@param [out] cat= (sasjs) The output catalog in which to create the package.
|
||||||
|
@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
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mcf_length(wrap=NO
|
||||||
|
,insert_cmplib=NO
|
||||||
|
,lib=WORK
|
||||||
|
,cat=SASJS
|
||||||
|
,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 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;
|
||||||
|
else if trunc(var,7)=var then len=7;
|
||||||
|
else len=8;
|
||||||
|
return(len);
|
||||||
|
endsub;
|
||||||
|
|
||||||
|
%if &wrap=YES %then %do;
|
||||||
|
quit;
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%if &insert_cmplib=YES %then %do;
|
||||||
|
options insert=(CMPLIB=(&lib..&cat));
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mcf_length;
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
%if %mf_existfunction(stpsrv_header)=1 %then %return;
|
%if %mf_existfunction(stpsrv_header)=1 %then %return;
|
||||||
|
|
||||||
%if &wrap=YES %then %do;
|
%if &wrap=YES %then %do;
|
||||||
proc fcmp outcat=&lib..&cat..&pkg;
|
proc fcmp outlib=&lib..&cat..&pkg;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
function stpsrv_header(name $, value $);
|
function stpsrv_header(name $, value $);
|
||||||
|
|||||||
@@ -39,6 +39,9 @@
|
|||||||
@param [out] pkg= (utils) The output package in which to create the function.
|
@param [out] pkg= (utils) The output package in which to create the function.
|
||||||
Uses a 3 part format: libref.catalog.package
|
Uses a 3 part format: libref.catalog.package
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existfunction.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mcf_string2file(wrap=NO
|
%macro mcf_string2file(wrap=NO
|
||||||
@@ -48,8 +51,10 @@
|
|||||||
,pkg=UTILS
|
,pkg=UTILS
|
||||||
)/*/STORE SOURCE*/;
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%if %mf_existfunction(mcf_string2file)=1 %then %return;
|
||||||
|
|
||||||
%if &wrap=YES %then %do;
|
%if &wrap=YES %then %do;
|
||||||
proc fcmp outcat=&lib..&cat..&pkg;
|
proc fcmp outlib=&lib..&cat..&pkg;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
function mcf_string2file(filepath $, string $, mode $);
|
function mcf_string2file(filepath $, string $, mode $);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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,129 +93,140 @@ 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,dbg=0 ';
|
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
|
||||||
|
put ' ,engine=DATASTEP ';
|
||||||
|
put ' ,missing=NULL ';
|
||||||
|
put ' ,showmeta=NO ';
|
||||||
put ')/*/STORE SOURCE*/; ';
|
put ')/*/STORE SOURCE*/; ';
|
||||||
put '%put output location=&jref; ';
|
put '%local tempds colinfo fmtds i numcols; ';
|
||||||
|
put '%let numcols=0; ';
|
||||||
|
put ' ';
|
||||||
put '%if &action=OPEN %then %do; ';
|
put '%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'' ; ';
|
||||||
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||||
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 ' 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 ' data;run;%let tempds=&syslast; ';
|
put ' %if &missing=STRING %then %do; ';
|
||||||
put ' proc sql;drop table &tempds; ';
|
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
|
||||||
|
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
|
||||||
|
put ' %goto datastep; ';
|
||||||
|
put ' %end; ';
|
||||||
put ' data &tempds /view=&tempds;set &ds; ';
|
put ' 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 ' proc json out=&jref pretty ';
|
put ' proc json out=&jref pretty ';
|
||||||
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 ' %local cols i tempds; ';
|
put ' %datastep: ';
|
||||||
put ' %let cols=0; ';
|
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
|
||||||
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
|
put ' %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 ._ - .z = null ';
|
put ' value bart (default=40) ';
|
||||||
|
put ' %if &missing=NULL %then %do; ';
|
||||||
|
put ' ._ - .z = null ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %do; ';
|
||||||
|
put ' ._ = [quote()] ';
|
||||||
|
put ' . = null ';
|
||||||
|
put ' .a - .z = [quote()] ';
|
||||||
|
put ' %end; ';
|
||||||
put ' other = [best.]; ';
|
put ' 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, ';
|
||||||
@@ -221,49 +236,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); ';
|
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; ';
|
||||||
@@ -321,14 +359,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,dbg=%str(&_debug) ';
|
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; ';
|
||||||
@@ -343,15 +382,12 @@ data _null_;
|
|||||||
put ' set &tempds; ';
|
put ' set &tempds; ';
|
||||||
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
|
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
|
||||||
put ' i+1; ';
|
put ' i+1; ';
|
||||||
put ' call symputx(''wt''!!left(i),name,''l''); ';
|
put ' call symputx(cats(''wt'',i),name,''l''); ';
|
||||||
put ' call symputx(''wtcnt'',i,''l''); ';
|
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||||
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
put ' data _null_; file &fref mod encoding=''utf-8''; ';
|
||||||
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''); ';
|
||||||
@@ -361,8 +397,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; ';
|
||||||
@@ -390,7 +425,11 @@ data _null_;
|
|||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
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(),E8601DT26.6)" ''" ''; ';
|
||||||
|
put ' length memsize $32; ';
|
||||||
|
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<<''; ';
|
||||||
@@ -418,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=);';
|
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
||||||
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt)';
|
put ' %mm_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
||||||
|
put ' ,showmeta=&showmeta';
|
||||||
|
put ' )';
|
||||||
put '%mend;';
|
put '%mend;';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ data _null_;
|
|||||||
set repos;
|
set repos;
|
||||||
where repositorytype in('CUSTOM','FOUNDATION');
|
where repositorytype in('CUSTOM','FOUNDATION');
|
||||||
keep id name ;
|
keep id name ;
|
||||||
call symputx('repo'!!left(_n_),name,'l');
|
call symputx(cats('repo',_n_),name,'l');
|
||||||
call symputx('repocnt',_n_,'l');
|
call symputx('repocnt',_n_,'l');
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,6 @@
|
|||||||
@param [in] stpcode= the source file (or fileref) containing the SAS code to load
|
@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.
|
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 [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
|
@param mDebug= set to 1 to show debug messages in the log
|
||||||
|
|
||||||
@version 9.3
|
@version 9.3
|
||||||
@@ -30,16 +27,8 @@
|
|||||||
,stpcode=
|
,stpcode=
|
||||||
,minify=NO
|
,minify=NO
|
||||||
,mdebug=0
|
,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 */
|
/* first, check if STP exists */
|
||||||
%local tsuri;
|
%local tsuri;
|
||||||
%let tsuri=stopifempty ;
|
%let tsuri=stopifempty ;
|
||||||
|
|||||||
@@ -23,17 +23,25 @@
|
|||||||
%mm_webout(CLOSE)
|
%mm_webout(CLOSE)
|
||||||
|
|
||||||
|
|
||||||
@param action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
||||||
@param ds The dataset to send back to the frontend
|
@param [in] ds The dataset to send back to the frontend
|
||||||
@param dslabel= value to use instead of the real name for sending to JSON
|
@param [out] dslabel= Value to use instead of table name for sending to JSON
|
||||||
@param fmt=(Y) Set to N to send back unformatted values
|
@param [in] fmt=(Y) Set to N to send back unformatted values
|
||||||
@param 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
|
||||||
|
(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);
|
%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;
|
||||||
@@ -91,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,dbg=%str(&_debug)
|
,engine=&jsonengine,missing=&missing,showmeta=&showmeta
|
||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
@@ -113,15 +122,12 @@
|
|||||||
set &tempds;
|
set &tempds;
|
||||||
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
||||||
i+1;
|
i+1;
|
||||||
call symputx('wt'!!left(i),name,'l');
|
call symputx(cats('wt',i),name,'l');
|
||||||
call symputx('wtcnt',i,'l');
|
call symputx('wtcnt',i,'l');
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8';
|
||||||
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');
|
||||||
@@ -131,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;
|
||||||
@@ -160,7 +165,11 @@
|
|||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
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));
|
||||||
|
put ',"MEMSIZE" : ' memsize;
|
||||||
put "}" @;
|
put "}" @;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
put '>>weboutEND<<';
|
put '>>weboutEND<<';
|
||||||
|
|||||||
365
package-lock.json
generated
365
package-lock.json
generated
@@ -10,73 +10,25 @@
|
|||||||
"ts-loader": "^9.2.6"
|
"ts-loader": "^9.2.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sasjs/cli": "^2.39.0"
|
"@sasjs/cli": "3.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/adapter": {
|
"node_modules/@sasjs/adapter": {
|
||||||
"version": "2.12.0",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-3.4.1.tgz",
|
||||||
"integrity": "sha512-zzIuohhR8KUDl3DfIFOW38gv3LADPnOBCLOvLoKu4hH5R/UJDkjZ/Gdgc8B35vI7aOprYOLK/T5D/Z44OaTkqw==",
|
"integrity": "sha512-FbsvYDaoJAuH8FMidXhX3Kh4Eb8qIcxy5iiCxHgcSRWM89W29W21WfCqCw0F28yFr+V5vZkvphwXMKFdOGGxlw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/utils": "^2.32.0",
|
|
||||||
"axios": "^0.21.4",
|
|
||||||
"axios-cookiejar-support": "^1.0.1",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"https": "^1.0.0",
|
|
||||||
"tough-cookie": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sasjs/cli": {
|
|
||||||
"version": "2.39.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.39.0.tgz",
|
|
||||||
"integrity": "sha512-n2LcU4n0QCEbUpXqZnBz/Ey5Td0nMJmgJpZRymMGfYEM0Y0x/CeXemd+kXHPjUvgQ+FX+SQzcvUQTEY/YlT4hA==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sasjs/adapter": "2.12.0",
|
|
||||||
"@sasjs/core": "2.45.2",
|
|
||||||
"@sasjs/lint": "1.11.2",
|
|
||||||
"@sasjs/utils": "2.32.0",
|
"@sasjs/utils": "2.32.0",
|
||||||
"chalk": "4.1.2",
|
"axios": "0.25.0",
|
||||||
"csv-stringify": "5.6.5",
|
"axios-cookiejar-support": "1.0.1",
|
||||||
"dotenv": "10.0.0",
|
"form-data": "4.0.0",
|
||||||
"esm": "3.2.25",
|
"https": "1.0.0",
|
||||||
"find": "0.3.0",
|
"tough-cookie": "4.0.0"
|
||||||
"get-installed-path": "4.0.8",
|
|
||||||
"js-base64": "3.7.2",
|
|
||||||
"jsdom": "17.0.0",
|
|
||||||
"jwt-decode": "3.1.2",
|
|
||||||
"lodash.groupby": "4.6.0",
|
|
||||||
"lodash.uniqby": "4.7.0",
|
|
||||||
"node-graphviz": "0.1.0",
|
|
||||||
"ora": "5.4.1",
|
|
||||||
"rimraf": "3.0.2",
|
|
||||||
"shelljs": "0.8.4",
|
|
||||||
"xml": "1.0.1",
|
|
||||||
"yargs": "17.2.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"sasjs": "build/index.js"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sasjs/core": {
|
"node_modules/@sasjs/adapter/node_modules/@sasjs/utils": {
|
||||||
"version": "2.45.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.45.2.tgz",
|
|
||||||
"integrity": "sha512-tg+oZCD8GFMXsg+vDL66LMnyU+t151Hrqd7yl+pMXH2qwkA14N/j6QdkTBZOchskqOA/3PnpOlAZN/xxMW2gdg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@sasjs/lint": {
|
|
||||||
"version": "1.11.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/lint/-/lint-1.11.2.tgz",
|
|
||||||
"integrity": "sha512-zEonhvha9kwrD+hxhG0hEhtfqpXwffH4vRDIr6eDiXkC7S8M3yImpjyFBvX/THJO5+8iuY8TYkOXKl7+nK/wAg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@sasjs/utils": "^2.19.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sasjs/utils": {
|
|
||||||
"version": "2.32.0",
|
"version": "2.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.32.0.tgz",
|
||||||
"integrity": "sha512-xnvdEuI4PhTtulcdDEIMK7IxVj9bOMU1JTnxRuSEKWcsclY9P9Fw3cnMOOEgXCDffrOPn3f54DP7Wb1GXd+f8g==",
|
"integrity": "sha512-xnvdEuI4PhTtulcdDEIMK7IxVj9bOMU1JTnxRuSEKWcsclY9P9Fw3cnMOOEgXCDffrOPn3f54DP7Wb1GXd+f8g==",
|
||||||
@@ -96,6 +48,101 @@
|
|||||||
"valid-url": "^1.0.9"
|
"valid-url": "^1.0.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@sasjs/cli": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-px5aFXNoTlML7g4TvP92q/uqWnHGbxnwEzy+4D+sJbXgpzLlCyyuUU7rbd/9sXT+/Pi7vjfHfBCv0JhoGYr6gw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@sasjs/adapter": "3.4.1",
|
||||||
|
"@sasjs/core": "3.10.0",
|
||||||
|
"@sasjs/lint": "1.11.2",
|
||||||
|
"@sasjs/utils": "2.35.0",
|
||||||
|
"chalk": "4.1.2",
|
||||||
|
"csv-stringify": "5.6.5",
|
||||||
|
"dotenv": "10.0.0",
|
||||||
|
"esm": "3.2.25",
|
||||||
|
"find": "0.3.0",
|
||||||
|
"get-installed-path": "4.0.8",
|
||||||
|
"js-base64": "3.7.2",
|
||||||
|
"jsdom": "17.0.0",
|
||||||
|
"jwt-decode": "3.1.2",
|
||||||
|
"lodash.groupby": "4.6.0",
|
||||||
|
"lodash.uniqby": "4.7.0",
|
||||||
|
"node-graphviz": "0.1.0",
|
||||||
|
"ora": "5.4.1",
|
||||||
|
"rimraf": "3.0.2",
|
||||||
|
"shelljs": "0.8.5",
|
||||||
|
"xml": "1.0.1",
|
||||||
|
"yargs": "17.2.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"sasjs": "build/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sasjs/cli/node_modules/@sasjs/utils": {
|
||||||
|
"version": "2.35.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.35.0.tgz",
|
||||||
|
"integrity": "sha512-q9ZKV+TXqwiaj+0z5U7/00eBpp2QpjKfC9BKx7A6rQjBl10WtoWd5C9Em+RQULWVEdRbVS2XcnNsWelbKq/Zsw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/fs-extra": "^9.0.13",
|
||||||
|
"@types/prompts": "^2.0.13",
|
||||||
|
"chalk": "^4.1.1",
|
||||||
|
"cli-table": "^0.3.6",
|
||||||
|
"consola": "^2.15.0",
|
||||||
|
"csv-stringify": "^5.6.5",
|
||||||
|
"find": "0.3.0",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"jwt-decode": "^3.1.2",
|
||||||
|
"prompts": "^2.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"valid-url": "^1.0.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sasjs/core": {
|
||||||
|
"version": "3.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-3.10.0.tgz",
|
||||||
|
"integrity": "sha512-lgLxDYpIvwSrXFaUaTFCR0KXHQEc5QIOL4DU87TvBHEUUAWNQHzuVQWkavLtW5hbvLGnPXnyvspzoSzmBojXzg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ts-loader": "^9.2.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sasjs/lint": {
|
||||||
|
"version": "1.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/lint/-/lint-1.11.2.tgz",
|
||||||
|
"integrity": "sha512-zEonhvha9kwrD+hxhG0hEhtfqpXwffH4vRDIr6eDiXkC7S8M3yImpjyFBvX/THJO5+8iuY8TYkOXKl7+nK/wAg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@sasjs/utils": "^2.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@sasjs/utils": {
|
||||||
|
"version": "2.34.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.34.1.tgz",
|
||||||
|
"integrity": "sha512-hd1qieH3d7+xH96n5DpRGTEazeAhYyBBKCdnKhOXMgF2TZVoHFdRs5REfT88CKza6DHBGRVGnIVm5ORGP4cVLg==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/fs-extra": "^9.0.11",
|
||||||
|
"@types/prompts": "^2.0.13",
|
||||||
|
"chalk": "^4.1.1",
|
||||||
|
"cli-table": "^0.3.6",
|
||||||
|
"consola": "^2.15.0",
|
||||||
|
"csv-stringify": "^5.6.5",
|
||||||
|
"find": "0.3.0",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"jwt-decode": "^3.1.2",
|
||||||
|
"lodash.groupby": "4.6.0",
|
||||||
|
"lodash.uniqby": "4.7.0",
|
||||||
|
"prompts": "^2.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"valid-url": "^1.0.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tootallnate/once": {
|
"node_modules/@tootallnate/once": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||||
@@ -132,9 +179,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/fs-extra": {
|
"node_modules/@types/fs-extra": {
|
||||||
"version": "9.0.12",
|
"version": "9.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||||
"integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==",
|
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -449,12 +496,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "0.21.4",
|
"version": "0.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
|
||||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.14.0"
|
"follow-redirects": "^1.14.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios-cookiejar-support": {
|
"node_modules/axios-cookiejar-support": {
|
||||||
@@ -1051,9 +1098,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.14.5",
|
"version": "1.14.7",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
|
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1335,9 +1382,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-core-module": {
|
"node_modules/is-core-module": {
|
||||||
"version": "2.4.0",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
|
||||||
"integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
|
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
@@ -1887,13 +1934,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/resolve": {
|
"node_modules/resolve": {
|
||||||
"version": "1.20.0",
|
"version": "1.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
|
||||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
"integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-core-module": "^2.2.0",
|
"is-core-module": "^2.8.0",
|
||||||
"path-parse": "^1.0.6"
|
"path-parse": "^1.0.7",
|
||||||
|
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"resolve": "bin/resolve"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
@@ -2019,9 +2070,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/shelljs": {
|
"node_modules/shelljs": {
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
|
||||||
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
|
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"glob": "^7.0.0",
|
"glob": "^7.0.0",
|
||||||
@@ -2111,6 +2162,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/supports-preserve-symlinks-flag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/symbol-tree": {
|
"node_modules/symbol-tree": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||||
@@ -2581,29 +2644,50 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sasjs/adapter": {
|
"@sasjs/adapter": {
|
||||||
"version": "2.12.0",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-3.4.1.tgz",
|
||||||
"integrity": "sha512-zzIuohhR8KUDl3DfIFOW38gv3LADPnOBCLOvLoKu4hH5R/UJDkjZ/Gdgc8B35vI7aOprYOLK/T5D/Z44OaTkqw==",
|
"integrity": "sha512-FbsvYDaoJAuH8FMidXhX3Kh4Eb8qIcxy5iiCxHgcSRWM89W29W21WfCqCw0F28yFr+V5vZkvphwXMKFdOGGxlw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sasjs/utils": "^2.32.0",
|
"@sasjs/utils": "2.32.0",
|
||||||
"axios": "^0.21.4",
|
"axios": "0.25.0",
|
||||||
"axios-cookiejar-support": "^1.0.1",
|
"axios-cookiejar-support": "1.0.1",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "4.0.0",
|
||||||
"https": "^1.0.0",
|
"https": "1.0.0",
|
||||||
"tough-cookie": "^4.0.0"
|
"tough-cookie": "4.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@sasjs/utils": {
|
||||||
|
"version": "2.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.32.0.tgz",
|
||||||
|
"integrity": "sha512-xnvdEuI4PhTtulcdDEIMK7IxVj9bOMU1JTnxRuSEKWcsclY9P9Fw3cnMOOEgXCDffrOPn3f54DP7Wb1GXd+f8g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/fs-extra": "^9.0.11",
|
||||||
|
"@types/prompts": "^2.0.13",
|
||||||
|
"chalk": "^4.1.1",
|
||||||
|
"cli-table": "^0.3.6",
|
||||||
|
"consola": "^2.15.0",
|
||||||
|
"csv-stringify": "^5.6.5",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"jwt-decode": "^3.1.2",
|
||||||
|
"prompts": "^2.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"valid-url": "^1.0.9"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sasjs/cli": {
|
"@sasjs/cli": {
|
||||||
"version": "2.39.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.39.0.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-3.6.0.tgz",
|
||||||
"integrity": "sha512-n2LcU4n0QCEbUpXqZnBz/Ey5Td0nMJmgJpZRymMGfYEM0Y0x/CeXemd+kXHPjUvgQ+FX+SQzcvUQTEY/YlT4hA==",
|
"integrity": "sha512-px5aFXNoTlML7g4TvP92q/uqWnHGbxnwEzy+4D+sJbXgpzLlCyyuUU7rbd/9sXT+/Pi7vjfHfBCv0JhoGYr6gw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@sasjs/adapter": "2.12.0",
|
"@sasjs/adapter": "3.4.1",
|
||||||
"@sasjs/core": "2.45.2",
|
"@sasjs/core": "3.10.0",
|
||||||
"@sasjs/lint": "1.11.2",
|
"@sasjs/lint": "1.11.2",
|
||||||
"@sasjs/utils": "2.32.0",
|
"@sasjs/utils": "2.35.0",
|
||||||
"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",
|
||||||
@@ -2618,16 +2702,41 @@
|
|||||||
"node-graphviz": "0.1.0",
|
"node-graphviz": "0.1.0",
|
||||||
"ora": "5.4.1",
|
"ora": "5.4.1",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"shelljs": "0.8.4",
|
"shelljs": "0.8.5",
|
||||||
"xml": "1.0.1",
|
"xml": "1.0.1",
|
||||||
"yargs": "17.2.1"
|
"yargs": "17.2.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@sasjs/utils": {
|
||||||
|
"version": "2.35.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.35.0.tgz",
|
||||||
|
"integrity": "sha512-q9ZKV+TXqwiaj+0z5U7/00eBpp2QpjKfC9BKx7A6rQjBl10WtoWd5C9Em+RQULWVEdRbVS2XcnNsWelbKq/Zsw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/fs-extra": "^9.0.13",
|
||||||
|
"@types/prompts": "^2.0.13",
|
||||||
|
"chalk": "^4.1.1",
|
||||||
|
"cli-table": "^0.3.6",
|
||||||
|
"consola": "^2.15.0",
|
||||||
|
"csv-stringify": "^5.6.5",
|
||||||
|
"find": "0.3.0",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"jwt-decode": "^3.1.2",
|
||||||
|
"prompts": "^2.4.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"valid-url": "^1.0.9"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sasjs/core": {
|
"@sasjs/core": {
|
||||||
"version": "2.45.2",
|
"version": "3.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.45.2.tgz",
|
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-3.10.0.tgz",
|
||||||
"integrity": "sha512-tg+oZCD8GFMXsg+vDL66LMnyU+t151Hrqd7yl+pMXH2qwkA14N/j6QdkTBZOchskqOA/3PnpOlAZN/xxMW2gdg==",
|
"integrity": "sha512-lgLxDYpIvwSrXFaUaTFCR0KXHQEc5QIOL4DU87TvBHEUUAWNQHzuVQWkavLtW5hbvLGnPXnyvspzoSzmBojXzg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ts-loader": "^9.2.6"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"@sasjs/lint": {
|
"@sasjs/lint": {
|
||||||
"version": "1.11.2",
|
"version": "1.11.2",
|
||||||
@@ -2639,9 +2748,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 +2759,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"
|
||||||
@@ -2690,9 +2802,9 @@
|
|||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"@types/fs-extra": {
|
"@types/fs-extra": {
|
||||||
"version": "9.0.12",
|
"version": "9.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||||
"integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==",
|
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -2974,12 +3086,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"axios": {
|
"axios": {
|
||||||
"version": "0.21.4",
|
"version": "0.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
|
||||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
"integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"follow-redirects": "^1.14.0"
|
"follow-redirects": "^1.14.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"axios-cookiejar-support": {
|
"axios-cookiejar-support": {
|
||||||
@@ -3421,9 +3533,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.14.5",
|
"version": "1.14.7",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
|
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
@@ -3627,9 +3739,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"is-core-module": {
|
"is-core-module": {
|
||||||
"version": "2.4.0",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
|
||||||
"integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==",
|
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"has": "^1.0.3"
|
"has": "^1.0.3"
|
||||||
@@ -4051,13 +4163,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.20.0",
|
"version": "1.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz",
|
||||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
"integrity": "sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-core-module": "^2.2.0",
|
"is-core-module": "^2.8.0",
|
||||||
"path-parse": "^1.0.6"
|
"path-parse": "^1.0.7",
|
||||||
|
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolve-dir": {
|
"resolve-dir": {
|
||||||
@@ -4138,9 +4251,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shelljs": {
|
"shelljs": {
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
|
||||||
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
|
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"glob": "^7.0.0",
|
"glob": "^7.0.0",
|
||||||
@@ -4212,6 +4325,12 @@
|
|||||||
"has-flag": "^4.0.0"
|
"has-flag": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"supports-preserve-symlinks-flag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"symbol-tree": {
|
"symbol-tree": {
|
||||||
"version": "3.2.4",
|
"version": "3.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@sasjs/core",
|
"name": "@sasjs/core",
|
||||||
"description": "Production Ready Macros for SAS Application Developers",
|
"description": "Macros for SAS Application Developers",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"SAS",
|
"SAS",
|
||||||
@@ -33,9 +33,6 @@
|
|||||||
"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.6.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ts-loader": "^9.2.6"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +1,73 @@
|
|||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
<!DOCTYPE html
|
||||||
|
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
<!-- HTML header for doxygen 1.8.17-->
|
<!-- HTML header for doxygen 1.8.17-->
|
||||||
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
<html xmlns="https://www.w3.org/1999/xhtml" lang="en">
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
|
||||||
<meta property="og:type" content="website">
|
|
||||||
<meta name="author" content="Allan Bowe">
|
|
||||||
<meta name="generator" content="Doxygen $doxygenversion" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<!--BEGIN PROJECT_NAME-->
|
|
||||||
<meta name="description" content="$projectbrief" />
|
|
||||||
<!--END PROJECT_NAME-->
|
|
||||||
<!--BEGIN !PROJECT_NAME-->
|
|
||||||
<title>$title</title>
|
|
||||||
<!--END !PROJECT_NAME-->
|
|
||||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
|
|
||||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
|
||||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
|
||||||
$treeview $search $mathjax
|
|
||||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
|
||||||
<link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" />
|
|
||||||
$extrastylesheet
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="top">
|
|
||||||
<!-- do not remove this div, it is closed by doxygen! -->
|
|
||||||
|
|
||||||
<!--BEGIN TITLEAREA-->
|
<head>
|
||||||
<div id="titlearea">
|
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" />
|
||||||
<table cellspacing="0" cellpadding="0">
|
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
||||||
<tbody>
|
<meta property="og:type" content="website">
|
||||||
<tr style="height: 56px">
|
<meta property="og:title" content="MacroCore" />
|
||||||
<!--BEGIN PROJECT_LOGO-->
|
<meta property="og:url" content="https://core.sasjs.io" />
|
||||||
<td id="projectlogo">
|
<meta property="og:image" content="https://core.sasjs.io/Macro_core_website_1.png" />
|
||||||
<a href="$relpath^"
|
<meta name="author" content="Allan Bowe">
|
||||||
><img alt="Logo" src="$relpath^$projectlogo"
|
<meta name="generator" content="Doxygen $doxygenversion" />
|
||||||
/></a>
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</td>
|
<!--BEGIN PROJECT_NAME-->
|
||||||
<!--END PROJECT_LOGO-->
|
<meta name="description" content="$projectbrief" />
|
||||||
<td id="projectalign" style="padding-left: 0.5em">
|
<meta name="og:description" content="$projectbrief" />
|
||||||
<div id="projectbrief">
|
<!--END PROJECT_NAME-->
|
||||||
Production Ready Macros for SAS Application Developers<br />
|
<!--BEGIN !PROJECT_NAME-->
|
||||||
<a href="https://github.com/sasjs/core">
|
<title>$title</title>
|
||||||
https://github.com/sasjs/core
|
<!--END !PROJECT_NAME-->
|
||||||
</a>
|
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css" />
|
||||||
</div>
|
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||||
</td>
|
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||||
</tr>
|
$treeview $search $mathjax
|
||||||
</table>
|
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||||
</td>
|
<link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" />
|
||||||
|
$extrastylesheet
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="top">
|
||||||
|
<!-- do not remove this div, it is closed by doxygen! -->
|
||||||
|
|
||||||
|
<!--BEGIN TITLEAREA-->
|
||||||
|
<div id="titlearea">
|
||||||
|
<table cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr style="height: 56px">
|
||||||
|
<!--BEGIN PROJECT_LOGO-->
|
||||||
|
<td id="projectlogo">
|
||||||
|
<a href="$relpath^"><img alt="Logo" src="$relpath^$projectlogo" /></a>
|
||||||
|
</td>
|
||||||
|
<!--END PROJECT_LOGO-->
|
||||||
|
<td id="projectalign" style="padding-left: 0.5em">
|
||||||
|
<div id="projectbrief">
|
||||||
|
Macros for SAS Application Developers<br />
|
||||||
|
<a href="https://github.com/sasjs/core">
|
||||||
|
https://github.com/sasjs/core
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
|
||||||
<!--BEGIN DISABLE_INDEX-->
|
<!--BEGIN DISABLE_INDEX-->
|
||||||
<!--BEGIN SEARCHENGINE-->
|
<!--BEGIN SEARCHENGINE-->
|
||||||
<td>$searchbox</td>
|
<td>$searchbox</td>
|
||||||
<!--END SEARCHENGINE-->
|
<!--END SEARCHENGINE-->
|
||||||
<!--END DISABLE_INDEX-->
|
<!--END DISABLE_INDEX-->
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
<!--END TITLEAREA-->
|
|
||||||
<!-- end header part -->
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
<!--END TITLEAREA-->
|
||||||
|
<!-- end header part -->
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://cli.sasjs.io/sasjsconfig-schema.json",
|
"$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json",
|
||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"base",
|
"base",
|
||||||
"fcmp",
|
"fcmp",
|
||||||
|
|||||||
@@ -20,11 +20,17 @@
|
|||||||
%ms_webout(CLOSE)
|
%ms_webout(CLOSE)
|
||||||
|
|
||||||
|
|
||||||
@param action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
|
||||||
@param ds The dataset to send back to the frontend
|
@param [in] ds The dataset to send back to the frontend
|
||||||
@param dslabel= value to use instead of the real name for sending to JSON
|
@param [out] dslabel= value to use instead of table name for sending to JSON
|
||||||
@param fmt=(Y) Set to N to send back unformatted values
|
@param [in] fmt= (Y) Set to N to send back unformatted values
|
||||||
@param 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
|
||||||
|
(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
|
||||||
@@ -39,7 +45,9 @@
|
|||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y);
|
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL
|
||||||
|
,showmeta=NO
|
||||||
|
);
|
||||||
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
|
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
|
||||||
sasjs_tables;
|
sasjs_tables;
|
||||||
|
|
||||||
@@ -79,7 +87,7 @@
|
|||||||
OPTIONS NOBOMFILE;
|
OPTIONS NOBOMFILE;
|
||||||
|
|
||||||
/* setup json */
|
/* setup json */
|
||||||
data _null_;file &fref encoding='utf-8';
|
data _null_;file &fref encoding='utf-8' termstr=lf;
|
||||||
%if %str(&_debug) ge 131 %then %do;
|
%if %str(&_debug) ge 131 %then %do;
|
||||||
put '>>weboutBEGIN<<';
|
put '>>weboutBEGIN<<';
|
||||||
%end;
|
%end;
|
||||||
@@ -91,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,dbg=%str(&_debug)
|
,engine=DATASTEP,missing=&missing,showmeta=&showmeta
|
||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
@@ -106,16 +114,13 @@
|
|||||||
set &tempds;
|
set &tempds;
|
||||||
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
||||||
i+1;
|
i+1;
|
||||||
call symputx('wt'!!left(i),name,'l');
|
call symputx(cats('wt',i),name,'l');
|
||||||
call symputx('wtcnt',i,'l');
|
call symputx('wtcnt',i,'l');
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
||||||
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
|
data _null_; file &fref mod encoding='utf-8' termstr=lf;
|
||||||
out=_data_ (keep=name type length format:);
|
|
||||||
run;%let tempds=%scan(&syslast,2,.);
|
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
|
||||||
dsid=open("WORK.&wt",'is');
|
dsid=open("WORK.&wt",'is');
|
||||||
nlobs=attrn(dsid,'NLOBS');
|
nlobs=attrn(dsid,'NLOBS');
|
||||||
nvars=attrn(dsid,'NVARS');
|
nvars=attrn(dsid,'NVARS');
|
||||||
@@ -124,17 +129,16 @@
|
|||||||
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';
|
|
||||||
put "}";
|
put "}";
|
||||||
%end;
|
%end;
|
||||||
data _null_; file &fref mod encoding='utf-8';
|
data _null_; file &fref mod encoding='utf-8' termstr=lf termstr=lf;
|
||||||
put "}";
|
put "}";
|
||||||
run;
|
run;
|
||||||
%end;
|
%end;
|
||||||
/* close off json */
|
/* close off json */
|
||||||
data _null_;file &fref mod encoding='utf-8';
|
data _null_;file &fref mod encoding='utf-8' termstr=lf;
|
||||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||||
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
put ",""SYSUSERID"" : ""&sysuserid"" ";
|
||||||
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
|
||||||
@@ -147,7 +151,9 @@
|
|||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
|
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
|
||||||
put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
|
put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
|
||||||
put ",""SYSPROCESSNAME"" : ""&SYSPROCESSNAME"" ";
|
length SYSPROCESSNAME $512;
|
||||||
|
SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
|
||||||
|
put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
@@ -155,9 +161,11 @@
|
|||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
|
||||||
autoexec=quote(trim(getoption('autoexec')));
|
length autoexec $512;
|
||||||
|
autoexec=quote(urlencode(trim(getoption('autoexec'))));
|
||||||
put ',"AUTOEXEC" : ' autoexec;
|
put ',"AUTOEXEC" : ' autoexec;
|
||||||
|
length memsize $32;
|
||||||
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
|
||||||
memsize=quote(cats(memsize));
|
memsize=quote(cats(memsize));
|
||||||
put ',"MEMSIZE" : ' memsize;
|
put ',"MEMSIZE" : ' memsize;
|
||||||
|
|||||||
67
tests/crossplatform/mcf_length.test.sas
Normal file
67
tests/crossplatform/mcf_length.test.sas
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mcf_length.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mcf_length.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mcf_length(wrap=YES, insert_cmplib=YES)
|
||||||
|
|
||||||
|
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));
|
||||||
|
call symputx('six',mcf_length(1234567890));
|
||||||
|
call symputx('seven',mcf_length(12345678901234));
|
||||||
|
call symputx('eight',mcf_length(12345678901234567));
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
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
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%str(&four)=%str(4)),
|
||||||
|
desc=Check for length 4
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%str(&five)=%str(5)),
|
||||||
|
desc=Check for length 5
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%str(&six)=%str(6)),
|
||||||
|
desc=Check for length 6
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%str(&seven)=%str(7)),
|
||||||
|
desc=Check for length 3
|
||||||
|
)
|
||||||
|
%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
|
||||||
|
)
|
||||||
22
tests/crossplatform/mf_existvar.test.sas
Normal file
22
tests/crossplatform/mf_existvar.test.sas
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_existvar macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existvar.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_existvar(sashelp.class,age)=1),
|
||||||
|
desc=Checking existing var exists,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_existvar(sashelp.class,isjustanumber)=0),
|
||||||
|
desc=Checking non existing var does not exist,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
46
tests/crossplatform/mf_islibds.test.sas
Normal file
46
tests/crossplatform/mf_islibds.test.sas
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_islibds macro
|
||||||
|
|
||||||
|
%put %mf_islibds(work.something)=1;
|
||||||
|
%put %mf_islibds(nolib)=0;
|
||||||
|
%put %mf_islibds(badlibref.ds)=0;
|
||||||
|
%put %mf_islibds(w.t.f)=0;
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_islibds.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%mf_islibds(work.something)=1
|
||||||
|
),
|
||||||
|
desc=%str(Checking mf_islibds(work.something)=1),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%mf_islibds(nolib)=0
|
||||||
|
),
|
||||||
|
desc=%str(Checking mf_islibds(nolib)=0),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%mf_islibds(badlibref.ds)=0
|
||||||
|
),
|
||||||
|
desc=%str(Checking mf_islibds(badlibref.ds)=0),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%mf_islibds(w.t.f)=0
|
||||||
|
),
|
||||||
|
desc=%str(Checking mf_islibds(w.t.f)=0),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
20
tests/crossplatform/mf_wordsinstr1andstr2.test.sas
Normal file
20
tests/crossplatform/mf_wordsinstr1andstr2.test.sas
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_wordsinstr1andstr2 macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_wordsinstr1andstr2.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%let x=%mf_wordsinstr1andstr2(str1=xx DOLLAR x $CHAR xxx W MONNAME
|
||||||
|
,str2=DOLLAR $CHAR W MONNAME xxxxxx
|
||||||
|
);
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"&x"="DOLLAR $CHAR W MONNAME"
|
||||||
|
),
|
||||||
|
desc=Checking basic string,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
20
tests/crossplatform/mf_wordsinstr1butnotstr2.test.sas
Normal file
20
tests/crossplatform/mf_wordsinstr1butnotstr2.test.sas
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_wordsinstr1butnotstr2 macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_wordsinstr1butnotstr2.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%let x=%mf_wordsinstr1butnotstr2(str1=xx DOLLAR x $CHAR xxx W MONNAME
|
||||||
|
,str2=ff xx x xxx xxxxxx
|
||||||
|
);
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"&x"="DOLLAR $CHAR W MONNAME"
|
||||||
|
),
|
||||||
|
desc=Checking basic string,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
74
tests/crossplatform/mf_writefile.test.sas
Normal file
74
tests/crossplatform/mf_writefile.test.sas
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_writefile.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_writefile.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mf_writefile(&sasjswork/myfile.txt,l1=some content,l2=more content)
|
||||||
|
data _null_;
|
||||||
|
infile "&sasjswork/myfile.txt";
|
||||||
|
input;
|
||||||
|
if _n_=2 then call symputx('test1',_infile_);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Check code ran without errors,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&test1=more content),
|
||||||
|
desc=Checking line was created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mf_writefile(&sasjswork/myfile.txt,l1=some content,l2=different content)
|
||||||
|
data _null_;
|
||||||
|
infile "&sasjswork/myfile.txt";
|
||||||
|
input;
|
||||||
|
if _n_=2 then call symputx('test2',_infile_);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Check code ran without errors for test2,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&test2=different content),
|
||||||
|
desc=Checking second line was overwritten,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%global test3 test4;
|
||||||
|
%mf_writefile(&sasjswork/myfile.txt
|
||||||
|
,mode=a
|
||||||
|
,l1=%str(aah, content)
|
||||||
|
,l2=append content
|
||||||
|
)
|
||||||
|
data _null_;
|
||||||
|
infile "&sasjswork/myfile.txt";
|
||||||
|
input;
|
||||||
|
if _n_=2 then call symputx('test3',_infile_);
|
||||||
|
if _n_=4 then call symputx('test4',_infile_);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Check code ran without errors for test2,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&test3=different content),
|
||||||
|
desc=Checking second line was not overwritten,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&test4=append content),
|
||||||
|
desc=Checking fourth line was appended,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
45
tests/crossplatform/mp_applyformats.test.sas
Normal file
45
tests/crossplatform/mp_applyformats.test.sas
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_applyformats.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getvarformat.sas
|
||||||
|
@li mp_applyformats.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_getcols.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 1 Base case
|
||||||
|
*/
|
||||||
|
|
||||||
|
data work.example;
|
||||||
|
set sashelp.prdsale;
|
||||||
|
format _all_;
|
||||||
|
run;
|
||||||
|
%let origfmt=%mf_getvarformat(work.example,month);
|
||||||
|
|
||||||
|
%mp_getcols(sashelp.prdsale,outds=work.cols)
|
||||||
|
|
||||||
|
data work.cols2;
|
||||||
|
set work.cols;
|
||||||
|
lib='WORK';
|
||||||
|
ds='EXAMPLE';
|
||||||
|
var=name;
|
||||||
|
fmt=format;
|
||||||
|
keep lib ds var fmt;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_applyformats(work.cols2)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&orig_fmt"=""),
|
||||||
|
desc=Check that formats were cleared,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("%mf_getvarformat(work.example,month)"="MONNAME3."),
|
||||||
|
desc=Check that formats were applied,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
15
tests/crossplatform/mp_assert.test.sas
Normal file
15
tests/crossplatform/mp_assert.test.sas
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_assert macro
|
||||||
|
@details This is quite "meta".. it's just testing itself
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(1=1),
|
||||||
|
desc=Checking result was created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
80
tests/crossplatform/mp_assertscope.test.sas
Normal file
80
tests/crossplatform/mp_assertscope.test.sas
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_assertscope macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getvalue.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertscope.sas
|
||||||
|
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro dostuff(action);
|
||||||
|
%if &action=ADD %then %do;
|
||||||
|
%global NEWVAR1 NEWVAR2;
|
||||||
|
%end;
|
||||||
|
%else %if &action=DEL %then %do;
|
||||||
|
%symdel NEWVAR1 NEWVAR2;
|
||||||
|
%end;
|
||||||
|
%else %if &action=MOD %then %do;
|
||||||
|
%let NEWVAR1=Let us pray..;
|
||||||
|
%end;
|
||||||
|
%else %if &action=NOTHING %then %do;
|
||||||
|
%local a b c d e;
|
||||||
|
%end;
|
||||||
|
%mend dostuff;
|
||||||
|
|
||||||
|
|
||||||
|
/* check for adding variables */
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%dostuff(ADD)
|
||||||
|
%mp_assertscope(COMPARE,outds=work.testing_the_tester1)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_getvalue(work.testing_the_tester1,test_comments)"
|
||||||
|
="Mod:() Add:(NEWVAR1 NEWVAR2) Del:()"
|
||||||
|
),
|
||||||
|
desc=Checking result when vars added,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/* check for modifying variables */
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%dostuff(MOD)
|
||||||
|
%mp_assertscope(COMPARE,outds=work.testing_the_tester2)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_getvalue(work.testing_the_tester2,test_comments)"
|
||||||
|
="Mod:(NEWVAR1) Add:() Del:()"
|
||||||
|
),
|
||||||
|
desc=Checking result when vars modified,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* check for deleting variables */
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%dostuff(DEL)
|
||||||
|
%mp_assertscope(COMPARE,outds=work.testing_the_tester3)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_getvalue(work.testing_the_tester3,test_comments)"
|
||||||
|
="Mod:() Add:() Del:(NEWVAR1 NEWVAR2)"
|
||||||
|
),
|
||||||
|
desc=Checking result when vars deleted,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* check for doing nothing */
|
||||||
|
%mp_assertscope(SNAPSHOT)
|
||||||
|
%dostuff(NOTHING)
|
||||||
|
%mp_assertscope(COMPARE,outds=work.testing_the_tester4)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%mf_getvalue(work.testing_the_tester4,test_comments)"
|
||||||
|
="GLOBAL Variables Unmodified"
|
||||||
|
),
|
||||||
|
desc=Checking results when nothing created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
31
tests/crossplatform/mp_coretable.test.sas
Normal file
31
tests/crossplatform/mp_coretable.test.sas
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_coretable.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_existds.sas
|
||||||
|
@li mp_coretable.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
%mp_coretable(LOCKTABLE,libds=work.lock)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_existds(work.lock)=1),
|
||||||
|
desc=Lock table created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_coretable(LOCKTABLE)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&syscc"="0"),
|
||||||
|
desc=DDL export ran without errors,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_coretable(FILTER_SUMMARY,libds=work.sum)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_existds(work.sum)=1),
|
||||||
|
desc=Filter summary table created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
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
|
||||||
|
)
|
||||||
85
tests/crossplatform/mp_filterstore.test.sas
Normal file
85
tests/crossplatform/mp_filterstore.test.sas
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_filterstore macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_coretable.sas
|
||||||
|
@li mp_filterstore.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
libname permlib (work);
|
||||||
|
|
||||||
|
%mp_coretable(LOCKTABLE,libds=permlib.locktable)
|
||||||
|
%mp_coretable(FILTER_SUMMARY,libds=permlib.filtsum)
|
||||||
|
%mp_coretable(FILTER_DETAIL,libds=permlib.filtdet)
|
||||||
|
%mp_coretable(MAXKEYTABLE,libds=permlib.maxkey)
|
||||||
|
|
||||||
|
/* valid filter */
|
||||||
|
data work.inds;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||||
|
OPERATOR_NM:$12. RAW_VALUE:$4000.;
|
||||||
|
datalines4;
|
||||||
|
AND,AND,1,AGE,=,12
|
||||||
|
AND,AND,1,SEX,<=,"'M'"
|
||||||
|
AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
|
||||||
|
AND,OR,2,Weight,>=,77.7
|
||||||
|
AND,OR,2,Weight,NE,77.7
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_filterstore(libds=sashelp.class,
|
||||||
|
queryds=work.inds,
|
||||||
|
filter_summary=permlib.filtsum,
|
||||||
|
filter_detail=permlib.filtdet,
|
||||||
|
lock_table=permlib.locktable,
|
||||||
|
maxkeytable=permlib.maxkey,
|
||||||
|
outresult=work.result,
|
||||||
|
outquery=work.query,
|
||||||
|
mdebug=1
|
||||||
|
)
|
||||||
|
%mp_assert(iftrue=(&syscc>0),
|
||||||
|
desc=Ensure macro runs without errors,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
/* ensure only one record created */
|
||||||
|
%mp_assertdsobs(permlib.filtsum,
|
||||||
|
desc=Initial query,
|
||||||
|
test=ATMOST 1,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
/* check RK is correct */
|
||||||
|
proc sql noprint;
|
||||||
|
select max(filter_rk) into: test1 from work.result;
|
||||||
|
%mp_assert(iftrue=(&test1=1),
|
||||||
|
desc=Ensure filter rk is correct,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* Test 2 - load same table again and ensure we get the same RK */
|
||||||
|
%mp_filterstore(libds=sashelp.class,
|
||||||
|
queryds=work.inds,
|
||||||
|
filter_summary=permlib.filtsum,
|
||||||
|
filter_detail=permlib.filtdet,
|
||||||
|
lock_table=permlib.locktable,
|
||||||
|
maxkeytable=permlib.maxkey,
|
||||||
|
outresult=work.result,
|
||||||
|
outquery=work.query,
|
||||||
|
mdebug=1
|
||||||
|
)
|
||||||
|
/* ensure only one record created */
|
||||||
|
%mp_assertdsobs(permlib.filtsum,
|
||||||
|
desc=Initial query - same obs,
|
||||||
|
test=ATMOST 1,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
/* check RK is correct */
|
||||||
|
proc sql noprint;
|
||||||
|
select max(filter_rk) into: test2 from work.result;
|
||||||
|
%mp_assert(iftrue=(&test2=1),
|
||||||
|
desc=Ensure filter rk is correct for second run,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_getcols.sas
|
@li mp_getcols.sas
|
||||||
|
@li mp_assertcols.sas
|
||||||
@li mp_assertcolvals.sas
|
@li mp_assertcolvals.sas
|
||||||
@li mp_assertdsobs.sas
|
@li mp_assertdsobs.sas
|
||||||
|
|
||||||
@@ -31,3 +32,9 @@ run;
|
|||||||
desc=All values have a match,
|
desc=All values have a match,
|
||||||
test=ALLVALS
|
test=ALLVALS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
%mp_assertcols(work.info,
|
||||||
|
cols=name type length varnum format label ddtype fmtname,
|
||||||
|
test=ALL,
|
||||||
|
desc=check all columns exist
|
||||||
|
)
|
||||||
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
|
||||||
|
)
|
||||||
@@ -34,6 +34,11 @@ data work.test;
|
|||||||
call symputx('dtval',dtval);
|
call symputx('dtval',dtval);
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Checking for error condition,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
%mp_assert(
|
%mp_assert(
|
||||||
iftrue=(&dtval=&compare),
|
iftrue=(&dtval=&compare),
|
||||||
52
tests/crossplatform/mp_jsonout.test.2.sas
Normal file
52
tests/crossplatform/mp_jsonout.test.2.sas
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_jsonout.sas macro with special missings
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_jsonout.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
filename webref temp;
|
||||||
|
|
||||||
|
data demo;
|
||||||
|
do x=._,.,.a,.b,.c,.d,.e,-99, 0, 1,2, 3.333333;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%mp_jsonout(OPEN,jref=webref)
|
||||||
|
%mp_jsonout(OBJ,demo,jref=webref,fmt=N,missing=STRING)
|
||||||
|
%mp_jsonout(CLOSE,jref=webref)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
infile webref;
|
||||||
|
input;
|
||||||
|
putlog _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
libname web JSON fileref=webref;
|
||||||
|
|
||||||
|
/* proc json turns to char - so switch back to numeric */
|
||||||
|
data work.test(keep=x);
|
||||||
|
set web.demo(rename=(x=y));
|
||||||
|
if y ='_' then x=._;
|
||||||
|
else if anyalpha(y) then x=input(cats(".",y),best.);
|
||||||
|
else x=input(y,best.);
|
||||||
|
put (_all_)(=);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Checking for error condition with special missing export,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
proc compare base=work.demo compare=work.test;
|
||||||
|
quit;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&sysinfo=0),
|
||||||
|
desc=Returned json is identical to input table for all special missings,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
|||||||
59
tests/crossplatform/mp_makedata.test.sas
Normal file
59
tests/crossplatform/mp_makedata.test.sas
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_makedata.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_nobs.sas
|
||||||
|
@li mp_makedata.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 1 - Regular makedata call
|
||||||
|
*/
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table work.example(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
constraint pk primary key(tx_from, dd_type,dd_source),
|
||||||
|
constraint nnn not null(DD_SHORTDESC)
|
||||||
|
);
|
||||||
|
%mp_makedata(work.example,obs=500)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("%mf_nobs(work.example)"="500"),
|
||||||
|
desc=Check that 500 rows were created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
data _null_;
|
||||||
|
set work.example;
|
||||||
|
call symputx('lenvar',length(dd_source));
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&lenvar"="2048"),
|
||||||
|
desc=Check that entire length of variable is populated,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
proc sql;
|
||||||
|
create table work.example2(
|
||||||
|
TX_FROM float format=datetime19.,
|
||||||
|
DD_TYPE char(16),
|
||||||
|
DD_SOURCE char(2048),
|
||||||
|
DD_SHORTDESC char(256),
|
||||||
|
some_num num
|
||||||
|
);
|
||||||
|
%mp_makedata(work.example2)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Ensure tables without keys still generate,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
116
tests/crossplatform/mp_retainedkey.test.sas
Normal file
116
tests/crossplatform/mp_retainedkey.test.sas
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_retainedkey macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_assert.sas
|
||||||
|
@li mp_assertcolvals.sas
|
||||||
|
@li mp_retainedkey.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup base tables
|
||||||
|
*/
|
||||||
|
proc sql;
|
||||||
|
create table work.maxkeytable(
|
||||||
|
keytable varchar(41) label='Base table in libref.dataset format',
|
||||||
|
keycolumn char(32) format=$32.
|
||||||
|
label='The Retained key field containing the key values.',
|
||||||
|
max_key num label=
|
||||||
|
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||||
|
processed_dttm num format=E8601DT26.6
|
||||||
|
label='Datetime this value was last updated',
|
||||||
|
constraint pk_mpe_maxkeyvalues
|
||||||
|
primary key(keytable));
|
||||||
|
|
||||||
|
create table work.locktable(
|
||||||
|
lock_lib char(8),
|
||||||
|
lock_ds char(32),
|
||||||
|
lock_status_cd char(10) not null,
|
||||||
|
lock_user_nm char(100) not null ,
|
||||||
|
lock_ref char(200),
|
||||||
|
lock_pid char(10),
|
||||||
|
lock_start_dttm num format=E8601DT26.6,
|
||||||
|
lock_end_dttm num format=E8601DT26.6,
|
||||||
|
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds));
|
||||||
|
|
||||||
|
data work.targetds;
|
||||||
|
rk_col=_n_;
|
||||||
|
set sashelp.class;
|
||||||
|
run;
|
||||||
|
|
||||||
|
data work.appendtable;
|
||||||
|
set sashelp.class;
|
||||||
|
if mod(_n_,2)=0 then name=cats('New',_n_);
|
||||||
|
if _n_<7;
|
||||||
|
run;
|
||||||
|
|
||||||
|
libname x (work);
|
||||||
|
|
||||||
|
/** Test 1 - base case **/
|
||||||
|
%mp_retainedkey(
|
||||||
|
base_lib=X
|
||||||
|
,base_dsn=targetds
|
||||||
|
,append_lib=X
|
||||||
|
,append_dsn=APPENDTABLE
|
||||||
|
,retained_key=rk_col
|
||||||
|
,business_key= name
|
||||||
|
,check_uniqueness=NO
|
||||||
|
,maxkeytable=0
|
||||||
|
,locktable=0
|
||||||
|
,outds=work.APPEND
|
||||||
|
,filter_str=
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Checking errors in test 1,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
data work.check;
|
||||||
|
do val=1,3,5,20,21,22;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%mp_assertcolvals(work.append.rk_col,
|
||||||
|
checkvals=work.check.val,
|
||||||
|
desc=All values have a match,
|
||||||
|
test=ALLVALS
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Test 2 - all new records, with metadata logging and unique check **/
|
||||||
|
data work.targetds2;
|
||||||
|
rk_col=_n_;
|
||||||
|
set sashelp.class;
|
||||||
|
run;
|
||||||
|
|
||||||
|
data work.appendtable2;
|
||||||
|
set sashelp.class;
|
||||||
|
do x=1 to 21;
|
||||||
|
name=cats('New',x);
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
stop;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_retainedkey(base_dsn=targetds2
|
||||||
|
,append_dsn=APPENDTABLE2
|
||||||
|
,retained_key=rk_col
|
||||||
|
,business_key= name
|
||||||
|
,check_uniqueness=YES
|
||||||
|
,maxkeytable=x.maxkeytable
|
||||||
|
,locktable=work.locktable
|
||||||
|
,outds=WORK.APPEND2
|
||||||
|
,filter_str=
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&syscc=0),
|
||||||
|
desc=Checking errors in test 2,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(%mf_nobs(work.append2)=21),
|
||||||
|
desc=Checking append records created,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -60,3 +60,73 @@ run;
|
|||||||
test=EQUALS 4,
|
test=EQUALS 4,
|
||||||
outds=work.test_results
|
outds=work.test_results
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 3 - FORMAT
|
||||||
|
*/
|
||||||
|
data test3;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input;
|
||||||
|
infile=_infile_;
|
||||||
|
%mp_validatecol(infile,FORMAT,is_format)
|
||||||
|
if is_format=1;
|
||||||
|
datalines4;
|
||||||
|
$.
|
||||||
|
$format.
|
||||||
|
$format12.2
|
||||||
|
somenum.
|
||||||
|
somenum12.4
|
||||||
|
above are good
|
||||||
|
the rest are bad
|
||||||
|
%abort
|
||||||
|
1&somethingverybad.
|
||||||
|
&
|
||||||
|
+-1
|
||||||
|
.
|
||||||
|
a.A
|
||||||
|
$format12.1b
|
||||||
|
$format12.1b1
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test3,
|
||||||
|
desc=Test3 - ISFORMAT,
|
||||||
|
test=EQUALS 5,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test 4 - ISINT
|
||||||
|
*/
|
||||||
|
data test4;
|
||||||
|
infile datalines4 dsd;
|
||||||
|
input;
|
||||||
|
infile=_infile_;
|
||||||
|
%mp_validatecol(infile,ISINT,is_integer)
|
||||||
|
if is_integer=1;
|
||||||
|
datalines4;
|
||||||
|
1
|
||||||
|
1234
|
||||||
|
-134
|
||||||
|
-1.0
|
||||||
|
1.0
|
||||||
|
0
|
||||||
|
above are good
|
||||||
|
the rest are bad
|
||||||
|
0.1
|
||||||
|
1.1
|
||||||
|
-0.001
|
||||||
|
%abort
|
||||||
|
1&somethingverybad.
|
||||||
|
&
|
||||||
|
+-1
|
||||||
|
.
|
||||||
|
a.A
|
||||||
|
$format12.1b
|
||||||
|
$format12.1b1
|
||||||
|
;;;;
|
||||||
|
run;
|
||||||
|
%mp_assertdsobs(work.test4,
|
||||||
|
desc=Test4 - ISFORMAT,
|
||||||
|
test=EQUALS 6,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mp_init.sas
|
@li mp_init.sas
|
||||||
|
@li mv_webout.sas
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|||||||
@@ -54,9 +54,9 @@
|
|||||||
that location
|
that location
|
||||||
@param [in] adapter= the macro uses the sasjs adapter by default. To use
|
@param [in] adapter= the macro uses the sasjs adapter by default. To use
|
||||||
another adapter, add a (different) fileref here.
|
another adapter, add a (different) fileref here.
|
||||||
@param [in] contextname= Choose a specific context on which to run the Job. Leave
|
@param [in] contextname= Choose a specific context on which to run the Job.
|
||||||
blank to use the default context. From Viya 3.5 it is possible to configure
|
Leave blank to use the default context. From Viya 3.5 it is possible to
|
||||||
a shared context - see
|
configure a shared context - see
|
||||||
https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5pyhn1wg3ja0drdl6h.htm&docsetVersion=3.5&locale=en
|
||||||
@param [in] mdebug=(0) set to 1 to enable DEBUG messages
|
@param [in] mdebug=(0) set to 1 to enable DEBUG messages
|
||||||
|
|
||||||
@@ -237,129 +237,140 @@ 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,dbg=0 ';
|
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
|
||||||
|
put ' ,engine=DATASTEP ';
|
||||||
|
put ' ,missing=NULL ';
|
||||||
|
put ' ,showmeta=NO ';
|
||||||
put ')/*/STORE SOURCE*/; ';
|
put ')/*/STORE SOURCE*/; ';
|
||||||
put '%put output location=&jref; ';
|
put '%local tempds colinfo fmtds i numcols; ';
|
||||||
|
put '%let numcols=0; ';
|
||||||
|
put ' ';
|
||||||
put '%if &action=OPEN %then %do; ';
|
put '%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'' ; ';
|
||||||
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
put ' put ''{"PROCESSED_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''"''; ';
|
||||||
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 ' 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 ' data;run;%let tempds=&syslast; ';
|
put ' %if &missing=STRING %then %do; ';
|
||||||
put ' proc sql;drop table &tempds; ';
|
put ' %put &sysmacroname: Special Missings not supported in proc json.; ';
|
||||||
|
put ' %put &sysmacroname: Switching to DATASTEP engine; ';
|
||||||
|
put ' %goto datastep; ';
|
||||||
|
put ' %end; ';
|
||||||
put ' data &tempds /view=&tempds;set &ds; ';
|
put ' 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 ' proc json out=&jref pretty ';
|
put ' proc json out=&jref pretty ';
|
||||||
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 ' %local cols i tempds; ';
|
put ' %datastep: ';
|
||||||
put ' %let cols=0; ';
|
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 ';
|
||||||
put ' %if %sysfunc(exist(&ds)) ne 1 & %sysfunc(exist(&ds,VIEW)) ne 1 %then %do; ';
|
put ' %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 ._ - .z = null ';
|
put ' value bart (default=40) ';
|
||||||
|
put ' %if &missing=NULL %then %do; ';
|
||||||
|
put ' ._ - .z = null ';
|
||||||
|
put ' %end; ';
|
||||||
|
put ' %else %do; ';
|
||||||
|
put ' ._ = [quote()] ';
|
||||||
|
put ' . = null ';
|
||||||
|
put ' .a - .z = [quote()] ';
|
||||||
|
put ' %end; ';
|
||||||
put ' other = [best.]; ';
|
put ' 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, ';
|
||||||
@@ -369,49 +380,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); ';
|
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; ';
|
||||||
@@ -533,12 +567,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,dbg=%str(&_debug) ';
|
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; ';
|
||||||
@@ -553,14 +588,11 @@ data _null_;
|
|||||||
put ' set &tempds; ';
|
put ' set &tempds; ';
|
||||||
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
|
put ' if not (upcase(name) =:"DATA"); /* ignore temp datasets */ ';
|
||||||
put ' i+1; ';
|
put ' i+1; ';
|
||||||
put ' call symputx(''wt''!!left(i),name); ';
|
put ' call symputx(cats(''wt'',i),name,''l''); ';
|
||||||
put ' call symputx(''wtcnt'',i); ';
|
put ' call symputx(''wtcnt'',i,''l''); ';
|
||||||
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''); ';
|
||||||
@@ -570,8 +602,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; ';
|
||||||
@@ -595,7 +626,11 @@ data _null_;
|
|||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
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(),E8601DT26.6)" ''" ''; ';
|
||||||
|
put ' length memsize $32; ';
|
||||||
|
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; ';
|
||||||
@@ -629,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=);';
|
put '%macro webout(action,ds,dslabel=,fmt=,missing=NULL,showmeta=NO);';
|
||||||
put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt)';
|
put ' %mv_webout(&action,ds=&ds,dslabel=&dslabel,fmt=&fmt,missing=&missing';
|
||||||
|
put ' ,showmeta=&showmeta';
|
||||||
|
put ' )';
|
||||||
put '%mend;';
|
put '%mend;';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ libname &libref1a JSON fileref=&fname1a;
|
|||||||
%let found=0;
|
%let found=0;
|
||||||
%put Getting object uri from &libref1a..items;
|
%put Getting object uri from &libref1a..items;
|
||||||
data _null_;
|
data _null_;
|
||||||
|
length contenttype name $1000;
|
||||||
set &libref1a..items;
|
set &libref1a..items;
|
||||||
if contenttype="&contenttype" and upcase(name)="%upcase(&name)" then do;
|
if contenttype="&contenttype" and upcase(name)="%upcase(&name)" then do;
|
||||||
call symputx('uri',uri,'l');
|
call symputx('uri',uri,'l');
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ libname &libref1a JSON fileref=&fname1a;
|
|||||||
%let found=0;
|
%let found=0;
|
||||||
%put Getting object uri from &libref1a..items;
|
%put Getting object uri from &libref1a..items;
|
||||||
data _null_;
|
data _null_;
|
||||||
|
length contenttype name $1000;
|
||||||
set &libref1a..items;
|
set &libref1a..items;
|
||||||
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
|
if contenttype='jobDefinition' and upcase(name)="%upcase(&name)" then do;
|
||||||
call symputx('uri',cats("&base_uri",uri),'l');
|
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;
|
|
||||||
@@ -272,7 +272,7 @@ data;run;%let jdswaitfor=&syslast;
|
|||||||
data _null_;
|
data _null_;
|
||||||
infile &jfref lrecl=32767;
|
infile &jfref lrecl=32767;
|
||||||
input;
|
input;
|
||||||
jparams='jparams'!!left(symget('jid'));
|
jparams=cats('jparams',symget('jid'));
|
||||||
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
|
call symputx(jparams,substr(_infile_,3,length(_infile_)-4));
|
||||||
run;
|
run;
|
||||||
%local jobuid&jid;
|
%local jobuid&jid;
|
||||||
|
|||||||
@@ -20,13 +20,19 @@
|
|||||||
%mv_webout(CLOSE)
|
%mv_webout(CLOSE)
|
||||||
|
|
||||||
|
|
||||||
@param action Either OPEN, ARR, OBJ or CLOSE
|
@param [in] action Either OPEN, ARR, OBJ or CLOSE
|
||||||
@param ds The dataset to send back to the frontend
|
@param [in] ds The dataset to send back to the frontend
|
||||||
@param _webout= fileref for returning the json
|
@param [in] _webout= fileref for returning the json
|
||||||
@param fref=(_mvwtemp) Temp fileref to which to write the output
|
@param [out] fref=(_mvwtemp) Temp fileref to which to write the output
|
||||||
@param dslabel= value to use instead of the real name for sending to JSON
|
@param [out] dslabel= value to use instead of table name for sending to JSON
|
||||||
@param fmt=(Y) change to N to strip formats from output
|
@param [in] fmt=(Y) change to N to strip formats from output
|
||||||
@param 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
|
||||||
|
(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
|
||||||
@@ -36,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);
|
%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;
|
||||||
@@ -158,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,dbg=%str(&_debug)
|
,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta
|
||||||
)
|
)
|
||||||
%end;
|
%end;
|
||||||
%else %if &action=CLOSE %then %do;
|
%else %if &action=CLOSE %then %do;
|
||||||
@@ -178,14 +187,11 @@
|
|||||||
set &tempds;
|
set &tempds;
|
||||||
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
if not (upcase(name) =:"DATA"); /* ignore temp datasets */
|
||||||
i+1;
|
i+1;
|
||||||
call symputx('wt'!!left(i),name);
|
call symputx(cats('wt',i),name,'l');
|
||||||
call symputx('wtcnt',i);
|
call symputx('wtcnt',i,'l');
|
||||||
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');
|
||||||
@@ -195,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;
|
||||||
@@ -220,7 +225,11 @@
|
|||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
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));
|
||||||
|
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user