mirror of
https://github.com/sasjs/core.git
synced 2025-12-27 21:10:05 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
500fb8124f | ||
|
|
88ddba2a4b | ||
|
|
86f6d06b85 | ||
|
|
1cefc0e7ee | ||
|
|
412182a022 | ||
|
|
43b8ee1c7e | ||
|
|
83eea02240 | ||
|
|
a14e31804a | ||
|
|
3fa639ebf7 | ||
|
|
ed11d44fe8 | ||
|
|
de4ea8888f | ||
|
|
ea0a936871 | ||
|
|
042987c91e | ||
|
|
6669e74baa | ||
|
|
906f9a139d | ||
|
|
b31f960635 | ||
|
|
1ed3cb31b5 | ||
|
|
ca7c332f20 | ||
|
|
d587b44b34 | ||
|
|
e43aac972a | ||
|
|
7dbe31b5d3 | ||
|
|
1672c96340 | ||
|
|
453aee2c1f | ||
|
|
00abbdcd65 | ||
|
|
88685dc585 | ||
|
|
cf8147d6ca | ||
|
|
f28f6b1530 | ||
|
|
cb4ea71e81 | ||
|
|
fe94d3781a | ||
|
|
7c17b39dad | ||
|
|
73dab4c651 | ||
|
|
5d72843167 | ||
|
|
f43df47cff | ||
|
|
aaca26770b | ||
|
|
4a124d5bd8 | ||
|
|
03cd52a01a | ||
|
|
da79181b00 | ||
|
|
a405104052 | ||
|
|
56fdaa65d2 | ||
|
|
9d60c49c9f | ||
|
|
380170d5ba | ||
|
|
4b450f2091 | ||
|
|
1b013fbf1c | ||
|
|
bf7459bd2d |
@@ -126,6 +126,15 @@
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "eltociear",
|
||||
"name": "Ikko Ashimine",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/22633385?v=4",
|
||||
"profile": "https://bandism.net/",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
8
.devcontainer/Dockerfile
Normal file
8
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/javascript-node/.devcontainer/base.Dockerfile
|
||||
|
||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||
ARG VARIANT="18-bullseye"
|
||||
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y doxygen
|
||||
26
.devcontainer/devcontainer.json
Normal file
26
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,26 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||
// https://github.com/microsoft/vscode-dev-containers/tree/v0.233.0/containers/typescript-node
|
||||
{
|
||||
"name": "Node.js & TypeScript",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
// Update 'VARIANT' to pick a Node version: 18, 16, 14.
|
||||
// Append -bullseye or -buster to pin to an OS version.
|
||||
// Use -bullseye variants on local on arm64/Apple Silicon.
|
||||
"args": {
|
||||
"VARIANT": "16-bullseye"
|
||||
}
|
||||
},
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"SASjs.sasjs-for-vscode"
|
||||
],
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "npm i && npm i -g @sasjs/cli",
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node"
|
||||
}
|
||||
@@ -6,7 +6,6 @@ sasjs/
|
||||
.github/
|
||||
.git-hooks/
|
||||
.vscode/
|
||||
main.dox
|
||||
make_singlefile.sh
|
||||
*.md
|
||||
.all-contributorsrc
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
[dependency-url]:https://github.com/sasjs/core/blob/main/package.json
|
||||
|
||||
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/CONTRIBUTING.md) are welcomed.
|
||||
Much quality. Many standards. The **Macro Core** library exists to save time and development effort! Herein ye shall find a veritable host of MIT-licenced, production quality SAS macros. These are a mix of tools, utilities, functions and code generators that are useful in the context of [Application Development](https://sasapps.io) on the SAS platform (eg https://datacontroller.io). [Contributions](https://github.com/sasjs/core/blob/main/.github/CONTRIBUTING.md) are welcome.
|
||||
|
||||
You can download and compile them all in just two lines of SAS code:
|
||||
|
||||
@@ -224,12 +224,13 @@ The following repositories are also worth checking out:
|
||||
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
|
||||
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
|
||||
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)
|
||||
* [rogerjdeangelis](https://github.com/rogerjdeangelis)
|
||||
* [scottbass/sas](https://github.com/scottbass/SAS)
|
||||
* [yabwon/sas_packages](https://github.com/yabwon/SAS_PACKAGES)
|
||||
|
||||
## Contributors ✨
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors-)
|
||||
[](#contributors-)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||
|
||||
@@ -251,6 +252,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://github.com/VladislavParhomchik"><img src="https://avatars.githubusercontent.com/u/83717836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Parhomchik</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=VladislavParhomchik" title="Tests">⚠️</a> <a href="https://github.com/sasjs/core/pulls?q=is%3Apr+reviewed-by%3AVladislavParhomchik" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/vznesh"><img src="https://avatars.githubusercontent.com/u/28916792?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vignesh T.</b></sub></a><br /><a href="https://github.com/sasjs/core/issues?q=author%3Avznesh" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/yabwon"><img src="https://avatars.githubusercontent.com/u/9314894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bart Jablonski</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=yabwon" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://bandism.net/"><img src="https://avatars.githubusercontent.com/u/22633385?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ikko Ashimine</b></sub></a><br /><a href="https://github.com/sasjs/core/commits?author=eltociear" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
603
all.sas
603
all.sas
@@ -198,6 +198,12 @@ options noquotelenmax;
|
||||
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %if &feature=EXPORTXLS %then %do;
|
||||
/* is it possible to PROC EXPORT an excel file? */
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 1;
|
||||
%else %if %sysfunc(sysprod(SAS/ACCESS Interface to PC Files)) = 1 %then 1;
|
||||
%else 0;
|
||||
%end;
|
||||
%else %do;
|
||||
-1
|
||||
%put &sysmacroname: &feature not found;
|
||||
@@ -369,6 +375,48 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
||||
%mend mf_existvarlist;
|
||||
|
||||
/** @endcond *//**
|
||||
@file
|
||||
@brief Returns E8601DT26.6 if compatible else DATETIME19.3
|
||||
@details From our experience in [Data Controller for SAS]
|
||||
(https://datacontroller.io) deployments, the E8601DT26.6 datetime format has
|
||||
the widest support when it comes to pass-through SQL queries.
|
||||
|
||||
However, it is not supported in WPS or early versions of SAS 9 (M3 and below)
|
||||
when used as a datetime literal, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
This macro will therefore return DATEITME19.3 as an alternative format
|
||||
based on the runtime environment so that it can be used in such cases, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_fmtdttm.test.sas
|
||||
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_fmtdttm(
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "&sysver"="9.2" or "&sysver"="9.3"
|
||||
or ("&sysver"="9.4" and "%substr(&SYSVLONG,9,1)" le "3")
|
||||
or "%substr(&sysver,1,1)"="4"
|
||||
or "%substr(&sysver,1,1)"="5"
|
||||
%then %do;DATETIME19.3%end;
|
||||
%else %do;E8601DT26.6%end;
|
||||
|
||||
%mend mf_fmtdttm;
|
||||
|
||||
|
||||
/**
|
||||
@file
|
||||
@brief Returns the appLoc from the _program variable
|
||||
@details When working with SASjs apps, web services / tests / jobs are always
|
||||
@@ -3833,8 +3881,11 @@ drop table &ddlds,&cntlds;
|
||||
|
||||
%mp_copyfolder(&rootdir,©dir)
|
||||
|
||||
@param source Unquoted path to the folder to copy from.
|
||||
@param target Unquoted path to the folder to copy to.
|
||||
@param [in] source Unquoted path to the folder to copy from.
|
||||
@param [out] target Unquoted path to the folder to copy to.
|
||||
@param [in] copymax=(MAX) Set to a positive integer to indicate the level of
|
||||
subdirectory copy recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
||||
recursion, set to MAX.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
@@ -3848,7 +3899,7 @@ drop table &ddlds,&cntlds;
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_copyfolder(source,target);
|
||||
%macro mp_copyfolder(source,target,copymax=MAX);
|
||||
|
||||
%mp_abort(iftrue=(%mf_isdir(&source)=0)
|
||||
,mac=&sysmacroname
|
||||
@@ -3867,7 +3918,7 @@ drop table &ddlds,&cntlds;
|
||||
%let tempds=%mf_getuniquename();
|
||||
|
||||
/* recursive directory listing */
|
||||
%mp_dirlist(path=&source,outds=work.&tempds, maxdepth=MAX)
|
||||
%mp_dirlist(path=&source,outds=work.&tempds,maxdepth=©max)
|
||||
|
||||
/* create folders and copy content */
|
||||
data _null_;
|
||||
@@ -3895,7 +3946,8 @@ drop table &ddlds,&cntlds;
|
||||
proc sql;
|
||||
drop table work.&tempds;
|
||||
|
||||
%mend mp_copyfolder;/**
|
||||
%mend mp_copyfolder;
|
||||
/**
|
||||
@file
|
||||
@brief Create the permanent Core tables
|
||||
@details Several macros in the [core](https://github.com/sasjs/core) library
|
||||
@@ -3962,10 +4014,12 @@ proc sql;
|
||||
%end;
|
||||
|
||||
%if &libds=0 %then %do;
|
||||
proc sql;
|
||||
describe table &syslast;
|
||||
drop table &syslast;
|
||||
%end;
|
||||
%mend mp_coretable;/**
|
||||
%mend mp_coretable;
|
||||
/**
|
||||
@file mp_createconstraints.sas
|
||||
@brief Creates constraints
|
||||
@details Takes the output from mp_getconstraints.sas as input
|
||||
@@ -4740,7 +4794,7 @@ drop table &out_ds;
|
||||
|
||||
Usage:
|
||||
|
||||
%mp_ds2cards(base_ds=sashelp.class
|
||||
%mp_ds2cards(sashelp.class
|
||||
, tgt_ds=work.class
|
||||
, cards_file= "C:\temp\class.sas"
|
||||
, showlog=NO
|
||||
@@ -4752,7 +4806,7 @@ drop table &out_ds;
|
||||
- explicity setting a unix LF
|
||||
- constraints / indexes etc
|
||||
|
||||
@param [in] base_ds= Should be two level - eg work.blah. This is the table
|
||||
@param [in] base_ds Should be two level - eg work.blah. This is the table
|
||||
that is converted to a cards file.
|
||||
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||
Optional - if omitted, will be same as BASE_DS.
|
||||
@@ -5599,6 +5653,11 @@ run;
|
||||
%local vars;
|
||||
%let vars=%upcase(%mf_getvarlist(&libds));
|
||||
|
||||
%if %trim(X&vars)=X %then %do;
|
||||
%put &sysmacroname: Table &libds has no columns!!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* create the header row */
|
||||
data _null_;
|
||||
file &outref;
|
||||
@@ -5850,7 +5909,36 @@ data &outds;
|
||||
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
|
||||
OPERATOR_NM $10 RAW_VALUE $4000;*/
|
||||
set &inds;
|
||||
length reason_cd $4032;
|
||||
length reason_cd $4032 vtype $1 vnum dsid 8;
|
||||
|
||||
/* quick check to ensure column exists */
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
|
||||
/* need to open the dataset to get the column type */
|
||||
dsid=open("&targetds","i");
|
||||
if dsid>0 then do;
|
||||
vnum=varnum(dsid,VARIABLE_NM);
|
||||
if vnum<1 then do;
|
||||
/* should not happen as was also tested for above */
|
||||
REASON_CD=cats("Variable (",VARIABLE_NM,") not found in &targetds");
|
||||
putlog REASON_CD= dsid=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
/* now we can get the type */
|
||||
else vtype=vartype(dsid,vnum);
|
||||
end;
|
||||
|
||||
/* closed list checks */
|
||||
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||
@@ -5874,17 +5962,8 @@ data &outds;
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
|
||||
('=','>','<','<=','>=','NE','GE','LE','BETWEEN','IN','NOT IN','CONTAINS')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
@@ -5893,6 +5972,18 @@ data &outds;
|
||||
output;
|
||||
end;
|
||||
|
||||
/* special missing logic */
|
||||
if vtype='N'
|
||||
and OPERATOR_NM in ('=','>','<','<=','>=','NE','GE','LE')
|
||||
and cats(upcase(raw_value)) in (
|
||||
'.','.A','.B','.C','.D','.E','.F','.G','.H','.I','.J','.K','.L','.M','.N'
|
||||
'.N','.O','.P','.Q','.R','.S','.T','.U','.V','.W','.X','.Y','.Z','._'
|
||||
)
|
||||
then do;
|
||||
/* valid numeric - exit data step loop */
|
||||
return;
|
||||
end;
|
||||
|
||||
/* special logic */
|
||||
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ','');
|
||||
else if OPERATOR_NM in ('IN','NOT IN') then do;
|
||||
@@ -6046,6 +6137,9 @@ filename &outref temp;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
proc sort data=&inds;
|
||||
by SUBGROUP_ID;
|
||||
run;
|
||||
data _null_;
|
||||
file &outref lrecl=32800;
|
||||
set &inds end=last;
|
||||
@@ -6528,6 +6622,22 @@ drop table &dropds;
|
||||
%let lib=%upcase(&lib);
|
||||
%let ds=%upcase(&ds);
|
||||
|
||||
/**
|
||||
* Cater for environments where sashelp.vcncolu is not available
|
||||
*/
|
||||
%if %sysfunc(exist(sashelp.vcncolu,view))=0 %then %do;
|
||||
proc sql;
|
||||
create table &outds(
|
||||
libref char(8)
|
||||
,TABLE_NAME char(32)
|
||||
,constraint_type char(8) label='Constraint Type'
|
||||
,constraint_name char(32) label='Constraint Name'
|
||||
,column_name char(32) label='Column'
|
||||
,constraint_order num
|
||||
);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Neither dictionary tables nor sashelp provides a constraint order column,
|
||||
* however they DO arrive in the correct order. So, create the col.
|
||||
@@ -8290,7 +8400,7 @@ run;
|
||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||
data &outds;
|
||||
length hashkey $32;
|
||||
retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
hashkey=put(md5("&salt"),$hex32.);
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
@@ -8301,9 +8411,14 @@ run;
|
||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||
%end;
|
||||
%else %do;
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar)/nonote2err;
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar)
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
/nonote2err
|
||||
%end;
|
||||
;
|
||||
length &prevkeyvar &keyvar $32;
|
||||
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
retain &prevkeyvar;
|
||||
if _n_=1 then &prevkeyvar=put(md5("&salt"),$hex32.);
|
||||
set &libds end=&lastvar;
|
||||
/* hash should include previous row */
|
||||
&keyvar=%mp_md5(
|
||||
@@ -9259,6 +9374,7 @@ options ibufsize=&ibufsize;
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_fmtdttm.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_lockfilecheck.sas
|
||||
@li mf_getuser.sas
|
||||
@@ -9348,7 +9464,7 @@ run;
|
||||
LOCK_LIB ="&lib";
|
||||
LOCK_DS="&ds";
|
||||
LOCK_STATUS_CD='LOCKED';
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
LOCK_USER_NM="&user";
|
||||
LOCK_PID="&sysjobid";
|
||||
LOCK_REF="&ref";
|
||||
@@ -9368,7 +9484,7 @@ run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='LOCKED'
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
@@ -9443,7 +9559,7 @@ run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='UNLOCKED'
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
@@ -10161,17 +10277,21 @@ https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-s
|
||||
%macro mp_resetoption(option /* the option to reset */
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data _null_;
|
||||
length code $1500;
|
||||
startup=getoption("&option",'startupvalue');
|
||||
current=getoption("&option");
|
||||
if startup ne current then do;
|
||||
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
||||
putlog "NOTE: Resetting system option: " code ;
|
||||
call execute(code );
|
||||
end;
|
||||
run;
|
||||
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
data _null_;
|
||||
length code $1500;
|
||||
startup=getoption("&option",'startupvalue');
|
||||
current=getoption("&option");
|
||||
if startup ne current then do;
|
||||
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
||||
putlog "NOTE: Resetting system option: " code ;
|
||||
call execute(code );
|
||||
end;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname: reset option feature unavailable on &sysvlong;
|
||||
%end;
|
||||
%mend mp_resetoption;/**
|
||||
@file
|
||||
@brief Generate and apply retained key values to a staging table
|
||||
@@ -10232,6 +10352,7 @@ run;
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existvar.sas
|
||||
@li mf_fmtdttm.sas
|
||||
@li mf_getquotedstr.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_nobs.sas
|
||||
@@ -10391,12 +10512,12 @@ quit;
|
||||
set keytable="&base_libds"
|
||||
,keycolumn="&retained_key"
|
||||
,max_key=%eval(&maxkey+&newkey_cnt)
|
||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
%end;
|
||||
%else %do;
|
||||
update &maxkeytable
|
||||
set max_key=%eval(&maxkey+&newkey_cnt)
|
||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
where keytable="&base_libds";
|
||||
%end;
|
||||
%mp_lockanytable(UNLOCK
|
||||
@@ -12980,11 +13101,21 @@ ods package close;
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
newval_char char(32765) label='New (character) value'
|
||||
);
|
||||
|
||||
%local lib;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create
|
||||
pk_mpe_audit=(load_ref libref dsn key_hash tgtvar_nm)
|
||||
/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_difftable;/**
|
||||
@file
|
||||
@brief Filtertable DDL
|
||||
@@ -12996,21 +13127,34 @@ ods package close;
|
||||
|
||||
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
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)
|
||||
filter_hash char(32) &nn,
|
||||
filter_line num &nn,
|
||||
group_logic char(3) &nn,
|
||||
subgroup_logic char(3) &nn,
|
||||
subgroup_id num &nn,
|
||||
variable_nm varchar(32) &nn,
|
||||
operator_nm varchar(12) &nn,
|
||||
raw_value varchar(4000) &nn,
|
||||
processed_dttm num &nn format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create pk_mpe_filterdetail=(filter_hash filter_line)/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_filterdetail;/**
|
||||
@file
|
||||
@brief Filtersummary DDL
|
||||
@@ -13022,16 +13166,29 @@ ods package close;
|
||||
|
||||
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
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)
|
||||
filter_rk num &nn,
|
||||
filter_hash char(32) &nn,
|
||||
filter_table char(41) &nn,
|
||||
processed_dttm num &nn format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create filter_rk /nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_filtersummary;/**
|
||||
@file
|
||||
@brief Locktable DDL
|
||||
@@ -13043,19 +13200,35 @@ ods package close;
|
||||
|
||||
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_status_cd char(10) &nn,
|
||||
lock_user_nm char(100) &nn ,
|
||||
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)
|
||||
lock_end_dttm num format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create
|
||||
pk_mp_lockanytable=(lock_lib lock_ds)
|
||||
/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_locktable;/**
|
||||
@file
|
||||
@brief Maxkeytable DDL
|
||||
@@ -13075,9 +13248,18 @@ ods package close;
|
||||
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));
|
||||
label='Datetime this value was last updated'
|
||||
);
|
||||
|
||||
%local lib;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create keytable /nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_maxkeytable;/**
|
||||
@file
|
||||
@@ -13095,8 +13277,8 @@ proc sql;
|
||||
create table &libds(
|
||||
FMTNAME char(32) label='Format name'
|
||||
/*
|
||||
to accomodate larger START values, mp_loadformat.sas will need the
|
||||
SQL dependency removed (proc sql needs to accomodate 3 index values in
|
||||
to accommodate larger START values, mp_loadformat.sas will need the
|
||||
SQL dependency removed (proc sql needs to accommodate 3 index values in
|
||||
a 32767 ibufsize limit)
|
||||
*/
|
||||
,START char(10000) label='Starting value for format'
|
||||
@@ -13121,7 +13303,8 @@ create table &libds(
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
|
||||
%mend mddl_sas_cntlout;/**
|
||||
%mend mddl_sas_cntlout;
|
||||
/**
|
||||
@file mm_adduser2group.sas
|
||||
@brief Adds a user to a group
|
||||
@details Adds a user to a metadata group. The macro first checks whether the
|
||||
@@ -19275,8 +19458,8 @@ data _null_;
|
||||
file &fname1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put "Authorization: Bearer " _infile_;
|
||||
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -19317,6 +19500,155 @@ run;
|
||||
options &optval;
|
||||
|
||||
%mend ms_createfile;
|
||||
/**
|
||||
@file
|
||||
@brief Creates a group on SASjs Server
|
||||
@details Creates a group on SASjs Server with the following attributes:
|
||||
|
||||
@li name
|
||||
@li description
|
||||
@li isActive
|
||||
|
||||
Examples:
|
||||
|
||||
%ms_creategroup(mynewgroup)
|
||||
|
||||
%ms_creategroup(mynewergroup, desc=The group description)
|
||||
|
||||
@param [in] groupname The group name to create. No spaces or special chars.
|
||||
@param [in] desc= (0) If no description provided, group name will be used.
|
||||
@param [in] isactive= (true) Set to false to create an inactive group.
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [out] outds= (work.ms_creategroup) This output dataset will contain the
|
||||
values from the JSON response (such as the id of the new group)
|
||||
|DESCRIPTION:$1.|GROUPID:best.|ISACTIVE:best.|NAME:$11.|
|
||||
|---|---|---|---|
|
||||
|`The group description`|`2 `|`1 `|`mynewergroup `|
|
||||
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_creategroup.test.sas
|
||||
@li ms_getgroups.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_creategroup(groupname
|
||||
,desc=0
|
||||
,isactive=true
|
||||
,outds=work.ms_creategroup
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_creategroup.sas
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
|
||||
%local fref0 fref1 fref2 libref optval rc msg;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
%let libref=%mf_getuniquelibref();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fref0 termstr=crlf;
|
||||
name=quote(cats(symget('groupname')));
|
||||
description=quote(cats(symget('desc')));
|
||||
if cats(description)='"0"' then description=name;
|
||||
isactive=symget('isactive');
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _all_;
|
||||
%end;
|
||||
|
||||
put '{'@;
|
||||
put '"name":' name @;
|
||||
put ',"description":' description @;
|
||||
put ',"isActive":' isactive @;
|
||||
put '}';
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &fref1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then do;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
end;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fref0;
|
||||
input;
|
||||
put _infile_;
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='POST' in=&fref0 headerin=&fref1 out=&fref2
|
||||
url="&_sasjs_apiserverurl/SASjsApi/group";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_creategroup.sas
|
||||
,msg=%str(Issue submitting query to SASjsApi/group)
|
||||
)
|
||||
|
||||
libname &libref JSON fileref=&fref2;
|
||||
|
||||
data &outds;
|
||||
set &libref..root;
|
||||
drop ordinal_root;
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _all_;
|
||||
%end;
|
||||
run;
|
||||
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_creategroup.sas
|
||||
,msg=%str(Issue reading response JSON)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fref0 clear;
|
||||
filename &fref1 clear;
|
||||
filename &fref2 clear;
|
||||
libname &libref clear;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
infile &fref2;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend ms_creategroup;
|
||||
/**
|
||||
@file
|
||||
@brief Creates a user on SASjs Server
|
||||
@@ -19406,9 +19738,11 @@ data _null_;
|
||||
file &fref1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
if _n_=1 then do;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
end;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -20033,7 +20367,7 @@ data _null_;
|
||||
file &headref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
proc http method='DELETE' headerin=&headref
|
||||
@@ -20082,7 +20416,7 @@ data _null_;
|
||||
file &headref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
proc http method='GET' out=&binaryfref headerin=&headref
|
||||
@@ -20096,6 +20430,111 @@ filename &binaryfref clear;
|
||||
filename &headref clear;
|
||||
|
||||
%mend ms_getfile;/**
|
||||
@file
|
||||
@brief Fetches the list of groups from SASjs Server
|
||||
@details Fetches the list of groups from SASjs Server and writes them to an
|
||||
output dataset.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_getgroups(outds=userlist)
|
||||
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [out] outds= (work.ms_getgroups) This output dataset will contain the
|
||||
list of groups. Format:
|
||||
|NAME:$32.|DESCRIPTION:$64.|GROUPID:best.|
|
||||
|---|---|---|
|
||||
|`SomeGroup `|`A group `|`1`|
|
||||
|`Another Group`|`this is a different group`|`2`|
|
||||
|`admin`|`Administrators `|`3`|
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_creategroup.sas
|
||||
@li ms_getusers.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_getgroups(
|
||||
outds=work.ms_getgroups
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
|
||||
%local fref0 fref1 libref optval rc msg;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let libref=%mf_getuniquelibref();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fref0 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then put "accept: application/json";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fref0;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='GET' headerin=&fref0 out=&fref1
|
||||
url="&_sasjs_apiserverurl/SASjsApi/group";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getgroups.sas
|
||||
,msg=%str(Issue submitting GET query to SASjsApi/group)
|
||||
)
|
||||
|
||||
libname &libref JSON fileref=&fref1;
|
||||
|
||||
data &outds;
|
||||
length NAME $32 DESCRIPTION $64. GROUPID 8;
|
||||
if _n_=1 then call missing(of _all_);
|
||||
set &libref..root;
|
||||
drop ordinal_root;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(Issue reading response JSON)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
filename &fref0 clear;
|
||||
filename &fref1 clear;
|
||||
libname &libref clear;
|
||||
%end;
|
||||
|
||||
%mend ms_getgroups;
|
||||
/**
|
||||
@file
|
||||
@brief Fetches the list of users from SASjs Server
|
||||
@details Fetches the list of users from SASjs Server and writes them to an
|
||||
@@ -20153,8 +20592,8 @@ data _null_;
|
||||
file &fref0 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put "accept: application/json";
|
||||
if _n_=1 then put "accept: application/json";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -20341,8 +20780,8 @@ data _null_;
|
||||
file &authref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put 'Authorization: Bearer ' _infile_;
|
||||
put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -20366,6 +20805,7 @@ proc http method='POST' headerin=&authref in=&mainref out=&outref
|
||||
%end;
|
||||
run;
|
||||
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
or &mdebug=1
|
||||
%then %do;
|
||||
data _null_;infile &outref;input;putlog _infile_;run;
|
||||
%end;
|
||||
@@ -20381,7 +20821,7 @@ options &optval;
|
||||
%if &outlogds ne _null_ or &mdebug=1 %then %do;
|
||||
%local dumplib;
|
||||
%let dumplib=%mf_getuniquelibref();
|
||||
libname &dumplib json (&outref);
|
||||
libname &dumplib json fileref=&outref;
|
||||
data &outlogds;
|
||||
set &dumplib..log;
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -20399,7 +20839,8 @@ options &optval;
|
||||
filename &authref;
|
||||
filename &mainref;
|
||||
%end;
|
||||
%mend ms_runstp;/**
|
||||
%mend ms_runstp;
|
||||
/**
|
||||
@file
|
||||
@brief Will execute a SASjs web service on SASjs Server
|
||||
@details Prepares the input files and retrieves the resulting datasets from
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
%else %if "&SYSVLONG" < "9.04.01M3" %then 0;
|
||||
%else 1;
|
||||
%end;
|
||||
%else %if &feature=EXPORTXLS %then %do;
|
||||
/* is it possible to PROC EXPORT an excel file? */
|
||||
%if "%substr(&sysver,1,1)"="4" or "%substr(&sysver,1,1)"="5" %then 1;
|
||||
%else %if %sysfunc(sysprod(SAS/ACCESS Interface to PC Files)) = 1 %then 1;
|
||||
%else 0;
|
||||
%end;
|
||||
%else %do;
|
||||
-1
|
||||
%put &sysmacroname: &feature not found;
|
||||
|
||||
42
base/mf_fmtdttm.sas
Normal file
42
base/mf_fmtdttm.sas
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
@file
|
||||
@brief Returns E8601DT26.6 if compatible else DATETIME19.3
|
||||
@details From our experience in [Data Controller for SAS]
|
||||
(https://datacontroller.io) deployments, the E8601DT26.6 datetime format has
|
||||
the widest support when it comes to pass-through SQL queries.
|
||||
|
||||
However, it is not supported in WPS or early versions of SAS 9 (M3 and below)
|
||||
when used as a datetime literal, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
This macro will therefore return DATEITME19.3 as an alternative format
|
||||
based on the runtime environment so that it can be used in such cases, eg:
|
||||
|
||||
data _null_;
|
||||
demo="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
demo=;
|
||||
run;
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mf_fmtdttm.test.sas
|
||||
|
||||
@author Allan Bowe
|
||||
**/
|
||||
|
||||
%macro mf_fmtdttm(
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%if "&sysver"="9.2" or "&sysver"="9.3"
|
||||
or ("&sysver"="9.4" and "%substr(&SYSVLONG,9,1)" le "3")
|
||||
or "%substr(&sysver,1,1)"="4"
|
||||
or "%substr(&sysver,1,1)"="5"
|
||||
%then %do;DATETIME19.3%end;
|
||||
%else %do;E8601DT26.6%end;
|
||||
|
||||
%mend mf_fmtdttm;
|
||||
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
%mp_copyfolder(&rootdir,©dir)
|
||||
|
||||
@param source Unquoted path to the folder to copy from.
|
||||
@param target Unquoted path to the folder to copy to.
|
||||
@param [in] source Unquoted path to the folder to copy from.
|
||||
@param [out] target Unquoted path to the folder to copy to.
|
||||
@param [in] copymax=(MAX) Set to a positive integer to indicate the level of
|
||||
subdirectory copy recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
||||
recursion, set to MAX.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
@@ -31,7 +34,7 @@
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_copyfolder(source,target);
|
||||
%macro mp_copyfolder(source,target,copymax=MAX);
|
||||
|
||||
%mp_abort(iftrue=(%mf_isdir(&source)=0)
|
||||
,mac=&sysmacroname
|
||||
@@ -50,7 +53,7 @@
|
||||
%let tempds=%mf_getuniquename();
|
||||
|
||||
/* recursive directory listing */
|
||||
%mp_dirlist(path=&source,outds=work.&tempds, maxdepth=MAX)
|
||||
%mp_dirlist(path=&source,outds=work.&tempds,maxdepth=©max)
|
||||
|
||||
/* create folders and copy content */
|
||||
data _null_;
|
||||
@@ -78,4 +81,4 @@
|
||||
proc sql;
|
||||
drop table work.&tempds;
|
||||
|
||||
%mend mp_copyfolder;
|
||||
%mend mp_copyfolder;
|
||||
|
||||
@@ -65,7 +65,8 @@ proc sql;
|
||||
%end;
|
||||
|
||||
%if &libds=0 %then %do;
|
||||
proc sql;
|
||||
describe table &syslast;
|
||||
drop table &syslast;
|
||||
%end;
|
||||
%mend mp_coretable;
|
||||
%mend mp_coretable;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
Usage:
|
||||
|
||||
%mp_ds2cards(base_ds=sashelp.class
|
||||
%mp_ds2cards(sashelp.class
|
||||
, tgt_ds=work.class
|
||||
, cards_file= "C:\temp\class.sas"
|
||||
, showlog=NO
|
||||
@@ -23,7 +23,7 @@
|
||||
- explicity setting a unix LF
|
||||
- constraints / indexes etc
|
||||
|
||||
@param [in] base_ds= Should be two level - eg work.blah. This is the table
|
||||
@param [in] base_ds Should be two level - eg work.blah. This is the table
|
||||
that is converted to a cards file.
|
||||
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||
Optional - if omitted, will be same as BASE_DS.
|
||||
|
||||
@@ -57,6 +57,11 @@
|
||||
%local vars;
|
||||
%let vars=%upcase(%mf_getvarlist(&libds));
|
||||
|
||||
%if %trim(X&vars)=X %then %do;
|
||||
%put &sysmacroname: Table &libds has no columns!!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* create the header row */
|
||||
data _null_;
|
||||
file &outref;
|
||||
|
||||
@@ -92,7 +92,36 @@ data &outds;
|
||||
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
|
||||
OPERATOR_NM $10 RAW_VALUE $4000;*/
|
||||
set &inds;
|
||||
length reason_cd $4032;
|
||||
length reason_cd $4032 vtype $1 vnum dsid 8;
|
||||
|
||||
/* quick check to ensure column exists */
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
|
||||
/* need to open the dataset to get the column type */
|
||||
dsid=open("&targetds","i");
|
||||
if dsid>0 then do;
|
||||
vnum=varnum(dsid,VARIABLE_NM);
|
||||
if vnum<1 then do;
|
||||
/* should not happen as was also tested for above */
|
||||
REASON_CD=cats("Variable (",VARIABLE_NM,") not found in &targetds");
|
||||
putlog REASON_CD= dsid=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
return;
|
||||
end;
|
||||
/* now we can get the type */
|
||||
else vtype=vartype(dsid,vnum);
|
||||
end;
|
||||
|
||||
/* closed list checks */
|
||||
if GROUP_LOGIC not in ('AND','OR') then do;
|
||||
@@ -116,17 +145,8 @@ data &outds;
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if upcase(VARIABLE_NM) not in
|
||||
(%upcase(%mf_getvarlist(&targetds,dlm=%str(,),quote=SINGLE)))
|
||||
then do;
|
||||
REASON_CD="Variable "!!cats(variable_nm)!!" not in &targetds";
|
||||
putlog REASON_CD= VARIABLE_NM=;
|
||||
call symputx('reason_cd',reason_cd,'l');
|
||||
call symputx('nobs',_n_,'l');
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
|
||||
('=','>','<','<=','>=','NE','GE','LE','BETWEEN','IN','NOT IN','CONTAINS')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
@@ -135,6 +155,18 @@ data &outds;
|
||||
output;
|
||||
end;
|
||||
|
||||
/* special missing logic */
|
||||
if vtype='N'
|
||||
and OPERATOR_NM in ('=','>','<','<=','>=','NE','GE','LE')
|
||||
and cats(upcase(raw_value)) in (
|
||||
'.','.A','.B','.C','.D','.E','.F','.G','.H','.I','.J','.K','.L','.M','.N'
|
||||
'.N','.O','.P','.Q','.R','.S','.T','.U','.V','.W','.X','.Y','.Z','._'
|
||||
)
|
||||
then do;
|
||||
/* valid numeric - exit data step loop */
|
||||
return;
|
||||
end;
|
||||
|
||||
/* special logic */
|
||||
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ','');
|
||||
else if OPERATOR_NM in ('IN','NOT IN') then do;
|
||||
|
||||
@@ -84,6 +84,9 @@ filename &outref temp;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
proc sort data=&inds;
|
||||
by SUBGROUP_ID;
|
||||
run;
|
||||
data _null_;
|
||||
file &outref lrecl=32800;
|
||||
set &inds end=last;
|
||||
|
||||
@@ -40,6 +40,22 @@
|
||||
%let lib=%upcase(&lib);
|
||||
%let ds=%upcase(&ds);
|
||||
|
||||
/**
|
||||
* Cater for environments where sashelp.vcncolu is not available
|
||||
*/
|
||||
%if %sysfunc(exist(sashelp.vcncolu,view))=0 %then %do;
|
||||
proc sql;
|
||||
create table &outds(
|
||||
libref char(8)
|
||||
,TABLE_NAME char(32)
|
||||
,constraint_type char(8) label='Constraint Type'
|
||||
,constraint_name char(32) label='Constraint Name'
|
||||
,column_name char(32) label='Column'
|
||||
,constraint_order num
|
||||
);
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/**
|
||||
* Neither dictionary tables nor sashelp provides a constraint order column,
|
||||
* however they DO arrive in the correct order. So, create the col.
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
%if %mf_getattrn(&libds,NLOBS)=0 %then %do;
|
||||
data &outds;
|
||||
length hashkey $32;
|
||||
retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
hashkey=put(md5("&salt"),$hex32.);
|
||||
output;
|
||||
stop;
|
||||
run;
|
||||
@@ -69,9 +69,14 @@
|
||||
%put %str(ERR)OR: Dataset &libds is not a dataset;
|
||||
%end;
|
||||
%else %do;
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar)/nonote2err;
|
||||
data &outds(rename=(&keyvar=hashkey) keep=&keyvar)
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
/nonote2err
|
||||
%end;
|
||||
;
|
||||
length &prevkeyvar &keyvar $32;
|
||||
retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)";
|
||||
retain &prevkeyvar;
|
||||
if _n_=1 then &prevkeyvar=put(md5("&salt"),$hex32.);
|
||||
set &libds end=&lastvar;
|
||||
/* hash should include previous row */
|
||||
&keyvar=%mp_md5(
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_fmtdttm.sas
|
||||
@li mp_abort.sas
|
||||
@li mp_lockfilecheck.sas
|
||||
@li mf_getuser.sas
|
||||
@@ -111,7 +112,7 @@ run;
|
||||
LOCK_LIB ="&lib";
|
||||
LOCK_DS="&ds";
|
||||
LOCK_STATUS_CD='LOCKED';
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
LOCK_USER_NM="&user";
|
||||
LOCK_PID="&sysjobid";
|
||||
LOCK_REF="&ref";
|
||||
@@ -131,7 +132,7 @@ run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='LOCKED'
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
@@ -206,7 +207,7 @@ run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='UNLOCKED'
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
|
||||
@@ -21,15 +21,19 @@ https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-s
|
||||
%macro mp_resetoption(option /* the option to reset */
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data _null_;
|
||||
length code $1500;
|
||||
startup=getoption("&option",'startupvalue');
|
||||
current=getoption("&option");
|
||||
if startup ne current then do;
|
||||
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
||||
putlog "NOTE: Resetting system option: " code ;
|
||||
call execute(code );
|
||||
end;
|
||||
run;
|
||||
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
data _null_;
|
||||
length code $1500;
|
||||
startup=getoption("&option",'startupvalue');
|
||||
current=getoption("&option");
|
||||
if startup ne current then do;
|
||||
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
||||
putlog "NOTE: Resetting system option: " code ;
|
||||
call execute(code );
|
||||
end;
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
%put &sysmacroname: reset option feature unavailable on &sysvlong;
|
||||
%end;
|
||||
%mend mp_resetoption;
|
||||
@@ -58,6 +58,7 @@
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_existvar.sas
|
||||
@li mf_fmtdttm.sas
|
||||
@li mf_getquotedstr.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_nobs.sas
|
||||
@@ -217,12 +218,12 @@ quit;
|
||||
set keytable="&base_libds"
|
||||
,keycolumn="&retained_key"
|
||||
,max_key=%eval(&maxkey+&newkey_cnt)
|
||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt;
|
||||
%end;
|
||||
%else %do;
|
||||
update &maxkeytable
|
||||
set max_key=%eval(&maxkey+&newkey_cnt)
|
||||
,processed_dttm="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
,processed_dttm="%sysfunc(datetime(),%mf_fmtdttm())"dt
|
||||
where keytable="&base_libds";
|
||||
%end;
|
||||
%mp_lockanytable(UNLOCK
|
||||
|
||||
@@ -26,9 +26,19 @@
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
newval_char char(32765) label='New (character) value'
|
||||
);
|
||||
|
||||
%local lib;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create
|
||||
pk_mpe_audit=(load_ref libref dsn key_hash tgtvar_nm)
|
||||
/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_difftable;
|
||||
@@ -9,19 +9,32 @@
|
||||
|
||||
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
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)
|
||||
filter_hash char(32) &nn,
|
||||
filter_line num &nn,
|
||||
group_logic char(3) &nn,
|
||||
subgroup_logic char(3) &nn,
|
||||
subgroup_id num &nn,
|
||||
variable_nm varchar(32) &nn,
|
||||
operator_nm varchar(12) &nn,
|
||||
raw_value varchar(4000) &nn,
|
||||
processed_dttm num &nn format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create pk_mpe_filterdetail=(filter_hash filter_line)/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_filterdetail;
|
||||
@@ -9,14 +9,27 @@
|
||||
|
||||
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
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)
|
||||
filter_rk num &nn,
|
||||
filter_hash char(32) &nn,
|
||||
filter_table char(41) &nn,
|
||||
processed_dttm num &nn format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create filter_rk /nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_filtersummary;
|
||||
@@ -9,17 +9,33 @@
|
||||
|
||||
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
||||
|
||||
%local nn lib;
|
||||
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
||||
%let nn=not null;
|
||||
%end;
|
||||
%else %let nn=;
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_status_cd char(10) &nn,
|
||||
lock_user_nm char(100) &nn ,
|
||||
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)
|
||||
lock_end_dttm num format=E8601DT26.6
|
||||
);
|
||||
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create
|
||||
pk_mp_lockanytable=(lock_lib lock_ds)
|
||||
/nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_locktable;
|
||||
@@ -17,8 +17,17 @@
|
||||
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));
|
||||
label='Datetime this value was last updated'
|
||||
);
|
||||
|
||||
%local lib;
|
||||
%let libds=%upcase(&libds);
|
||||
%if %index(&libds,.)=0 %then %let lib=WORK;
|
||||
%else %let lib=%scan(&libds,1,.);
|
||||
|
||||
proc datasets lib=&lib noprint;
|
||||
modify %scan(&libds,-1,.);
|
||||
index create keytable /nomiss unique;
|
||||
quit;
|
||||
|
||||
%mend mddl_dc_maxkeytable;
|
||||
@@ -14,8 +14,8 @@ proc sql;
|
||||
create table &libds(
|
||||
FMTNAME char(32) label='Format name'
|
||||
/*
|
||||
to accomodate larger START values, mp_loadformat.sas will need the
|
||||
SQL dependency removed (proc sql needs to accomodate 3 index values in
|
||||
to accommodate larger START values, mp_loadformat.sas will need the
|
||||
SQL dependency removed (proc sql needs to accommodate 3 index values in
|
||||
a 32767 ibufsize limit)
|
||||
*/
|
||||
,START char(10000) label='Starting value for format'
|
||||
@@ -40,4 +40,4 @@ create table &libds(
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
|
||||
%mend mddl_sas_cntlout;
|
||||
%mend mddl_sas_cntlout;
|
||||
|
||||
11
main.dox
11
main.dox
@@ -92,4 +92,15 @@
|
||||
* Auto-generated from the plain source `.lua` files in the same directory
|
||||
* Prefixes: _ml_
|
||||
|
||||
*/
|
||||
|
||||
/*! \dir ddl
|
||||
* \brief Data Definition Language files
|
||||
* \details Provides templates for commonly used tables in sasjs/core.
|
||||
Attributes:
|
||||
|
||||
* OS independent
|
||||
* No X command
|
||||
* Prefixes: _mddl_
|
||||
|
||||
*/
|
||||
@@ -43,7 +43,7 @@
|
||||
},
|
||||
{
|
||||
"name": "sas9",
|
||||
"serverUrl": "https://sas.analytium.co.uk:8343",
|
||||
"serverUrl": "",
|
||||
"serverType": "SAS9",
|
||||
"httpsAgentOptions": {
|
||||
"allowInsecureRequests": false
|
||||
@@ -78,7 +78,7 @@
|
||||
},
|
||||
{
|
||||
"name": "docsonly",
|
||||
"serverType": "SAS9",
|
||||
"serverType": "SASJS",
|
||||
"appLoc": "dummy",
|
||||
"macroFolders": [
|
||||
"meta",
|
||||
@@ -105,4 +105,4 @@
|
||||
"contextName": "SAS Job Execution compute context"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,8 +68,8 @@ data _null_;
|
||||
file &fname1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put "Authorization: Bearer " _infile_;
|
||||
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
|
||||
149
server/ms_creategroup.sas
Normal file
149
server/ms_creategroup.sas
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
@file
|
||||
@brief Creates a group on SASjs Server
|
||||
@details Creates a group on SASjs Server with the following attributes:
|
||||
|
||||
@li name
|
||||
@li description
|
||||
@li isActive
|
||||
|
||||
Examples:
|
||||
|
||||
%ms_creategroup(mynewgroup)
|
||||
|
||||
%ms_creategroup(mynewergroup, desc=The group description)
|
||||
|
||||
@param [in] groupname The group name to create. No spaces or special chars.
|
||||
@param [in] desc= (0) If no description provided, group name will be used.
|
||||
@param [in] isactive= (true) Set to false to create an inactive group.
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [out] outds= (work.ms_creategroup) This output dataset will contain the
|
||||
values from the JSON response (such as the id of the new group)
|
||||
|DESCRIPTION:$1.|GROUPID:best.|ISACTIVE:best.|NAME:$11.|
|
||||
|---|---|---|---|
|
||||
|`The group description`|`2 `|`1 `|`mynewergroup `|
|
||||
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_creategroup.test.sas
|
||||
@li ms_getgroups.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_creategroup(groupname
|
||||
,desc=0
|
||||
,isactive=true
|
||||
,outds=work.ms_creategroup
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_creategroup.sas
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
|
||||
%local fref0 fref1 fref2 libref optval rc msg;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let fref2=%mf_getuniquefileref();
|
||||
%let libref=%mf_getuniquelibref();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fref0 termstr=crlf;
|
||||
name=quote(cats(symget('groupname')));
|
||||
description=quote(cats(symget('desc')));
|
||||
if cats(description)='"0"' then description=name;
|
||||
isactive=symget('isactive');
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _all_;
|
||||
%end;
|
||||
|
||||
put '{'@;
|
||||
put '"name":' name @;
|
||||
put ',"description":' description @;
|
||||
put ',"isActive":' isactive @;
|
||||
put '}';
|
||||
run;
|
||||
|
||||
data _null_;
|
||||
file &fref1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then do;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
end;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fref0;
|
||||
input;
|
||||
put _infile_;
|
||||
data _null_;
|
||||
infile &fref1;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='POST' in=&fref0 headerin=&fref1 out=&fref2
|
||||
url="&_sasjs_apiserverurl/SASjsApi/group";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_creategroup.sas
|
||||
,msg=%str(Issue submitting query to SASjsApi/group)
|
||||
)
|
||||
|
||||
libname &libref JSON fileref=&fref2;
|
||||
|
||||
data &outds;
|
||||
set &libref..root;
|
||||
drop ordinal_root;
|
||||
%if &mdebug=1 %then %do;
|
||||
putlog _all_;
|
||||
%end;
|
||||
run;
|
||||
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_creategroup.sas
|
||||
,msg=%str(Issue reading response JSON)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &mdebug=0 %then %do;
|
||||
filename &fref0 clear;
|
||||
filename &fref1 clear;
|
||||
filename &fref2 clear;
|
||||
libname &libref clear;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
infile &fref2;
|
||||
input;
|
||||
putlog _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
%mend ms_creategroup;
|
||||
@@ -87,9 +87,11 @@ data _null_;
|
||||
file &fref1 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
if _n_=1 then do;
|
||||
put "Content-Type: application/json";
|
||||
put "accept: application/json";
|
||||
end;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
|
||||
@@ -33,7 +33,7 @@ data _null_;
|
||||
file &headref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
proc http method='DELETE' headerin=&headref
|
||||
|
||||
@@ -34,7 +34,7 @@ data _null_;
|
||||
file &headref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
proc http method='GET' out=&binaryfref headerin=&headref
|
||||
|
||||
105
server/ms_getgroups.sas
Normal file
105
server/ms_getgroups.sas
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
@file
|
||||
@brief Fetches the list of groups from SASjs Server
|
||||
@details Fetches the list of groups from SASjs Server and writes them to an
|
||||
output dataset.
|
||||
|
||||
Example:
|
||||
|
||||
%ms_getgroups(outds=userlist)
|
||||
|
||||
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
|
||||
@param [out] outds= (work.ms_getgroups) This output dataset will contain the
|
||||
list of groups. Format:
|
||||
|NAME:$32.|DESCRIPTION:$64.|GROUPID:best.|
|
||||
|---|---|---|
|
||||
|`SomeGroup `|`A group `|`1`|
|
||||
|`Another Group`|`this is a different group`|`2`|
|
||||
|`admin`|`Administrators `|`3`|
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquefileref.sas
|
||||
@li mf_getuniquelibref.sas
|
||||
@li mp_abort.sas
|
||||
|
||||
<h4> Related Files </h4>
|
||||
@li ms_creategroup.sas
|
||||
@li ms_getusers.test.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro ms_getgroups(
|
||||
outds=work.ms_getgroups
|
||||
,mdebug=0
|
||||
);
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
)
|
||||
|
||||
%local fref0 fref1 libref optval rc msg;
|
||||
%let fref0=%mf_getuniquefileref();
|
||||
%let fref1=%mf_getuniquefileref();
|
||||
%let libref=%mf_getuniquelibref();
|
||||
|
||||
/* avoid sending bom marker to API */
|
||||
%let optval=%sysfunc(getoption(bomfile));
|
||||
options nobomfile;
|
||||
|
||||
data _null_;
|
||||
file &fref0 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
if _n_=1 then put "accept: application/json";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
infile &fref0;
|
||||
input;
|
||||
put _infile_;
|
||||
run;
|
||||
%end;
|
||||
|
||||
proc http method='GET' headerin=&fref0 out=&fref1
|
||||
url="&_sasjs_apiserverurl/SASjsApi/group";
|
||||
%if &mdebug=1 %then %do;
|
||||
debug level=1;
|
||||
%end;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getgroups.sas
|
||||
,msg=%str(Issue submitting GET query to SASjsApi/group)
|
||||
)
|
||||
|
||||
libname &libref JSON fileref=&fref1;
|
||||
|
||||
data &outds;
|
||||
length NAME $32 DESCRIPTION $64. GROUPID 8;
|
||||
if _n_=1 then call missing(of _all_);
|
||||
set &libref..root;
|
||||
drop ordinal_root;
|
||||
run;
|
||||
|
||||
%mp_abort(
|
||||
iftrue=(&syscc ne 0)
|
||||
,mac=ms_getusers.sas
|
||||
,msg=%str(Issue reading response JSON)
|
||||
)
|
||||
|
||||
/* reset options */
|
||||
options &optval;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
filename &fref0 clear;
|
||||
filename &fref1 clear;
|
||||
libname &libref clear;
|
||||
%end;
|
||||
|
||||
%mend ms_getgroups;
|
||||
@@ -56,8 +56,8 @@ data _null_;
|
||||
file &fref0 lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put "Authorization: Bearer " _infile_;
|
||||
put "accept: application/json";
|
||||
if _n_=1 then put "accept: application/json";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
|
||||
@@ -137,8 +137,8 @@ data _null_;
|
||||
file &authref lrecl=1000;
|
||||
infile "&_sasjs_tokenfile" lrecl=1000;
|
||||
input;
|
||||
put 'Authorization: Bearer ' _infile_;
|
||||
put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
if _n_=1 then put "Content-Type: multipart/form-data; boundary=&boundary";
|
||||
put _infile_;
|
||||
run;
|
||||
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -162,6 +162,7 @@ proc http method='POST' headerin=&authref in=&mainref out=&outref
|
||||
%end;
|
||||
run;
|
||||
%if (&SYS_PROCHTTP_STATUS_CODE ne 200 and &SYS_PROCHTTP_STATUS_CODE ne 201)
|
||||
or &mdebug=1
|
||||
%then %do;
|
||||
data _null_;infile &outref;input;putlog _infile_;run;
|
||||
%end;
|
||||
@@ -177,7 +178,7 @@ options &optval;
|
||||
%if &outlogds ne _null_ or &mdebug=1 %then %do;
|
||||
%local dumplib;
|
||||
%let dumplib=%mf_getuniquelibref();
|
||||
libname &dumplib json (&outref);
|
||||
libname &dumplib json fileref=&outref;
|
||||
data &outlogds;
|
||||
set &dumplib..log;
|
||||
%if &mdebug=1 %then %do;
|
||||
@@ -195,4 +196,4 @@ options &optval;
|
||||
filename &authref;
|
||||
filename &mainref;
|
||||
%end;
|
||||
%mend ms_runstp;
|
||||
%mend ms_runstp;
|
||||
|
||||
22
tests/crossplatform/mf_fmtdttm.test.sas
Normal file
22
tests/crossplatform/mf_fmtdttm.test.sas
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mf_fmtdttm macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_fmtdttm.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
%global test1;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%let test1=%mf_fmtdttm();
|
||||
%mp_assertscope(COMPARE,ignorelist=test1)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=("&test1"="DATETIME19.3" or "&test1"="E8601DT26.6"),
|
||||
desc=Basic test,
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -21,7 +21,8 @@
|
||||
%mf_mkdir(&root/a/d)
|
||||
%mf_mkdir(&root/a/e)
|
||||
%mf_mkdir(&root/a/e/f)
|
||||
data "&root/a/e/f/ds1.sas7bdat";
|
||||
libname test "&root/a/e/f";
|
||||
data test.ds1;
|
||||
x=1;
|
||||
run;
|
||||
|
||||
|
||||
@@ -6,11 +6,39 @@
|
||||
@li mp_filtercheck.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
/* set up test data */
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* valid filter */
|
||||
/* valid filter conditions */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
@@ -21,14 +49,19 @@ 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
|
||||
AND,AND,1,age,=,.A
|
||||
AND,AND,1,height,<,.B
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%let syscc=0;
|
||||
%mp_assertdsobs(work.badrecords,
|
||||
desc=Valid filter query,
|
||||
@@ -49,7 +82,7 @@ AND,OR,2,Weight,>=,7
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -71,7 +104,7 @@ AND,OR,2,Name,NOT IN,"(''''Jane','Alfred')"
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -94,7 +127,7 @@ AND,OR,2,Weight,>=,7
|
||||
run;
|
||||
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -115,7 +148,7 @@ AND,AND,1,age,=,;;%abort
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
@@ -137,7 +170,7 @@ AND,AND,1,age,=,0
|
||||
run;
|
||||
%let syscc=0;
|
||||
%mp_filtercheck(work.inds,
|
||||
targetds=sashelp.class,
|
||||
targetds=work.class,
|
||||
outds=work.badrecords,
|
||||
abort=NO
|
||||
)
|
||||
|
||||
@@ -11,6 +11,34 @@
|
||||
|
||||
options source2;
|
||||
|
||||
/* set up test data */
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* valid filter */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
@@ -23,10 +51,10 @@ AND,OR,2,Name,NOT IN,"('Jane','Janet')"
|
||||
AND,OR,2,Weight,>=,84.6
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -43,10 +71,10 @@ data work.inds;
|
||||
datalines4;
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -64,10 +92,10 @@ datalines4;
|
||||
AND,OR,2,Name,IN,"('Jane','Janet')"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -86,10 +114,10 @@ OR,OR,2,Name,IN,"('Jane','Janet')"
|
||||
OR,OR,3,Name,IN,"('James')"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
@@ -108,10 +136,10 @@ AND,OR,2,Name,IN,"('Jane','Janet')"
|
||||
AND,OR,3,Name,IN,"('James')"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtercheck(work.inds,targetds=sashelp.class)
|
||||
%mp_filtercheck(work.inds,targetds=work.class)
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
data work.test;
|
||||
set sashelp.class;
|
||||
set work.class;
|
||||
where %inc myfilter;;
|
||||
run;
|
||||
%mp_assertdsobs(work.test,
|
||||
|
||||
@@ -10,6 +10,34 @@
|
||||
|
||||
**/
|
||||
|
||||
/* set up test data */
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
libname permlib (work);
|
||||
|
||||
%mp_coretable(LOCKTABLE,libds=permlib.locktable)
|
||||
@@ -31,7 +59,7 @@ AND,OR,2,Weight,NE,77.7
|
||||
;;;;
|
||||
run;
|
||||
|
||||
%mp_filterstore(libds=sashelp.class,
|
||||
%mp_filterstore(libds=work.class,
|
||||
queryds=work.inds,
|
||||
filter_summary=permlib.filtsum,
|
||||
filter_detail=permlib.filtdet,
|
||||
@@ -60,7 +88,7 @@ select max(filter_rk) into: test1 from work.result;
|
||||
)
|
||||
|
||||
/* Test 2 - load same table again and ensure we get the same RK */
|
||||
%mp_filterstore(libds=sashelp.class,
|
||||
%mp_filterstore(libds=work.class,
|
||||
queryds=work.inds,
|
||||
filter_summary=permlib.filtsum,
|
||||
filter_detail=permlib.filtdet,
|
||||
|
||||
@@ -7,9 +7,36 @@
|
||||
@li mp_filtervalidate.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
data work.class ;
|
||||
length name $8 sex $1 age height weight 8;
|
||||
infile cards dsd;
|
||||
input Name:$char. Sex :$char. Age Height Weight;
|
||||
datalines4;
|
||||
Alfred,M,14,69,112.5
|
||||
Alice,F,13,56.5,84
|
||||
Barbara,F,13,65.3,98
|
||||
Carol,F,14,62.8,102.5
|
||||
Henry,M,14,63.5,102.5
|
||||
James,M,12,57.3,83
|
||||
Jane,F,12,59.8,84.5
|
||||
Janet,F,15,62.5,112.5
|
||||
Jeffrey,M,13,62.5,84
|
||||
John,M,12,59,99.5
|
||||
Joyce,F,11,51.3,50.5
|
||||
Judy,F,14,64.3,90
|
||||
Louise,F,12,56.3,77
|
||||
Mary,F,15,66.5,112
|
||||
Philip,M,16,72,150
|
||||
Robert,M,12,64.8,128
|
||||
Ronald,M,15,67,133
|
||||
Thomas,M,11,57.5,85
|
||||
William,M,15,66.5,112
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* valid filter */
|
||||
data work.inds;
|
||||
@@ -21,10 +48,16 @@ AND,AND,1,AGE,>,5
|
||||
AND,AND,1,SEX,NE,"'M'"
|
||||
AND,OR,2,Name,NOT IN,"('Jane','Janet')"
|
||||
AND,OR,2,Weight,>=,84.6
|
||||
AND,AND,3,age,=,.a
|
||||
AND,AND,4,weight,NE,._
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assertdsobs(work.results,
|
||||
desc=Valid filter,
|
||||
test=EMPTY,
|
||||
@@ -40,7 +73,7 @@ datalines4;
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%mp_assertdsobs(work.results,
|
||||
desc=Empty filter,
|
||||
test=EMPTY,
|
||||
@@ -59,7 +92,7 @@ AND,AND,1,SEX,NE,2
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%let syscc=0;
|
||||
%let test3=0;
|
||||
data _null_;
|
||||
@@ -84,7 +117,7 @@ AND,AND,1,age,NE,"'M'"
|
||||
;;;;
|
||||
run;
|
||||
%mp_filtergenerate(work.inds,outref=myfilter)
|
||||
%mp_filtervalidate(myfilter,sashelp.class,outds=work.results,abort=NO)
|
||||
%mp_filtervalidate(myfilter,work.class,outds=work.results,abort=NO)
|
||||
%let syscc=0;
|
||||
%let test4=0;
|
||||
data _null_;
|
||||
|
||||
@@ -6,24 +6,52 @@
|
||||
@li mf_nobs.sas
|
||||
@li mp_getconstraints.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
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 unq unique(tx_from, dd_type),
|
||||
constraint nnn not null(DD_SHORTDESC)
|
||||
);
|
||||
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%macro conditional();
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=6),
|
||||
desc=Output table work.constraints created with correct number of records,
|
||||
outds=work.test_results
|
||||
)
|
||||
%if %sysfunc(exist(sashelp.vcncolu,view))=1 %then %do;
|
||||
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 unq unique(tx_from, dd_type),
|
||||
constraint nnn not null(DD_SHORTDESC)
|
||||
);
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=6),
|
||||
desc=Output table work.constraints created with correct number of records,
|
||||
outds=work.test_results
|
||||
)
|
||||
%end;
|
||||
%else %do;
|
||||
proc sql;
|
||||
create table work.example(
|
||||
TX_FROM float format=datetime19.,
|
||||
DD_TYPE char(16),
|
||||
DD_SOURCE char(2048),
|
||||
DD_SHORTDESC char(256)
|
||||
);
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_getconstraints(lib=work,ds=example,outds=work.constraints)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=0),
|
||||
desc=Empty table created as constraints not supported,
|
||||
outds=work.test_results
|
||||
)
|
||||
%end;
|
||||
%mend conditional;
|
||||
|
||||
%conditional()
|
||||
|
||||
31
tests/crossplatform/mp_resetoption.test.sas
Normal file
31
tests/crossplatform/mp_resetoption.test.sas
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_resetoption macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mp_resetoption.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%let orig=%sysfunc(getoption(obs));
|
||||
|
||||
options obs=30;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_resetoption(OBS)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%let new=%sysfunc(ifc(
|
||||
"%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5",
|
||||
%sysfunc(getoption(obs)), /* test it worked */
|
||||
&orig /* cannot test as option unavailable */
|
||||
));
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&new=&orig),
|
||||
desc=Checking option was reset (if reset option available),
|
||||
outds=work.test_results
|
||||
)
|
||||
50
tests/serveronly/ms_creategroup.test.sas
Normal file
50
tests/serveronly/ms_creategroup.test.sas
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_creategroup.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getuniquename.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li ms_creategroup.sas
|
||||
@li ms_getgroups.sas
|
||||
|
||||
**/
|
||||
|
||||
%let group=%substr(%mf_getuniquename(),1,8);
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_creategroup(&group, desc=The description,mdebug=&sasjs_mdebug,outds=test1)
|
||||
%mp_assertscope(COMPARE
|
||||
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
|
||||
)
|
||||
|
||||
%let id=0;
|
||||
data _null_;
|
||||
set work.test1;
|
||||
call symputx('id',groupid);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&id>0),
|
||||
desc=Checking that group was created with an ID,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* double check by querying the list of users */
|
||||
%ms_getgroups(outds=work.test2)
|
||||
%let checkid=0;
|
||||
data _null_;
|
||||
set work.test2;
|
||||
where name="&group";
|
||||
call symputx('checkid',groupid);
|
||||
run;
|
||||
%mp_assert(
|
||||
iftrue=(&checkid=&id),
|
||||
desc=Checking that fetched group exists and has the same ID,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
23
tests/serveronly/ms_getgroups.test.sas
Normal file
23
tests/serveronly/ms_getgroups.test.sas
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing ms_getgroups.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li ms_getgroups.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%ms_getgroups(outds=work.test1,mdebug=&sasjs_mdebug)
|
||||
%mp_assertscope(COMPARE
|
||||
,ignorelist=MCLIB0_JADP1LEN MCLIB0_JADPNUM MCLIB0_JADVLEN
|
||||
)
|
||||
|
||||
%mp_assertdsobs(work.test1,test=ATLEAST 1)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user