mirror of
https://github.com/sasjs/core.git
synced 2025-12-11 06:24:35 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22bf8065bb | ||
|
|
7bb089e269 | ||
|
|
a6e9158814 | ||
|
|
19a1336720 | ||
|
|
79d5d42e6b | ||
|
|
2d81de5841 | ||
|
|
107ab836d6 | ||
|
|
5225e74465 | ||
|
|
39253d2828 | ||
|
|
171c169537 | ||
|
|
76af9fa33c | ||
|
|
37ccc8a2aa | ||
|
|
d0a0274990 | ||
|
|
b3d374f1b1 | ||
|
|
1c4458faf6 | ||
|
|
96e1d146f4 | ||
|
|
aadc4fb83d | ||
|
|
988ee89cdb | ||
|
|
51cbfbf4bc | ||
|
|
4b69e91362 | ||
|
|
8f9715035a | ||
|
|
35ddccaa16 | ||
|
|
cb0ddfb61c | ||
|
|
c3b6f06b3a | ||
|
|
8046d5a0b1 | ||
|
|
aed07f2943 | ||
|
|
5bf87a78b8 | ||
|
|
0851523d18 | ||
|
|
9e2de81dae | ||
|
|
4887f355c8 | ||
|
|
9b32e6e3f2 |
@@ -98,7 +98,27 @@
|
||||
"contributions": [
|
||||
"ideas"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "VladislavParhomchik",
|
||||
"name": "Vladislav Parhomchik",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/83717836?v=4",
|
||||
"profile": "https://github.com/VladislavParhomchik",
|
||||
"contributions": [
|
||||
"test",
|
||||
"review"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "vznesh",
|
||||
"name": "Vignesh T.",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/28916792?v=4",
|
||||
"profile": "https://github.com/vznesh",
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7
|
||||
"contributorsPerLine": 7,
|
||||
"skipCi": true
|
||||
}
|
||||
|
||||
4
.github/workflows/run-tests.yml
vendored
4
.github/workflows/run-tests.yml
vendored
@@ -12,12 +12,12 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
node-version: [lts/fermium]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
|
||||
19
.gitpod.yml
19
.gitpod.yml
@@ -1,8 +1,25 @@
|
||||
tasks:
|
||||
- init: nvm install --latest-npm && npm i -g @sasjs/cli
|
||||
- init: nvm install --lts && npm i -g @sasjs/cli
|
||||
|
||||
image:
|
||||
file: .gitpod.dockerfile
|
||||
vscode:
|
||||
extensions:
|
||||
- sasjs.sasjs-for-vscode
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
# enable for the master/default branch (defaults to true)
|
||||
master: true
|
||||
# enable for all branches in this repo (defaults to false)
|
||||
branches: false
|
||||
# enable for pull requests coming from this repo (defaults to true)
|
||||
pullRequests: true
|
||||
# enable for pull requests coming from forks (defaults to false)
|
||||
pullRequestsFromForks: true
|
||||
# add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
|
||||
addComment: true
|
||||
# add a "Review in Gitpod" button to pull requests (defaults to false)
|
||||
addBadge: false
|
||||
# add a label once the prebuild is ready to pull requests (defaults to false)
|
||||
addLabel: prebuilt-in-gitpod
|
||||
@@ -9,3 +9,4 @@ sasjs/
|
||||
main.dox
|
||||
make_singlefile.sh
|
||||
*.md
|
||||
.all-contributorsrc
|
||||
|
||||
@@ -189,7 +189,7 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
|
||||
|
||||
## 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)):
|
||||
|
||||
@@ -208,6 +208,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/kkchandok"><img src="https://avatars.githubusercontent.com/u/46090627?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kkchandok</b></sub></a><br /><a href="#ideas-kkchandok" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<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>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
543
all.sas
543
all.sas
@@ -975,7 +975,8 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -1037,7 +1038,11 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
||||
%let vlen = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &libds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
@@ -1161,7 +1166,11 @@ returns:
|
||||
%let vnum = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &ds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
@@ -1210,7 +1219,11 @@ Usage:
|
||||
%let vtype = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &libds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
@@ -1817,7 +1830,7 @@ Usage:
|
||||
msg=cats('"',msg,'"');
|
||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||
else debug='""';
|
||||
if debug ge '"131"' then put '>>weboutBEGIN<<';
|
||||
put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
@@ -1849,7 +1862,7 @@ Usage:
|
||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
if debug ge '"131"' then put '>>weboutEND<<';
|
||||
put '>>weboutEND<<';
|
||||
run;
|
||||
|
||||
%put _all_;
|
||||
@@ -3057,20 +3070,13 @@ run;
|
||||
@brief Returns all files and subdirectories within a specified parent
|
||||
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
||||
|
||||
If getattrs=YES then the doptname / foptname functions are used to scan all
|
||||
properties - any characters that are not valid in a SAS name (v7) are simply
|
||||
stripped, and the table is transposed so theat each property is a column
|
||||
and there is one file per row. An attempt is made to get all properties
|
||||
whether a file or folder, but some files/folders cannot be accessed, and so
|
||||
not all properties can / will be populated.
|
||||
|
||||
Credit for the rename approach:
|
||||
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
||||
|
||||
|
||||
usage:
|
||||
|
||||
%mp_dirlist(path=/some/location,outds=myTable)
|
||||
%mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX)
|
||||
|
||||
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
|
||||
|
||||
@@ -3084,11 +3090,19 @@ run;
|
||||
X CMD) do please raise an issue!
|
||||
|
||||
|
||||
@param path= for which to return contents
|
||||
@param fref= Provide a DISK engine fileref as an alternative to PATH
|
||||
@param outds= the output dataset to create
|
||||
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
|
||||
all attributes for each file / folder.
|
||||
@param [in] path= for which to return contents
|
||||
@param [in] fref= Provide a DISK engine fileref as an alternative to PATH
|
||||
@param [in] maxdepth= (0) Set to a positive integer to indicate the level of
|
||||
subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
||||
recursion, set to MAX.
|
||||
@param [out] outds= the output dataset to create
|
||||
@param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname
|
||||
functions are used to scan all properties - any characters that are not
|
||||
valid in a SAS name (v7) are simply stripped, and the table is transposed
|
||||
so theat each property is a column and there is one file per row. An
|
||||
attempt is made to get all properties whether a file or folder, but some
|
||||
files/folders cannot be accessed, and so not all properties can / will be
|
||||
populated.
|
||||
|
||||
|
||||
@returns outds contains the following variables:
|
||||
@@ -3098,8 +3112,12 @@ run;
|
||||
- filename (just the file name)
|
||||
- ext (.extension)
|
||||
- msg (system message if any issues)
|
||||
- level (depth of folder)
|
||||
- OS SPECIFIC variables, if <code>getattrs=</code> is used.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_dropmembers.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
@@ -3108,14 +3126,27 @@ run;
|
||||
, fref=0
|
||||
, outds=work.mp_dirlist
|
||||
, getattrs=NO
|
||||
, maxdepth=0
|
||||
, level=0 /* The level of recursion to perform. For internal use only. */
|
||||
)/*/STORE SOURCE*/;
|
||||
%let getattrs=%upcase(&getattrs)XX;
|
||||
|
||||
data &outds(compress=no
|
||||
keep=file_or_folder filepath filename ext msg directory
|
||||
/* temp table */
|
||||
%local out_ds;
|
||||
data;run;
|
||||
%let out_ds=%str(&syslast);
|
||||
|
||||
/* drop main (top) table if it exists */
|
||||
%if &level=0 %then %do;
|
||||
%mp_dropmembers(&outds, libref=WORK)
|
||||
%end;
|
||||
|
||||
data &out_ds(compress=no
|
||||
keep=file_or_folder filepath filename ext msg directory level
|
||||
);
|
||||
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
|
||||
ext $20 msg $200;
|
||||
retain level &level;
|
||||
%if &fref=0 %then %do;
|
||||
rc = filename(fref, "&path");
|
||||
%end;
|
||||
@@ -3173,8 +3204,8 @@ data &outds(compress=no
|
||||
run;
|
||||
|
||||
%if %substr(&getattrs,1,1)=Y %then %do;
|
||||
data &outds;
|
||||
set &outds;
|
||||
data &out_ds;
|
||||
set &out_ds;
|
||||
length infoname infoval $60 fref $8;
|
||||
rc=filename(fref,filepath);
|
||||
drop rc infoname fid i close fref;
|
||||
@@ -3215,12 +3246,37 @@ run;
|
||||
run;
|
||||
proc sort;
|
||||
by filepath sasname;
|
||||
proc transpose data=&outds out=&outds(drop=_:);
|
||||
proc transpose data=&out_ds out=&out_ds(drop=_:);
|
||||
id sasname;
|
||||
var infoval;
|
||||
by filepath file_or_folder filename ext ;
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* update main table */
|
||||
proc append base=&outds data=&out_ds;
|
||||
run;
|
||||
|
||||
data &outds;
|
||||
set &outds(where=(filepath ne ''));
|
||||
run;
|
||||
|
||||
/* recursive call */
|
||||
%if &maxdepth>&level or &maxdepth=MAX %then %do;
|
||||
data _null_;
|
||||
set &out_ds;
|
||||
where file_or_folder='folder';
|
||||
code=cats('%nrstr(%mp_dirlist(path=',filepath,",outds=&outds"
|
||||
,",getattrs=&getattrs,level=%eval(&level+1),maxdepth=&maxdepth))");
|
||||
put code=;
|
||||
call execute(code);
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* tidy up */
|
||||
proc sql;
|
||||
drop table &out_ds;
|
||||
|
||||
%mend mp_dirlist;/**
|
||||
@file
|
||||
@brief Creates a dataset containing distinct _formatted_ values
|
||||
@@ -3474,7 +3530,12 @@ data datalines_2;
|
||||
*/
|
||||
else if upcase(format)=:'$HEX' then
|
||||
dataline=cats('put(trim(',name,'),',format,')');
|
||||
else dataline=name;
|
||||
/**
|
||||
* There is no easy way to store line breaks in a cards file.
|
||||
* To discuss this, use: https://github.com/sasjs/core/issues/80
|
||||
* Removing all nonprintables with kw (keep writeable)
|
||||
*/
|
||||
else dataline=cats('compress(',name,', ,"kw")');
|
||||
run;
|
||||
|
||||
proc sql noprint;
|
||||
@@ -3551,7 +3612,7 @@ data _null_;
|
||||
put 'run;';
|
||||
end;
|
||||
else do;
|
||||
put "infile cards dsd delimiter=',';";
|
||||
put "infile cards dsd;";
|
||||
put "input ";
|
||||
%do i = 1 %to &nvars.;
|
||||
%if(%length(&&input_stmt_&i..)) %then
|
||||
@@ -4448,8 +4509,11 @@ create table &outds as
|
||||
on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
|
||||
and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
|
||||
and a.constraint_name=b.constraint_name
|
||||
where upcase(a.TABLE_CATALOG)="&lib"
|
||||
and upcase(b.TABLE_CATALOG)="&lib"
|
||||
/**
|
||||
* We cannot apply this clause to the underlying dictionary table. See:
|
||||
* https://communities.sas.com/t5/SAS-Programming/Unexpected-Where-Clause-behaviour-in-dictionary-TABLE/m-p/771554#M244867
|
||||
*/
|
||||
where calculated libref="&lib"
|
||||
%if "&ds" ne "" %then %do;
|
||||
and upcase(a.TABLE_NAME)="&ds"
|
||||
and upcase(b.TABLE_NAME)="&ds"
|
||||
@@ -6183,6 +6247,357 @@ select distinct lowcase(memname)
|
||||
%end;
|
||||
|
||||
%mend mp_lib2inserts;/**
|
||||
@file
|
||||
@brief Mechanism for locking tables to prevent parallel modifications
|
||||
@details Uses a control table to enable ANY table to be locked for updates.
|
||||
Only useful if every update uses the macro! Used heavily within
|
||||
[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:
|
||||
@li LOCK - Sets the lock flag, also confirms if a SAS lock is available
|
||||
@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
|
||||
assigned.
|
||||
@param [in] ds= The dataset to lock
|
||||
@param [in] ref= A meaningful reference to enable the lock to be traced. Max
|
||||
length is 200 characters.
|
||||
@param [out] ctl_ds= (0) The control table which controls the actual locking.
|
||||
Should already be assigned and available.
|
||||
@param [in] loops= (25) Number of times to check for a lock.
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mp_lockfilecheck.sas
|
||||
@li mf_getuser.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_lockanytable.test.sas
|
||||
|
||||
@version 9.2
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_lockanytable(
|
||||
action
|
||||
,lib= WORK
|
||||
,ds=0
|
||||
,ref=
|
||||
,ctl_ds=0
|
||||
,loops=25
|
||||
,loop_secs=1
|
||||
);
|
||||
data _null_;
|
||||
if _n_=1 then putlog "&sysmacroname entry vars:";
|
||||
set sashelp.vmacro;
|
||||
where scope="&sysmacroname";
|
||||
put name '=' value;
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&ds=0 and &action ne MAKETABLE)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(dataset was not provided)
|
||||
)
|
||||
%mp_abort(iftrue= (&ctl_ds=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Control dataset was not provided)
|
||||
)
|
||||
|
||||
/* set up lib & mac vars */
|
||||
%let lib=%upcase(&lib);
|
||||
%let ds=%upcase(&ds);
|
||||
%let action=%upcase(&action);
|
||||
%local user x trans msg abortme;
|
||||
%let user=%mf_getuser();
|
||||
%let abortme=0;
|
||||
|
||||
%mp_abort(iftrue= (&action ne LOCK & &action ne UNLOCK & &action ne MAKETABLE)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid action (&action) provided)
|
||||
)
|
||||
|
||||
/* if an err condition exists, exit before we even begin */
|
||||
%mp_abort(iftrue= (&syscc>0 and &action=LOCK)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(aborting due to syscc=&syscc on LOCK entry)
|
||||
)
|
||||
|
||||
/* do not bother locking work tables (else may affect all WORK libraries) */
|
||||
%if (%upcase(&lib)=WORK or %str(&lib)=%str()) & &action ne MAKETABLE %then %do;
|
||||
%put NOTE: WORK libraries will not be registered in the locking system.;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* do not proceed if no observations can be processed */
|
||||
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(options obs = 0. syserrortext=&syserrortext)
|
||||
)
|
||||
|
||||
%if &ACTION=LOCK %then %do;
|
||||
|
||||
/* abort if a SAS lock is already in place, or cannot be applied */
|
||||
%mp_lockfilecheck(&lib..&ds)
|
||||
|
||||
/* next, check there is a record for this table */
|
||||
%local record_exists_check;
|
||||
proc sql noprint;
|
||||
select count(*) into: record_exists_check from &ctl_ds
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
%if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
|
||||
%if &record_exists_check=0 %then %do;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: adding record to lock table..";
|
||||
run;
|
||||
|
||||
data ;
|
||||
if 0 then set &ctl_ds;
|
||||
LOCK_LIB ="&lib";
|
||||
LOCK_DS="&ds";
|
||||
LOCK_STATUS_CD='LOCKED';
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
LOCK_USER_NM="&user";
|
||||
LOCK_PID="&sysjobid";
|
||||
LOCK_REF="&ref";
|
||||
output;stop;
|
||||
run;
|
||||
%let trans=&syslast;
|
||||
proc append base=&ctl_ds data=&trans;
|
||||
run;
|
||||
%end;
|
||||
/* if record does exist, perform lock attempts */
|
||||
%else %do x=1 %to &loops;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: attempting lock (iteration &x) "@;
|
||||
putlog "at %sysfunc(datetime(),datetime19.) ..";
|
||||
run;
|
||||
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='LOCKED'
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
/**
|
||||
* NOTE - occasionally SQL server will return an err code (deadlocked
|
||||
* transaction). If so, ignore it, keep calm, and carry on..
|
||||
*/
|
||||
%if &syscc>0 %then %do;
|
||||
data _null_;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
putlog "NOTE- &sysmacroname: Update failed. "@;
|
||||
putlog "Resetting err conditions and re-attempting.";
|
||||
putlog "NOTE- syscc=&syscc syserr=&syserr sqlrc=&sqlrc";
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
run;
|
||||
%let syscc=0;
|
||||
%let sqlrc=0;
|
||||
%end;
|
||||
|
||||
/* now check if the record was successfully updated */
|
||||
%local success_check;
|
||||
proc sql noprint;
|
||||
select count(*) into: success_check from &ctl_ds
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds"
|
||||
and LOCK_PID="&sysjobid" and LOCK_STATUS_CD='LOCKED';
|
||||
quit;
|
||||
%if &success_check=0 %then %do;
|
||||
%if &x < &loops %then %do;
|
||||
/* pause before next check */
|
||||
data _null_;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
putlog "NOTE- &sysmacroname: table locked, waiting "@;
|
||||
putlog "%sysfunc(sleep(&loop_inc)) seconds.. ";
|
||||
putlog "NOTE- (iteration &x of &loops)";
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
%let msg=Unable to lock &lib..&ds via &ctl_ds after &loops attempts.\n
|
||||
Please ask your administrator to investigate!;
|
||||
%let abortme=1;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
putlog "NOTE- &sysmacroname: Table &lib..&ds locked at "@
|
||||
putlog " %sysfunc(datetime(),datetime19.) (iteration &x)"@;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
run;
|
||||
%if &syscc>0 %then %do;
|
||||
%put setting syscc(&syscc) back to 0;
|
||||
%let syscc=0;
|
||||
%end;
|
||||
%let x=&loops; /* no more iterations needed */
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
%else %if &ACTION=UNLOCK %then %do;
|
||||
%local status;
|
||||
proc sql noprint;
|
||||
select LOCK_STATUS_CD into: status from &ctl_ds
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
%if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
|
||||
%if &status=LOCKED %then %do;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: unlocking &lib..&ds:";
|
||||
run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='UNLOCKED'
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
%end;
|
||||
%else %if &status=UNLOCKED %then %do;
|
||||
%put %str(WAR)NING: &lib..&ds is already unlocked!;
|
||||
%end;
|
||||
%else %do;
|
||||
%put NOTE: Unrecognised STATUS_CD (&status) in &ctl_ds;
|
||||
%let abortme=1;
|
||||
%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;
|
||||
%let msg=lock_anytable given unsupported action (&action);
|
||||
%let abortme=1;
|
||||
%end;
|
||||
|
||||
/* catch errors - mp_abort must be called outside of a logic block */
|
||||
%mp_abort(iftrue=(&abortme=1),
|
||||
msg=%superq(msg),
|
||||
mac=&sysmacroname
|
||||
)
|
||||
|
||||
%exit_macro:
|
||||
data _null_;
|
||||
put "&sysmacroname: Exit vars: action=&action lib=&lib ds=&ds";
|
||||
put " syscc=&syscc sqlrc=&sqlrc syserr=&syserr";
|
||||
run;
|
||||
%mend mp_lockanytable;
|
||||
|
||||
|
||||
/**
|
||||
@file
|
||||
@brief Aborts if a SAS lock file is in place, or if one cannot be applied.
|
||||
@details Used in conjuction with the mp_lockanytable macro.
|
||||
More info here: https://sasensei.com/flash/24
|
||||
|
||||
Usage:
|
||||
|
||||
data work.test; a=1;run;
|
||||
%mp_lockfilecheck(work.test)
|
||||
|
||||
@param [in] libds The libref.dataset for which to check the lock status
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getattrc.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_lockanytable.sas
|
||||
@li mp_lockfilecheck.test.sas
|
||||
|
||||
@version 9.2
|
||||
**/
|
||||
|
||||
%macro mp_lockfilecheck(
|
||||
libds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data _null_;
|
||||
if _n_=1 then putlog "&sysmacroname entry vars:";
|
||||
set sashelp.vmacro;
|
||||
where scope="&sysmacroname";
|
||||
put name '=' value;
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&syscc>0)
|
||||
,mac=checklock.sas
|
||||
,msg=Aborting with syscc=&syscc on entry.
|
||||
)
|
||||
%mp_abort(iftrue= (&libds=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(libds not provided)
|
||||
)
|
||||
|
||||
%local msg lib ds;
|
||||
%let lib=%upcase(%scan(&libds,1,.));
|
||||
%let ds=%upcase(%scan(&libds,2,.));
|
||||
|
||||
/* do not proceed if no observations can be processed */
|
||||
%let msg=options obs = 0. syserrortext=%superq(syserrortext);
|
||||
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
|
||||
,mac=checklock.sas
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
data _null_;
|
||||
putlog "Checking engine & member type";
|
||||
run;
|
||||
%local engine memtype;
|
||||
%let memtype=%mf_getattrc(&libds,MTYPE);
|
||||
%let engine=%mf_getattrc(&libds,ENGINE);
|
||||
|
||||
%if &engine ne V9 and &engine ne BASE %then %do;
|
||||
data _null_;
|
||||
putlog "Lib &lib is not assigned using BASE engine - uses &engine instead";
|
||||
putlog "SAS lock check will not be performed";
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%else %if &memtype ne DATA %then %do;
|
||||
%put NOTE: Cannot lock a VIEW!! Memtype=&memtype;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
putlog "Engine = &engine, memtype=&memtype";
|
||||
putlog "Attempting lock statement";
|
||||
run;
|
||||
|
||||
lock &libds;
|
||||
|
||||
%local abortme;
|
||||
%let abortme=0;
|
||||
%if &syscc>0 or &SYSLCKRC ne 0 %then %do;
|
||||
%let msg=Unable to apply lock on &libds (SYSLCKRC=&SYSLCKRC syscc=&syscc);
|
||||
%put %str(ERR)OR: &sysmacroname: &msg;
|
||||
%let abortme=1;
|
||||
%end;
|
||||
|
||||
lock &libds clear;
|
||||
|
||||
%mp_abort(iftrue= (&abortme=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
%mend mp_lockfilecheck;/**
|
||||
@file
|
||||
@brief Create a Markdown Table from a dataset
|
||||
@details A markdown table is a simple table representation for use in
|
||||
@@ -7546,10 +7961,10 @@ run;
|
||||
@file mp_unzip.sas
|
||||
@brief Unzips a zip file
|
||||
@details Opens the zip file and copies all the contents to another directory.
|
||||
It is not possible to retain permissions / timestamps, also the BOF marker
|
||||
is lost so it cannot extract binary files.
|
||||
It is not possible to retain permissions / timestamps, also the BOF marker
|
||||
is lost so it cannot extract binary files.
|
||||
|
||||
Usage:
|
||||
Usage:
|
||||
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
@@ -7560,8 +7975,9 @@ run;
|
||||
@li mf_mkdir.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
|
||||
@param ziploc= fileref or quoted full path to zip file ("/path/to/file.zip")
|
||||
@param outdir= directory in which to write the outputs (created if non existant)
|
||||
@param ziploc= Fileref or quoted full path to zip file ("/path/to/file.zip")
|
||||
@param outdir= (%sysfunc(pathname(work))) Directory in which to write the
|
||||
outputs (created if non existant)
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@@ -7579,7 +7995,8 @@ run;
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
|
||||
filename &fname1 ZIP &ziploc; * Macro variable &datazip would be read from the file*;
|
||||
/* Macro variable &datazip would be read from the file */
|
||||
filename &fname1 ZIP &ziploc;
|
||||
|
||||
/* Read the "members" (files) from the ZIP file */
|
||||
data _data_(keep=memname isFolder);
|
||||
@@ -7767,6 +8184,42 @@ alter table &libds modify &var char(&len);
|
||||
|
||||
%mend mp_validatecol;
|
||||
/**
|
||||
@file
|
||||
@brief Wait until a file arrives before continuing execution
|
||||
@details Loops with a `sleep()` command until a file arrives or the max wait
|
||||
period expires.
|
||||
|
||||
@example
|
||||
|
||||
Wait 3 minutes OR for /tmp/flag.txt to appear
|
||||
|
||||
%mp_wait4file(/tmp/flag.txt , maxwait=60*3)
|
||||
|
||||
@param [in] file The file to wait for. Must be provided.
|
||||
@param [in] maxwait= (0) Number of seconds to wait. If set to zero, will
|
||||
loop indefinitely (to a maximum of 46 days, per SAS [documentation](
|
||||
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a001418809.htm
|
||||
)). Otherwise, execution will proceed upon sleep expiry.
|
||||
@param [in] interval= (1) The wait period between sleeps, in seconds
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_wait4file(file, maxwait=0, interval=1);
|
||||
|
||||
%if %str(&file)=%str() %then %do;
|
||||
%put %str(ERR)OR: file not provided;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
maxwait=&maxwait;
|
||||
if maxwait=0 then maxwait=60*60*24*46;
|
||||
do until (fileexist("&file") or slept>maxwait );
|
||||
slept=sum(slept,sleep(&interval,1));
|
||||
end;
|
||||
run;
|
||||
|
||||
%mend mp_wait4file;/**
|
||||
@file
|
||||
@brief Fix the `_WEBIN` variables provided to SAS web services
|
||||
@details When uploading files to SAS Stored Processes or Viya Jobs a number
|
||||
@@ -7842,11 +8295,18 @@ alter table &libds modify &var char(&len);
|
||||
@li mp_dirlist.sas
|
||||
|
||||
@param in= unquoted filepath, dataset of files or directory to zip
|
||||
@param type= FILE, DATASET, DIRECTORY. (FILE / DATASET not ready yet)
|
||||
@param outname= output file to create, without .zip extension
|
||||
@param outpath= location for output zip file
|
||||
@param type= (FILE) Valid values:
|
||||
@li FILE - /full/path/and/filename.extension to a particular file
|
||||
@li DATASET - a dataset containing a list of files to zip (see `incol`)
|
||||
@li DIRECTORY - a directory to zip
|
||||
@param outname= (FILE) Output file to create, _without_ .zip extension
|
||||
@param outpath= (%sysfunc(pathname(WORK))) Parent folder for output zip file
|
||||
@param incol= if DATASET input, say which column contains the filepath
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_unzip.sas
|
||||
@li mp_zip.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@@ -7877,9 +8337,9 @@ ods package open nopf;
|
||||
set &ds;
|
||||
length __command $4000;
|
||||
if file_or_folder='file';
|
||||
command=cats('ods package add file="',filepath
|
||||
__command=cats('ods package add file="',filepath
|
||||
,'" mimetype="application/x-compress";');
|
||||
call execute(command);
|
||||
call execute(__command);
|
||||
run;
|
||||
/* tidy up */
|
||||
%if &debug=NO %then %do;
|
||||
@@ -7890,11 +8350,10 @@ ods package open nopf;
|
||||
data _null_;
|
||||
set ∈
|
||||
length __command $4000;
|
||||
command=cats('ods package add file="',&incol
|
||||
__command=cats('ods package add file="',&incol
|
||||
,'" mimetype="application/x-compress";');
|
||||
call execute(command);
|
||||
call execute(__command);
|
||||
run;
|
||||
ods package add file="&in" mimetype="application/x-compress";
|
||||
%end;
|
||||
|
||||
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
|
||||
@@ -43,7 +43,11 @@
|
||||
%let vlen = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &libds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
|
||||
@@ -43,7 +43,11 @@ returns:
|
||||
%let vnum = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &ds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
|
||||
@@ -39,7 +39,11 @@ Usage:
|
||||
%let vtype = %str( );
|
||||
%end;
|
||||
%end;
|
||||
%else %put dataset &libds not opened! (rc=&dsid);
|
||||
%else %do;
|
||||
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||
%put &sysmacroname: %sysfunc(sysmsg());
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* Close dataset */
|
||||
%let rc = %sysfunc(close(&dsid));
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
msg=cats('"',msg,'"');
|
||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||
else debug='""';
|
||||
if debug ge '"131"' then put '>>weboutBEGIN<<';
|
||||
put '>>weboutBEGIN<<';
|
||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||
put ',"sasjsAbort" : [{';
|
||||
put ' "MSG":' msg ;
|
||||
@@ -201,7 +201,7 @@
|
||||
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||
put "}" @;
|
||||
if debug ge '"131"' then put '>>weboutEND<<';
|
||||
put '>>weboutEND<<';
|
||||
run;
|
||||
|
||||
%put _all_;
|
||||
|
||||
@@ -3,20 +3,13 @@
|
||||
@brief Returns all files and subdirectories within a specified parent
|
||||
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
|
||||
|
||||
If getattrs=YES then the doptname / foptname functions are used to scan all
|
||||
properties - any characters that are not valid in a SAS name (v7) are simply
|
||||
stripped, and the table is transposed so theat each property is a column
|
||||
and there is one file per row. An attempt is made to get all properties
|
||||
whether a file or folder, but some files/folders cannot be accessed, and so
|
||||
not all properties can / will be populated.
|
||||
|
||||
Credit for the rename approach:
|
||||
https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
|
||||
|
||||
|
||||
usage:
|
||||
|
||||
%mp_dirlist(path=/some/location,outds=myTable)
|
||||
%mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX)
|
||||
|
||||
%mp_dirlist(outds=cwdfileprops, getattrs=YES)
|
||||
|
||||
@@ -30,11 +23,19 @@
|
||||
X CMD) do please raise an issue!
|
||||
|
||||
|
||||
@param path= for which to return contents
|
||||
@param fref= Provide a DISK engine fileref as an alternative to PATH
|
||||
@param outds= the output dataset to create
|
||||
@param getattrs= YES/NO (default=NO). Uses doptname and foptname to return
|
||||
all attributes for each file / folder.
|
||||
@param [in] path= for which to return contents
|
||||
@param [in] fref= Provide a DISK engine fileref as an alternative to PATH
|
||||
@param [in] maxdepth= (0) Set to a positive integer to indicate the level of
|
||||
subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
|
||||
recursion, set to MAX.
|
||||
@param [out] outds= the output dataset to create
|
||||
@param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname
|
||||
functions are used to scan all properties - any characters that are not
|
||||
valid in a SAS name (v7) are simply stripped, and the table is transposed
|
||||
so theat each property is a column and there is one file per row. An
|
||||
attempt is made to get all properties whether a file or folder, but some
|
||||
files/folders cannot be accessed, and so not all properties can / will be
|
||||
populated.
|
||||
|
||||
|
||||
@returns outds contains the following variables:
|
||||
@@ -44,8 +45,12 @@
|
||||
- filename (just the file name)
|
||||
- ext (.extension)
|
||||
- msg (system message if any issues)
|
||||
- level (depth of folder)
|
||||
- OS SPECIFIC variables, if <code>getattrs=</code> is used.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_dropmembers.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
**/
|
||||
@@ -54,14 +59,27 @@
|
||||
, fref=0
|
||||
, outds=work.mp_dirlist
|
||||
, getattrs=NO
|
||||
, maxdepth=0
|
||||
, level=0 /* The level of recursion to perform. For internal use only. */
|
||||
)/*/STORE SOURCE*/;
|
||||
%let getattrs=%upcase(&getattrs)XX;
|
||||
|
||||
data &outds(compress=no
|
||||
keep=file_or_folder filepath filename ext msg directory
|
||||
/* temp table */
|
||||
%local out_ds;
|
||||
data;run;
|
||||
%let out_ds=%str(&syslast);
|
||||
|
||||
/* drop main (top) table if it exists */
|
||||
%if &level=0 %then %do;
|
||||
%mp_dropmembers(&outds, libref=WORK)
|
||||
%end;
|
||||
|
||||
data &out_ds(compress=no
|
||||
keep=file_or_folder filepath filename ext msg directory level
|
||||
);
|
||||
length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
|
||||
ext $20 msg $200;
|
||||
retain level &level;
|
||||
%if &fref=0 %then %do;
|
||||
rc = filename(fref, "&path");
|
||||
%end;
|
||||
@@ -119,8 +137,8 @@ data &outds(compress=no
|
||||
run;
|
||||
|
||||
%if %substr(&getattrs,1,1)=Y %then %do;
|
||||
data &outds;
|
||||
set &outds;
|
||||
data &out_ds;
|
||||
set &out_ds;
|
||||
length infoname infoval $60 fref $8;
|
||||
rc=filename(fref,filepath);
|
||||
drop rc infoname fid i close fref;
|
||||
@@ -161,10 +179,35 @@ run;
|
||||
run;
|
||||
proc sort;
|
||||
by filepath sasname;
|
||||
proc transpose data=&outds out=&outds(drop=_:);
|
||||
proc transpose data=&out_ds out=&out_ds(drop=_:);
|
||||
id sasname;
|
||||
var infoval;
|
||||
by filepath file_or_folder filename ext ;
|
||||
run;
|
||||
%end;
|
||||
|
||||
data &out_ds;
|
||||
set &out_ds(where=(filepath ne ''));
|
||||
run;
|
||||
|
||||
/* update main table */
|
||||
proc append base=&outds data=&out_ds;
|
||||
run;
|
||||
|
||||
/* recursive call */
|
||||
%if &maxdepth>&level or &maxdepth=MAX %then %do;
|
||||
data _null_;
|
||||
set &out_ds;
|
||||
where file_or_folder='folder';
|
||||
code=cats('%nrstr(%mp_dirlist(path=',filepath,",outds=&outds"
|
||||
,",getattrs=&getattrs,level=%eval(&level+1),maxdepth=&maxdepth))");
|
||||
put code=;
|
||||
call execute(code);
|
||||
run;
|
||||
%end;
|
||||
|
||||
/* tidy up */
|
||||
proc sql;
|
||||
drop table &out_ds;
|
||||
|
||||
%mend mp_dirlist;
|
||||
@@ -161,7 +161,12 @@ data datalines_2;
|
||||
*/
|
||||
else if upcase(format)=:'$HEX' then
|
||||
dataline=cats('put(trim(',name,'),',format,')');
|
||||
else dataline=name;
|
||||
/**
|
||||
* There is no easy way to store line breaks in a cards file.
|
||||
* To discuss this, use: https://github.com/sasjs/core/issues/80
|
||||
* Removing all nonprintables with kw (keep writeable)
|
||||
*/
|
||||
else dataline=cats('compress(',name,', ,"kw")');
|
||||
run;
|
||||
|
||||
proc sql noprint;
|
||||
@@ -238,7 +243,7 @@ data _null_;
|
||||
put 'run;';
|
||||
end;
|
||||
else do;
|
||||
put "infile cards dsd delimiter=',';";
|
||||
put "infile cards dsd;";
|
||||
put "input ";
|
||||
%do i = 1 %to &nvars.;
|
||||
%if(%length(&&input_stmt_&i..)) %then
|
||||
|
||||
@@ -49,8 +49,11 @@ create table &outds as
|
||||
on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
|
||||
and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
|
||||
and a.constraint_name=b.constraint_name
|
||||
where upcase(a.TABLE_CATALOG)="&lib"
|
||||
and upcase(b.TABLE_CATALOG)="&lib"
|
||||
/**
|
||||
* We cannot apply this clause to the underlying dictionary table. See:
|
||||
* https://communities.sas.com/t5/SAS-Programming/Unexpected-Where-Clause-behaviour-in-dictionary-TABLE/m-p/771554#M244867
|
||||
*/
|
||||
where calculated libref="&lib"
|
||||
%if "&ds" ne "" %then %do;
|
||||
and upcase(a.TABLE_NAME)="&ds"
|
||||
and upcase(b.TABLE_NAME)="&ds"
|
||||
|
||||
255
base/mp_lockanytable.sas
Normal file
255
base/mp_lockanytable.sas
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
@file
|
||||
@brief Mechanism for locking tables to prevent parallel modifications
|
||||
@details Uses a control table to enable ANY table to be locked for updates.
|
||||
Only useful if every update uses the macro! Used heavily within
|
||||
[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:
|
||||
@li LOCK - Sets the lock flag, also confirms if a SAS lock is available
|
||||
@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
|
||||
assigned.
|
||||
@param [in] ds= The dataset to lock
|
||||
@param [in] ref= A meaningful reference to enable the lock to be traced. Max
|
||||
length is 200 characters.
|
||||
@param [out] ctl_ds= (0) The control table which controls the actual locking.
|
||||
Should already be assigned and available.
|
||||
@param [in] loops= (25) Number of times to check for a lock.
|
||||
@param [in] loop_secs= (1) Seconds to wait between each lock attempt
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mp_lockfilecheck.sas
|
||||
@li mf_getuser.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_lockanytable.test.sas
|
||||
|
||||
@version 9.2
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_lockanytable(
|
||||
action
|
||||
,lib= WORK
|
||||
,ds=0
|
||||
,ref=
|
||||
,ctl_ds=0
|
||||
,loops=25
|
||||
,loop_secs=1
|
||||
);
|
||||
data _null_;
|
||||
if _n_=1 then putlog "&sysmacroname entry vars:";
|
||||
set sashelp.vmacro;
|
||||
where scope="&sysmacroname";
|
||||
put name '=' value;
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&ds=0 and &action ne MAKETABLE)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(dataset was not provided)
|
||||
)
|
||||
%mp_abort(iftrue= (&ctl_ds=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Control dataset was not provided)
|
||||
)
|
||||
|
||||
/* set up lib & mac vars */
|
||||
%let lib=%upcase(&lib);
|
||||
%let ds=%upcase(&ds);
|
||||
%let action=%upcase(&action);
|
||||
%local user x trans msg abortme;
|
||||
%let user=%mf_getuser();
|
||||
%let abortme=0;
|
||||
|
||||
%mp_abort(iftrue= (&action ne LOCK & &action ne UNLOCK & &action ne MAKETABLE)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Invalid action (&action) provided)
|
||||
)
|
||||
|
||||
/* if an err condition exists, exit before we even begin */
|
||||
%mp_abort(iftrue= (&syscc>0 and &action=LOCK)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(aborting due to syscc=&syscc on LOCK entry)
|
||||
)
|
||||
|
||||
/* do not bother locking work tables (else may affect all WORK libraries) */
|
||||
%if (%upcase(&lib)=WORK or %str(&lib)=%str()) & &action ne MAKETABLE %then %do;
|
||||
%put NOTE: WORK libraries will not be registered in the locking system.;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
/* do not proceed if no observations can be processed */
|
||||
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(options obs = 0. syserrortext=&syserrortext)
|
||||
)
|
||||
|
||||
%if &ACTION=LOCK %then %do;
|
||||
|
||||
/* abort if a SAS lock is already in place, or cannot be applied */
|
||||
%mp_lockfilecheck(&lib..&ds)
|
||||
|
||||
/* next, check there is a record for this table */
|
||||
%local record_exists_check;
|
||||
proc sql noprint;
|
||||
select count(*) into: record_exists_check from &ctl_ds
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
%if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
|
||||
%if &record_exists_check=0 %then %do;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: adding record to lock table..";
|
||||
run;
|
||||
|
||||
data ;
|
||||
if 0 then set &ctl_ds;
|
||||
LOCK_LIB ="&lib";
|
||||
LOCK_DS="&ds";
|
||||
LOCK_STATUS_CD='LOCKED';
|
||||
LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt;
|
||||
LOCK_USER_NM="&user";
|
||||
LOCK_PID="&sysjobid";
|
||||
LOCK_REF="&ref";
|
||||
output;stop;
|
||||
run;
|
||||
%let trans=&syslast;
|
||||
proc append base=&ctl_ds data=&trans;
|
||||
run;
|
||||
%end;
|
||||
/* if record does exist, perform lock attempts */
|
||||
%else %do x=1 %to &loops;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: attempting lock (iteration &x) "@;
|
||||
putlog "at %sysfunc(datetime(),datetime19.) ..";
|
||||
run;
|
||||
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='LOCKED'
|
||||
, LOCK_START_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
/**
|
||||
* NOTE - occasionally SQL server will return an err code (deadlocked
|
||||
* transaction). If so, ignore it, keep calm, and carry on..
|
||||
*/
|
||||
%if &syscc>0 %then %do;
|
||||
data _null_;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
putlog "NOTE- &sysmacroname: Update failed. "@;
|
||||
putlog "Resetting err conditions and re-attempting.";
|
||||
putlog "NOTE- syscc=&syscc syserr=&syserr sqlrc=&sqlrc";
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
run;
|
||||
%let syscc=0;
|
||||
%let sqlrc=0;
|
||||
%end;
|
||||
|
||||
/* now check if the record was successfully updated */
|
||||
%local success_check;
|
||||
proc sql noprint;
|
||||
select count(*) into: success_check from &ctl_ds
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds"
|
||||
and LOCK_PID="&sysjobid" and LOCK_STATUS_CD='LOCKED';
|
||||
quit;
|
||||
%if &success_check=0 %then %do;
|
||||
%if &x < &loops %then %do;
|
||||
/* pause before next check */
|
||||
data _null_;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
putlog "NOTE- &sysmacroname: table locked, waiting "@;
|
||||
putlog "%sysfunc(sleep(&loop_inc)) seconds.. ";
|
||||
putlog "NOTE- (iteration &x of &loops)";
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
run;
|
||||
%end;
|
||||
%else %do;
|
||||
%let msg=Unable to lock &lib..&ds via &ctl_ds after &loops attempts.\n
|
||||
Please ask your administrator to investigate!;
|
||||
%let abortme=1;
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
data _null_;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
putlog "NOTE- &sysmacroname: Table &lib..&ds locked at "@
|
||||
putlog " %sysfunc(datetime(),datetime19.) (iteration &x)"@;
|
||||
putlog 'NOTE-' / 'NOTE-';
|
||||
run;
|
||||
%if &syscc>0 %then %do;
|
||||
%put setting syscc(&syscc) back to 0;
|
||||
%let syscc=0;
|
||||
%end;
|
||||
%let x=&loops; /* no more iterations needed */
|
||||
%end;
|
||||
%end;
|
||||
%end;
|
||||
%else %if &ACTION=UNLOCK %then %do;
|
||||
%local status;
|
||||
proc sql noprint;
|
||||
select LOCK_STATUS_CD into: status from &ctl_ds
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
%if &syscc>0 %then %put syscc=&syscc sqlrc=&sqlrc;
|
||||
%if &status=LOCKED %then %do;
|
||||
data _null_;
|
||||
putlog "&sysmacroname: unlocking &lib..&ds:";
|
||||
run;
|
||||
proc sql;
|
||||
update &ctl_ds
|
||||
set LOCK_STATUS_CD='UNLOCKED'
|
||||
, LOCK_END_DTTM="%sysfunc(datetime(),E8601DT26.6)"dt
|
||||
, LOCK_USER_NM="&user"
|
||||
, LOCK_PID="&sysjobid"
|
||||
, LOCK_REF="&ref"
|
||||
where LOCK_LIB ="&lib" and LOCK_DS="&ds";
|
||||
quit;
|
||||
%end;
|
||||
%else %if &status=UNLOCKED %then %do;
|
||||
%put %str(WAR)NING: &lib..&ds is already unlocked!;
|
||||
%end;
|
||||
%else %do;
|
||||
%put NOTE: Unrecognised STATUS_CD (&status) in &ctl_ds;
|
||||
%let abortme=1;
|
||||
%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;
|
||||
%let msg=lock_anytable given unsupported action (&action);
|
||||
%let abortme=1;
|
||||
%end;
|
||||
|
||||
/* catch errors - mp_abort must be called outside of a logic block */
|
||||
%mp_abort(iftrue=(&abortme=1),
|
||||
msg=%superq(msg),
|
||||
mac=&sysmacroname
|
||||
)
|
||||
|
||||
%exit_macro:
|
||||
data _null_;
|
||||
put "&sysmacroname: Exit vars: action=&action lib=&lib ds=&ds";
|
||||
put " syscc=&syscc sqlrc=&sqlrc syserr=&syserr";
|
||||
run;
|
||||
%mend mp_lockanytable;
|
||||
|
||||
|
||||
97
base/mp_lockfilecheck.sas
Normal file
97
base/mp_lockfilecheck.sas
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
@file
|
||||
@brief Aborts if a SAS lock file is in place, or if one cannot be applied.
|
||||
@details Used in conjuction with the mp_lockanytable macro.
|
||||
More info here: https://sasensei.com/flash/24
|
||||
|
||||
Usage:
|
||||
|
||||
data work.test; a=1;run;
|
||||
%mp_lockfilecheck(work.test)
|
||||
|
||||
@param [in] libds The libref.dataset for which to check the lock status
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_abort.sas
|
||||
@li mf_getattrc.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_lockanytable.sas
|
||||
@li mp_lockfilecheck.test.sas
|
||||
|
||||
@version 9.2
|
||||
**/
|
||||
|
||||
%macro mp_lockfilecheck(
|
||||
libds
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
data _null_;
|
||||
if _n_=1 then putlog "&sysmacroname entry vars:";
|
||||
set sashelp.vmacro;
|
||||
where scope="&sysmacroname";
|
||||
put name '=' value;
|
||||
run;
|
||||
|
||||
%mp_abort(iftrue= (&syscc>0)
|
||||
,mac=checklock.sas
|
||||
,msg=Aborting with syscc=&syscc on entry.
|
||||
)
|
||||
%mp_abort(iftrue= (&libds=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(libds not provided)
|
||||
)
|
||||
|
||||
%local msg lib ds;
|
||||
%let lib=%upcase(%scan(&libds,1,.));
|
||||
%let ds=%upcase(%scan(&libds,2,.));
|
||||
|
||||
/* do not proceed if no observations can be processed */
|
||||
%let msg=options obs = 0. syserrortext=%superq(syserrortext);
|
||||
%mp_abort(iftrue= (%sysfunc(getoption(OBS))=0)
|
||||
,mac=checklock.sas
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
data _null_;
|
||||
putlog "Checking engine & member type";
|
||||
run;
|
||||
%local engine memtype;
|
||||
%let memtype=%mf_getattrc(&libds,MTYPE);
|
||||
%let engine=%mf_getattrc(&libds,ENGINE);
|
||||
|
||||
%if &engine ne V9 and &engine ne BASE %then %do;
|
||||
data _null_;
|
||||
putlog "Lib &lib is not assigned using BASE engine - uses &engine instead";
|
||||
putlog "SAS lock check will not be performed";
|
||||
run;
|
||||
%return;
|
||||
%end;
|
||||
%else %if &memtype ne DATA %then %do;
|
||||
%put NOTE: Cannot lock a VIEW!! Memtype=&memtype;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
putlog "Engine = &engine, memtype=&memtype";
|
||||
putlog "Attempting lock statement";
|
||||
run;
|
||||
|
||||
lock &libds;
|
||||
|
||||
%local abortme;
|
||||
%let abortme=0;
|
||||
%if &syscc>0 or &SYSLCKRC ne 0 %then %do;
|
||||
%let msg=Unable to apply lock on &libds (SYSLCKRC=&SYSLCKRC syscc=&syscc);
|
||||
%put %str(ERR)OR: &sysmacroname: &msg;
|
||||
%let abortme=1;
|
||||
%end;
|
||||
|
||||
lock &libds clear;
|
||||
|
||||
%mp_abort(iftrue= (&abortme=1)
|
||||
,mac=&sysmacroname
|
||||
,msg=%superq(msg)
|
||||
)
|
||||
|
||||
%mend mp_lockfilecheck;
|
||||
@@ -2,10 +2,10 @@
|
||||
@file mp_unzip.sas
|
||||
@brief Unzips a zip file
|
||||
@details Opens the zip file and copies all the contents to another directory.
|
||||
It is not possible to retain permissions / timestamps, also the BOF marker
|
||||
is lost so it cannot extract binary files.
|
||||
It is not possible to retain permissions / timestamps, also the BOF marker
|
||||
is lost so it cannot extract binary files.
|
||||
|
||||
Usage:
|
||||
Usage:
|
||||
|
||||
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
%inc mc;
|
||||
@@ -16,8 +16,9 @@
|
||||
@li mf_mkdir.sas
|
||||
@li mf_getuniquefileref.sas
|
||||
|
||||
@param ziploc= fileref or quoted full path to zip file ("/path/to/file.zip")
|
||||
@param outdir= directory in which to write the outputs (created if non existant)
|
||||
@param ziploc= Fileref or quoted full path to zip file ("/path/to/file.zip")
|
||||
@param outdir= (%sysfunc(pathname(work))) Directory in which to write the
|
||||
outputs (created if non existant)
|
||||
|
||||
@version 9.4
|
||||
@author Allan Bowe
|
||||
@@ -35,7 +36,8 @@
|
||||
%let fname2=%mf_getuniquefileref();
|
||||
%let fname3=%mf_getuniquefileref();
|
||||
|
||||
filename &fname1 ZIP &ziploc; * Macro variable &datazip would be read from the file*;
|
||||
/* Macro variable &datazip would be read from the file */
|
||||
filename &fname1 ZIP &ziploc;
|
||||
|
||||
/* Read the "members" (files) from the ZIP file */
|
||||
data _data_(keep=memname isFolder);
|
||||
|
||||
37
base/mp_wait4file.sas
Normal file
37
base/mp_wait4file.sas
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
@file
|
||||
@brief Wait until a file arrives before continuing execution
|
||||
@details Loops with a `sleep()` command until a file arrives or the max wait
|
||||
period expires.
|
||||
|
||||
@example
|
||||
|
||||
Wait 3 minutes OR for /tmp/flag.txt to appear
|
||||
|
||||
%mp_wait4file(/tmp/flag.txt , maxwait=60*3)
|
||||
|
||||
@param [in] file The file to wait for. Must be provided.
|
||||
@param [in] maxwait= (0) Number of seconds to wait. If set to zero, will
|
||||
loop indefinitely (to a maximum of 46 days, per SAS [documentation](
|
||||
https://support.sas.com/documentation/cdl/en/lrdict/64316/HTML/default/viewer.htm#a001418809.htm
|
||||
)). Otherwise, execution will proceed upon sleep expiry.
|
||||
@param [in] interval= (1) The wait period between sleeps, in seconds
|
||||
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_wait4file(file, maxwait=0, interval=1);
|
||||
|
||||
%if %str(&file)=%str() %then %do;
|
||||
%put %str(ERR)OR: file not provided;
|
||||
%end;
|
||||
|
||||
data _null_;
|
||||
maxwait=&maxwait;
|
||||
if maxwait=0 then maxwait=60*60*24*46;
|
||||
do until (fileexist("&file") or slept>maxwait );
|
||||
slept=sum(slept,sleep(&interval,1));
|
||||
end;
|
||||
run;
|
||||
|
||||
%mend mp_wait4file;
|
||||
@@ -16,11 +16,18 @@
|
||||
@li mp_dirlist.sas
|
||||
|
||||
@param in= unquoted filepath, dataset of files or directory to zip
|
||||
@param type= FILE, DATASET, DIRECTORY. (FILE / DATASET not ready yet)
|
||||
@param outname= output file to create, without .zip extension
|
||||
@param outpath= location for output zip file
|
||||
@param type= (FILE) Valid values:
|
||||
@li FILE - /full/path/and/filename.extension to a particular file
|
||||
@li DATASET - a dataset containing a list of files to zip (see `incol`)
|
||||
@li DIRECTORY - a directory to zip
|
||||
@param outname= (FILE) Output file to create, _without_ .zip extension
|
||||
@param outpath= (%sysfunc(pathname(WORK))) Parent folder for output zip file
|
||||
@param incol= if DATASET input, say which column contains the filepath
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_unzip.sas
|
||||
@li mp_zip.test.sas
|
||||
|
||||
@version 9.2
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
@@ -51,9 +58,9 @@ ods package open nopf;
|
||||
set &ds;
|
||||
length __command $4000;
|
||||
if file_or_folder='file';
|
||||
command=cats('ods package add file="',filepath
|
||||
__command=cats('ods package add file="',filepath
|
||||
,'" mimetype="application/x-compress";');
|
||||
call execute(command);
|
||||
call execute(__command);
|
||||
run;
|
||||
/* tidy up */
|
||||
%if &debug=NO %then %do;
|
||||
@@ -64,11 +71,10 @@ ods package open nopf;
|
||||
data _null_;
|
||||
set ∈
|
||||
length __command $4000;
|
||||
command=cats('ods package add file="',&incol
|
||||
__command=cats('ods package add file="',&incol
|
||||
,'" mimetype="application/x-compress";');
|
||||
call execute(command);
|
||||
call execute(__command);
|
||||
run;
|
||||
ods package add file="&in" mimetype="application/x-compress";
|
||||
%end;
|
||||
|
||||
|
||||
|
||||
153
package-lock.json
generated
153
package-lock.json
generated
@@ -7,43 +7,42 @@
|
||||
"name": "@sasjs/core",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@sasjs/cli": "^2.37.8"
|
||||
"@sasjs/cli": "^2.39.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/adapter": {
|
||||
"version": "2.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.10.4.tgz",
|
||||
"integrity": "sha512-9b3WGZUzRgszZVyPfrabSx6TycL4JFFXQR/RQKsFCDDwT8UgLGfJ4JmgaGCSmGCcsqML/Q41QipdgWu1M/QA3g==",
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.12.0.tgz",
|
||||
"integrity": "sha512-zzIuohhR8KUDl3DfIFOW38gv3LADPnOBCLOvLoKu4hH5R/UJDkjZ/Gdgc8B35vI7aOprYOLK/T5D/Z44OaTkqw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@sasjs/utils": "^2.27.1",
|
||||
"axios": "^0.21.1",
|
||||
"@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"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=15"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/cli": {
|
||||
"version": "2.37.8",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.37.8.tgz",
|
||||
"integrity": "sha512-V1FGK0ByS5v5+xOX5J1rlQDOXSA37YLL18etEUQOmWK5X9R5tbK4LPIx/pEjLyfZcCHddrm+0nGzRroC715Ilg==",
|
||||
"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.10.4",
|
||||
"@sasjs/core": "2.42.0",
|
||||
"@sasjs/adapter": "2.12.0",
|
||||
"@sasjs/core": "2.45.2",
|
||||
"@sasjs/lint": "1.11.2",
|
||||
"@sasjs/utils": "2.28.0",
|
||||
"@sasjs/utils": "2.32.0",
|
||||
"chalk": "4.1.2",
|
||||
"csv-stringify": "5.6.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.6.1",
|
||||
"js-base64": "3.7.2",
|
||||
"jsdom": "17.0.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"lodash.groupby": "4.6.0",
|
||||
@@ -53,16 +52,16 @@
|
||||
"rimraf": "3.0.2",
|
||||
"shelljs": "0.8.4",
|
||||
"xml": "1.0.1",
|
||||
"yargs": "17.1.1"
|
||||
"yargs": "17.2.1"
|
||||
},
|
||||
"bin": {
|
||||
"sasjs": "build/index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/core": {
|
||||
"version": "2.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.42.0.tgz",
|
||||
"integrity": "sha512-EISPPCEv+vB3Nqp03NUaIvMZwEnggzGJeEpVfm2Sb504ySN1I2xiJ8bOHXIzvvYP5N/V9X8bXe1raLYnnt8HGA==",
|
||||
"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": {
|
||||
@@ -75,24 +74,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sasjs/utils": {
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.28.0.tgz",
|
||||
"integrity": "sha512-aZAbBDSjvW2TCY1lkuGqqiyWtR4W8GFpCNCUKa72VIkl4zwTE/pnSHQ+v8QilGDSHSPbPEsJGaBaldMGwoI12w==",
|
||||
"version": "2.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.32.0.tgz",
|
||||
"integrity": "sha512-xnvdEuI4PhTtulcdDEIMK7IxVj9bOMU1JTnxRuSEKWcsclY9P9Fw3cnMOOEgXCDffrOPn3f54DP7Wb1GXd+f8g==",
|
||||
"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",
|
||||
"fs-extra": "^10.0.0",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"prompts": "^2.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"valid-url": "^1.0.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=15"
|
||||
}
|
||||
},
|
||||
"node_modules/@tootallnate/once": {
|
||||
@@ -197,9 +195,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -478,9 +476,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/csv-stringify": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.2.tgz",
|
||||
"integrity": "sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A==",
|
||||
"version": "5.6.5",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz",
|
||||
"integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
@@ -679,9 +677,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz",
|
||||
"integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==",
|
||||
"version": "1.14.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -1031,9 +1029,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/js-base64": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz",
|
||||
"integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
"integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jsdom": {
|
||||
@@ -1806,9 +1804,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz",
|
||||
"integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==",
|
||||
"version": "17.2.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz",
|
||||
"integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cliui": "^7.0.2",
|
||||
@@ -1835,13 +1833,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sasjs/adapter": {
|
||||
"version": "2.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.10.4.tgz",
|
||||
"integrity": "sha512-9b3WGZUzRgszZVyPfrabSx6TycL4JFFXQR/RQKsFCDDwT8UgLGfJ4JmgaGCSmGCcsqML/Q41QipdgWu1M/QA3g==",
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/adapter/-/adapter-2.12.0.tgz",
|
||||
"integrity": "sha512-zzIuohhR8KUDl3DfIFOW38gv3LADPnOBCLOvLoKu4hH5R/UJDkjZ/Gdgc8B35vI7aOprYOLK/T5D/Z44OaTkqw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@sasjs/utils": "^2.27.1",
|
||||
"axios": "^0.21.1",
|
||||
"@sasjs/utils": "^2.32.0",
|
||||
"axios": "^0.21.4",
|
||||
"axios-cookiejar-support": "^1.0.1",
|
||||
"form-data": "^4.0.0",
|
||||
"https": "^1.0.0",
|
||||
@@ -1849,22 +1847,22 @@
|
||||
}
|
||||
},
|
||||
"@sasjs/cli": {
|
||||
"version": "2.37.8",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/cli/-/cli-2.37.8.tgz",
|
||||
"integrity": "sha512-V1FGK0ByS5v5+xOX5J1rlQDOXSA37YLL18etEUQOmWK5X9R5tbK4LPIx/pEjLyfZcCHddrm+0nGzRroC715Ilg==",
|
||||
"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,
|
||||
"requires": {
|
||||
"@sasjs/adapter": "2.10.4",
|
||||
"@sasjs/core": "2.42.0",
|
||||
"@sasjs/adapter": "2.12.0",
|
||||
"@sasjs/core": "2.45.2",
|
||||
"@sasjs/lint": "1.11.2",
|
||||
"@sasjs/utils": "2.28.0",
|
||||
"@sasjs/utils": "2.32.0",
|
||||
"chalk": "4.1.2",
|
||||
"csv-stringify": "5.6.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.6.1",
|
||||
"js-base64": "3.7.2",
|
||||
"jsdom": "17.0.0",
|
||||
"jwt-decode": "3.1.2",
|
||||
"lodash.groupby": "4.6.0",
|
||||
@@ -1874,13 +1872,13 @@
|
||||
"rimraf": "3.0.2",
|
||||
"shelljs": "0.8.4",
|
||||
"xml": "1.0.1",
|
||||
"yargs": "17.1.1"
|
||||
"yargs": "17.2.1"
|
||||
}
|
||||
},
|
||||
"@sasjs/core": {
|
||||
"version": "2.42.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-2.42.0.tgz",
|
||||
"integrity": "sha512-EISPPCEv+vB3Nqp03NUaIvMZwEnggzGJeEpVfm2Sb504ySN1I2xiJ8bOHXIzvvYP5N/V9X8bXe1raLYnnt8HGA==",
|
||||
"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
|
||||
},
|
||||
"@sasjs/lint": {
|
||||
@@ -1893,9 +1891,9 @@
|
||||
}
|
||||
},
|
||||
"@sasjs/utils": {
|
||||
"version": "2.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@sasjs/utils/-/utils-2.28.0.tgz",
|
||||
"integrity": "sha512-aZAbBDSjvW2TCY1lkuGqqiyWtR4W8GFpCNCUKa72VIkl4zwTE/pnSHQ+v8QilGDSHSPbPEsJGaBaldMGwoI12w==",
|
||||
"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",
|
||||
@@ -1903,6 +1901,7 @@
|
||||
"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",
|
||||
@@ -1993,9 +1992,9 @@
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
@@ -2198,9 +2197,9 @@
|
||||
}
|
||||
},
|
||||
"csv-stringify": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.2.tgz",
|
||||
"integrity": "sha512-n3rIVbX6ylm1YsX2NEug9IaPV8xRnT+9/NNZbrA/bcHgOSSeqtWla6XnI/xmyu57wIw+ASCAoX1oM6EZtqJV0A==",
|
||||
"version": "5.6.5",
|
||||
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.6.5.tgz",
|
||||
"integrity": "sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==",
|
||||
"dev": true
|
||||
},
|
||||
"data-urls": {
|
||||
@@ -2347,9 +2346,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.3",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz",
|
||||
"integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==",
|
||||
"version": "1.14.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
|
||||
"dev": true
|
||||
},
|
||||
"form-data": {
|
||||
@@ -2600,9 +2599,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz",
|
||||
"integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz",
|
||||
"integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"jsdom": {
|
||||
@@ -3192,9 +3191,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz",
|
||||
"integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==",
|
||||
"version": "17.2.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz",
|
||||
"integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
|
||||
@@ -33,6 +33,6 @@
|
||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sasjs/cli": "^2.37.8"
|
||||
"@sasjs/cli": "^2.39.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,10 @@
|
||||
"appLoc": "/Shared Data/temp/macrocore",
|
||||
"macroFolders": [
|
||||
"tests/sas9only"
|
||||
]
|
||||
],
|
||||
"deployConfig": {
|
||||
"deployServicePack": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "docsonly",
|
||||
|
||||
50
tests/crossplatform/mp_dirlist.test.sas
Normal file
50
tests/crossplatform/mp_dirlist.test.sas
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_ds2cards.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mf_mkdir.sas
|
||||
@li mp_dirlist.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
/**
|
||||
* make a directory structure
|
||||
*/
|
||||
|
||||
%let root=%sysfunc(pathname(work))/top;
|
||||
%mf_mkdir(&root)
|
||||
%mf_mkdir(&root/a)
|
||||
%mf_mkdir(&root/b)
|
||||
%mf_mkdir(&root/a/d)
|
||||
%mf_mkdir(&root/a/e)
|
||||
%mf_mkdir(&root/a/e/f)
|
||||
data "&root/a/e/f/ds1.sas7bdat";
|
||||
x=1;
|
||||
run;
|
||||
|
||||
%mp_dirlist(path=&root, outds=myTable, maxdepth=MAX)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.mytable)=6),
|
||||
desc=All levels returned,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_dirlist(path=&root, outds=myTable2, maxdepth=2)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.mytable2)=5),
|
||||
desc=Top two levels returned,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_dirlist(path=&root, outds=myTable3, maxdepth=0)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.mytable3)=2),
|
||||
desc=Top level returned,
|
||||
outds=work.test_results
|
||||
)
|
||||
29
tests/crossplatform/mp_getconstraints.test.sas
Normal file
29
tests/crossplatform/mp_getconstraints.test.sas
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_getconstraints.sas macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_nobs.sas
|
||||
@li mp_getconstraints.sas
|
||||
@li mp_assert.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)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_nobs(work.constraints)=6),
|
||||
desc=Output table work.constraints created with correct number of records,
|
||||
outds=work.test_results
|
||||
)
|
||||
62
tests/crossplatform/mp_lockanytable.test.sas
Normal file
62
tests/crossplatform/mp_lockanytable.test.sas
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_lockfilecheck macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_lockanytable.sas
|
||||
@li mp_assertcols.sas
|
||||
@li mp_assertcolvals.sas
|
||||
|
||||
**/
|
||||
|
||||
/* check create table */
|
||||
|
||||
%mp_lockanytable(MAKETABLE, ctl_ds=work.controller)
|
||||
|
||||
%mp_assertcols(work.controller,
|
||||
cols=lock_status_cd lock_lib lock_ds lock_user_nm lock_ref lock_pid
|
||||
lock_start_dttm lock_end_dttm,
|
||||
test=ALL,
|
||||
desc=check all control columns exist
|
||||
)
|
||||
|
||||
/* check lock table */
|
||||
options dlcreatedir;
|
||||
libname tmp "%sysfunc(pathname(work))/tmp";
|
||||
data tmp.sometable;
|
||||
x=1;
|
||||
run;
|
||||
|
||||
%mp_lockanytable(LOCK,lib=tmp,ds=sometable,ref=This Ref, ctl_ds=work.controller)
|
||||
|
||||
data work.checkds1;
|
||||
checkval='SOMETABLE';
|
||||
run;
|
||||
%mp_assertcolvals(work.controller.lock_ds,
|
||||
checkvals=work.checkds1.checkval,
|
||||
desc=table is captured in lock,
|
||||
test=ANYVAL
|
||||
)
|
||||
|
||||
data work.checkds2;
|
||||
checkval='LOCKED';
|
||||
run;
|
||||
%mp_assertcolvals(work.controller.lock_status_cd,
|
||||
checkvals=work.checkds2.checkval,
|
||||
desc=code is captured in lock,
|
||||
test=ANYVAL
|
||||
)
|
||||
|
||||
|
||||
|
||||
/* check for unsuccessful unlock */
|
||||
%mp_lockanytable(UNLOCK,lib=tmp,ds=sometable,ref=bye, ctl_ds=work.controller)
|
||||
|
||||
data work.checkds3;
|
||||
checkval='UNLOCKED';
|
||||
run;
|
||||
%mp_assertcolvals(work.controller.lock_status_cd,
|
||||
checkvals=work.checkds3.checkval,
|
||||
desc=Ref is captured in unlock,
|
||||
test=ANYVAL
|
||||
)
|
||||
36
tests/crossplatform/mp_lockfilecheck.test.sas
Normal file
36
tests/crossplatform/mp_lockfilecheck.test.sas
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_lockfilecheck macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_lockfilecheck.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
/* check for regular lock */
|
||||
data work.test; a=1;run;
|
||||
%mp_lockfilecheck(work.test)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking regular table can be locked,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
/* check for unsuccessful lock */
|
||||
%global success abortme;
|
||||
%let success=0;
|
||||
%macro mp_abort(iftrue=,mac=,msg=);
|
||||
%if &abortme=1 %then %let success=1;
|
||||
%mend mp_abort;
|
||||
|
||||
%mp_lockfilecheck(sashelp.class)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&success=1),
|
||||
desc=Checking sashelp table cannot be locked,
|
||||
outds=work.test_results
|
||||
)
|
||||
115
tests/crossplatform/mp_zip.test.sas
Normal file
115
tests/crossplatform/mp_zip.test.sas
Normal file
@@ -0,0 +1,115 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_zip macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_mkdir.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_zip.sas
|
||||
@li mp_unzip.sas
|
||||
|
||||
**/
|
||||
|
||||
%let work=%sysfunc(pathname(work));
|
||||
%let root=&work/zipme;
|
||||
|
||||
/* TEST 1 - zip a file */
|
||||
%mf_mkdir(&root)
|
||||
|
||||
data _null_;
|
||||
file "&root/test.txt";
|
||||
put "houston, this is a test";
|
||||
run;
|
||||
|
||||
%mp_zip(in=&root/test.txt
|
||||
,type=FILE
|
||||
,outpath=&work
|
||||
,outname=myFile
|
||||
)
|
||||
|
||||
%mp_unzip(ziploc="&work/myFile.zip",outdir=&work)
|
||||
|
||||
data _null_;
|
||||
infile "&work/test.txt";
|
||||
input;
|
||||
call symputx('content1',_infile_);
|
||||
putlog _infile_;
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
%str(&content1)=%str(houston, this is a test)
|
||||
),
|
||||
desc=Checking if file zip / unzip works,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* TEST 2 - zip a dataset of files */
|
||||
data _null_;
|
||||
file "&root/test2.txt";
|
||||
put "houston, this is test2";
|
||||
run;
|
||||
libname tmp "&root";
|
||||
data tmp.test;
|
||||
filepath="&root/test2.txt";
|
||||
run;
|
||||
|
||||
%mp_zip(in=tmp.test
|
||||
,incol=filepath
|
||||
,type=DATASET
|
||||
,outpath=&work
|
||||
,outname=myFile2
|
||||
)
|
||||
|
||||
%mp_unzip(ziploc="&work/myFile2.zip",outdir=&work)
|
||||
|
||||
data _null_;
|
||||
infile "&work/test2.txt";
|
||||
input;
|
||||
call symputx('content2',_infile_);
|
||||
putlog _infile_;
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
%str(&content2)=%str(houston, this is test2)
|
||||
),
|
||||
desc=Checking if file zip / unzip from a dataset works,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* TEST 3 - zip a dataset of files */
|
||||
%mf_mkdir(&work/out3)
|
||||
|
||||
%mp_zip(in=&root
|
||||
,type=DIRECTORY
|
||||
,outpath=&work
|
||||
,outname=myFile3
|
||||
)
|
||||
|
||||
%mp_unzip(ziploc="&work/myFile3.zip",outdir=&work/out3)
|
||||
|
||||
data _null_;
|
||||
infile "&work/out3/test.txt";
|
||||
input;
|
||||
call symputx('content3a',_infile_);
|
||||
putlog _infile_;
|
||||
run;
|
||||
data _null_;
|
||||
infile "&work/out3/test2.txt";
|
||||
input;
|
||||
call symputx('content3b',_infile_);
|
||||
putlog _infile_;
|
||||
run;
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
%str(&content3a)=%str(houston, this is a test)
|
||||
and
|
||||
%str(&content3b)=%str(houston, this is test2)
|
||||
),
|
||||
desc=Checking if file zip / unzip from a directory works,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
@@ -5,4 +5,12 @@
|
||||
**/
|
||||
|
||||
/* location in metadata or SAS Drive for temporary files */
|
||||
%let mcTestAppLoc=/Public/temp/macrocore;
|
||||
%let mcTestAppLoc=/Public/temp/macrocore;
|
||||
|
||||
%macro loglevel();
|
||||
%if &_debug=2477 %then %do;
|
||||
options mprint;
|
||||
%end;
|
||||
%mend loglevel;
|
||||
|
||||
%loglevel()
|
||||
Reference in New Issue
Block a user