mirror of
https://github.com/sasjs/core.git
synced 2025-12-20 01:34:35 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0a0274990 | ||
|
|
b3d374f1b1 | ||
|
|
1c4458faf6 | ||
|
|
96e1d146f4 | ||
|
|
aadc4fb83d | ||
|
|
988ee89cdb | ||
|
|
51cbfbf4bc | ||
|
|
4b69e91362 | ||
|
|
8f9715035a | ||
|
|
35ddccaa16 | ||
|
|
cb0ddfb61c | ||
|
|
c3b6f06b3a | ||
|
|
8046d5a0b1 | ||
|
|
aed07f2943 | ||
|
|
5bf87a78b8 | ||
|
|
0851523d18 | ||
|
|
9e2de81dae | ||
|
|
4887f355c8 | ||
|
|
9b32e6e3f2 | ||
|
|
74790ec80e | ||
|
|
afd8a754b4 | ||
|
|
bc1f7b3baa | ||
|
|
51690e68dc | ||
|
|
0fa076cb73 | ||
|
|
6506993704 | ||
|
|
a69db2ebfb | ||
|
|
d72ca7cb24 | ||
|
|
52dfa7b8f7 | ||
|
|
dae03c5730 | ||
|
|
14efe5d3fd | ||
|
|
653244d737 | ||
|
|
086831b3f5 | ||
|
|
6eca585fc1 | ||
|
|
f6ba36fc28 | ||
|
|
7406288d79 | ||
|
|
2e7fcbe5b8 | ||
|
|
3e7b9f8c14 | ||
|
|
e9189ccc06 | ||
|
|
8c00d715c2 | ||
|
|
d47a369cdf | ||
|
|
52bf6019fd | ||
|
|
25e61fd8ef | ||
|
|
3038be83a0 | ||
|
|
6e2447c70a | ||
|
|
486aba84ca | ||
|
|
b5944181e1 | ||
|
|
3f69cf506a | ||
|
|
6013897c50 |
@@ -98,7 +98,18 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"ideas"
|
"ideas"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "VladislavParhomchik",
|
||||||
|
"name": "Vladislav Parhomchik",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/83717836?v=4",
|
||||||
|
"profile": "https://github.com/VladislavParhomchik",
|
||||||
|
"contributions": [
|
||||||
|
"test",
|
||||||
|
"review"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"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:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [12.x]
|
node-version: [lts/fermium]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
|
|||||||
19
.gitpod.yml
19
.gitpod.yml
@@ -1,8 +1,25 @@
|
|||||||
tasks:
|
tasks:
|
||||||
- init: nvm install --latest-npm && npm i -g @sasjs/cli
|
- init: nvm install --lts && npm i -g @sasjs/cli
|
||||||
|
|
||||||
image:
|
image:
|
||||||
file: .gitpod.dockerfile
|
file: .gitpod.dockerfile
|
||||||
vscode:
|
vscode:
|
||||||
extensions:
|
extensions:
|
||||||
- sasjs.sasjs-for-vscode
|
- 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
|
main.dox
|
||||||
make_singlefile.sh
|
make_singlefile.sh
|
||||||
*.md
|
*.md
|
||||||
|
.all-contributorsrc
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
|
|||||||
|
|
||||||
## Contributors ✨
|
## Contributors ✨
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
@@ -208,6 +208,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|||||||
</tr>
|
</tr>
|
||||||
<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/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>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|||||||
494
all.sas
494
all.sas
@@ -703,39 +703,60 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Assigns and returns an unused fileref
|
@brief Assigns and returns an unused fileref
|
||||||
@details
|
@details Using the native approach for assigning filerefs fails as some
|
||||||
|
procedures (such as proc http) do not recognise the temporary names (starting
|
||||||
|
with a hash), returning a message such as:
|
||||||
|
|
||||||
|
> ERROR 22-322: Expecting a name.
|
||||||
|
|
||||||
|
This macro works by attempting a random fileref (with a prefix), seeing if it
|
||||||
|
is already assigned, and if not - returning the fileref.
|
||||||
|
|
||||||
|
If your process can accept filerefs with the hash (#) prefix, then set
|
||||||
|
`prefix=0` to revert to the native approach - which is significantly faster
|
||||||
|
when there are a lot of filerefs in a session.
|
||||||
|
|
||||||
Use as follows:
|
Use as follows:
|
||||||
|
|
||||||
%let fileref1=%mf_getuniquefileref();
|
%let fileref1=%mf_getuniquefileref();
|
||||||
%let fileref2=%mf_getuniquefileref();
|
%let fileref2=%mf_getuniquefileref(prefix=0);
|
||||||
%put &fileref1 &fileref2;
|
%put &fileref1 &fileref2;
|
||||||
|
|
||||||
which returns:
|
which returns filerefs similar to:
|
||||||
|
|
||||||
> mcref0 mcref1
|
> _7432233 #LN00070
|
||||||
|
|
||||||
@param prefix= first part of fileref. Remember that filerefs can only be 8
|
@param [in] prefix= (_) first part of fileref. Remember that filerefs can only
|
||||||
characters, so a 7 letter prefix would mean that `maxtries` should be 10.
|
be 8 characters, so a 7 letter prefix would mean `maxtries` should be 10.
|
||||||
@param maxtries= the last part of the libref. Provide an integer value.
|
if using zero (0) as the prefix, a native assignment is used.
|
||||||
|
@param [in] maxtries= (1000) the last part of the libref. Must be an integer.
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_getuniquefileref(prefix=mcref,maxtries=1000);
|
%macro mf_getuniquefileref(prefix=_,maxtries=1000);
|
||||||
%local x fname;
|
%local rc fname;
|
||||||
%let x=0;
|
%if &prefix=0 %then %do;
|
||||||
%do x=0 %to &maxtries;
|
|
||||||
%if %sysfunc(fileref(&prefix&x)) > 0 %then %do;
|
|
||||||
%let fname=&prefix&x;
|
|
||||||
%let rc=%sysfunc(filename(fname,,temp));
|
%let rc=%sysfunc(filename(fname,,temp));
|
||||||
%if &rc %then %put %sysfunc(sysmsg());
|
%if &rc %then %put %sysfunc(sysmsg());
|
||||||
&prefix&x
|
&fname
|
||||||
%*put &sysmacroname: Fileref &prefix&x was assigned and returned;
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%local x len;
|
||||||
|
%let len=%eval(8-%length(&prefix));
|
||||||
|
%let x=0;
|
||||||
|
%do x=0 %to &maxtries;
|
||||||
|
%let fname=&prefix%substr(%sysfunc(ranuni(0)),3,&len);
|
||||||
|
%if %sysfunc(fileref(&fname)) > 0 %then %do;
|
||||||
|
%let rc=%sysfunc(filename(fname,,temp));
|
||||||
|
%if &rc %then %put %sysfunc(sysmsg());
|
||||||
|
&fname
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%put unable to find available fileref in range &prefix.0-&maxtries;
|
%put unable to find available fileref after &maxtries attempts;
|
||||||
|
%end;
|
||||||
%mend mf_getuniquefileref;/**
|
%mend mf_getuniquefileref;/**
|
||||||
@file
|
@file
|
||||||
@brief Returns an unused libref
|
@brief Returns an unused libref
|
||||||
@@ -954,7 +975,8 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
%put dataset &libds not opened! (rc=&dsid);
|
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||||
|
%put &sysmacroname: %sysfunc(sysmsg());
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
@@ -1016,7 +1038,11 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
|||||||
%let vlen = %str( );
|
%let vlen = %str( );
|
||||||
%end;
|
%end;
|
||||||
%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 */
|
/* Close dataset */
|
||||||
%let rc = %sysfunc(close(&dsid));
|
%let rc = %sysfunc(close(&dsid));
|
||||||
@@ -1140,7 +1166,11 @@ returns:
|
|||||||
%let vnum = %str( );
|
%let vnum = %str( );
|
||||||
%end;
|
%end;
|
||||||
%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 */
|
/* Close dataset */
|
||||||
%let rc = %sysfunc(close(&dsid));
|
%let rc = %sysfunc(close(&dsid));
|
||||||
@@ -1189,7 +1219,11 @@ Usage:
|
|||||||
%let vtype = %str( );
|
%let vtype = %str( );
|
||||||
%end;
|
%end;
|
||||||
%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 */
|
/* Close dataset */
|
||||||
%let rc = %sysfunc(close(&dsid));
|
%let rc = %sysfunc(close(&dsid));
|
||||||
@@ -1785,7 +1819,7 @@ Usage:
|
|||||||
/* send response in SASjs JSON format */
|
/* send response in SASjs JSON format */
|
||||||
data _null_;
|
data _null_;
|
||||||
file _webout mod lrecl=32000 encoding='utf-8';
|
file _webout mod lrecl=32000 encoding='utf-8';
|
||||||
length msg $32767 debug $8;
|
length msg $32767 ;
|
||||||
sasdatetime=datetime();
|
sasdatetime=datetime();
|
||||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
||||||
/* escape the quotes */
|
/* escape the quotes */
|
||||||
@@ -1796,7 +1830,7 @@ Usage:
|
|||||||
msg=cats('"',msg,'"');
|
msg=cats('"',msg,'"');
|
||||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||||
else debug='""';
|
else debug='""';
|
||||||
if debug ge '"131"' then put '>>weboutBEGIN<<';
|
put '>>weboutBEGIN<<';
|
||||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||||
put ',"sasjsAbort" : [{';
|
put ',"sasjsAbort" : [{';
|
||||||
put ' "MSG":' msg ;
|
put ' "MSG":' msg ;
|
||||||
@@ -1816,23 +1850,26 @@ Usage:
|
|||||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||||
put ",""SYSCC"" : ""&syscc"" ";
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
syserrortext=quote(trim(symget('syserrortext')));
|
||||||
|
put ",""SYSERRORTEXT"" : " syserrortext;
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
syswarningtext=quote(trim(symget('syswarningtext')));
|
||||||
|
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||||
put "}" @;
|
put "}" @;
|
||||||
if debug ge '"131"' then put '>>weboutEND<<';
|
put '>>weboutEND<<';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%put _all_;
|
%put _all_;
|
||||||
|
|
||||||
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
putlog 'stpsrvset program error and syscc';
|
putlog 'stpsrvset program err and syscc';
|
||||||
rc=stpsrvset('program error', 0);
|
rc=stpsrvset('program error', 0);
|
||||||
call symputx("syscc",0,"g");
|
call symputx("syscc",0,"g");
|
||||||
run;
|
run;
|
||||||
@@ -1878,6 +1915,62 @@ Usage:
|
|||||||
%mend mp_abort;
|
%mend mp_abort;
|
||||||
|
|
||||||
/** @endcond *//**
|
/** @endcond *//**
|
||||||
|
@file
|
||||||
|
@brief Append (concatenate) two or more files.
|
||||||
|
@details Will append one more more `appendrefs` (filerefs) to a `baseref`.
|
||||||
|
Uses a binary mechanism, so will work with any file type. For that reason -
|
||||||
|
use with care! And supply your own trailing carriage returns in each file..
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
filename tmp1 temp;
|
||||||
|
filename tmp2 temp;
|
||||||
|
filename tmp3 temp;
|
||||||
|
data _null_; file tmp1; put 'base file';
|
||||||
|
data _null_; file tmp2; put 'append1';
|
||||||
|
data _null_; file tmp3; put 'append2';
|
||||||
|
run;
|
||||||
|
%mp_appendfile(baseref=tmp1, appendrefs=tmp2 tmp3)
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] baseref= Fileref of the base file (should exist)
|
||||||
|
@param [in] appendrefs= One or more filerefs to be appended to the base
|
||||||
|
fileref. Space separated.
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_binarycopy.sas
|
||||||
|
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_appendfile(
|
||||||
|
baseref=0,
|
||||||
|
appendrefs=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&baseref=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Baseref NOT specified!)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue= (&appendrefs=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Appendrefs NOT specified!)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local i;
|
||||||
|
%do i=1 %to %sysfunc(countw(&appendrefs));
|
||||||
|
%mp_abort(iftrue= (&syscc>0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(syscc=&syscc)
|
||||||
|
)
|
||||||
|
%mp_binarycopy(inref=%scan(&appendrefs,&i), outref=&baseref, mode=APPEND)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mp_appendfile;/**
|
||||||
@file
|
@file
|
||||||
@brief Generic assertion
|
@brief Generic assertion
|
||||||
@details Useful in the context of writing sasjs tests. The results of the
|
@details Useful in the context of writing sasjs tests. The results of the
|
||||||
@@ -2108,6 +2201,7 @@ Usage:
|
|||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existds.sas
|
@li mf_existds.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
@@ -2193,6 +2287,26 @@ Usage:
|
|||||||
select count(*) into: orig from &lib..&ds;
|
select count(*) into: orig from &lib..&ds;
|
||||||
quit;
|
quit;
|
||||||
|
|
||||||
|
%local notfound tmp1 tmp2;
|
||||||
|
%let tmp1=%mf_getuniquename();
|
||||||
|
%let tmp2=%mf_getuniquename();
|
||||||
|
|
||||||
|
/* this is a bit convoluted - but using sql outobs=10 throws warnings */
|
||||||
|
proc sql noprint;
|
||||||
|
create view &tmp1 as
|
||||||
|
select distinct &col
|
||||||
|
from &lib..&ds
|
||||||
|
where &col not in (
|
||||||
|
select &ccol from &clib..&cds
|
||||||
|
);
|
||||||
|
data &tmp2;
|
||||||
|
set &tmp1;
|
||||||
|
if _n_>10 then stop;
|
||||||
|
run;
|
||||||
|
proc sql;
|
||||||
|
select distinct &col into: notfound separated by ' ' from &tmp2;
|
||||||
|
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(syscc=&syscc after macro query)
|
,msg=%str(syscc=&syscc after macro query)
|
||||||
@@ -2203,7 +2317,7 @@ Usage:
|
|||||||
test_description=symget('desc');
|
test_description=symget('desc');
|
||||||
test_result='FAIL';
|
test_result='FAIL';
|
||||||
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
|
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
|
||||||
!!"not in &clib..&cds..&ccol ";
|
!!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
|
||||||
%if &test=ANYVAL %then %do;
|
%if &test=ANYVAL %then %do;
|
||||||
if &result < &orig then test_result='PASS';
|
if &result < &orig then test_result='PASS';
|
||||||
%end;
|
%end;
|
||||||
@@ -3214,12 +3328,21 @@ run;
|
|||||||
@file
|
@file
|
||||||
@brief Create a CARDS file from a SAS dataset.
|
@brief Create a CARDS file from a SAS dataset.
|
||||||
@details Uses dataset attributes to convert all data into datalines.
|
@details Uses dataset attributes to convert all data into datalines.
|
||||||
Running the generated file will rebuild the original dataset.
|
Running the generated file will rebuild the original dataset. Includes
|
||||||
|
support for large decimals, binary data, PROCESSED_DTTM columns, and
|
||||||
|
alternative encoding. If the input dataset is empty, the cards file will
|
||||||
|
still be created.
|
||||||
|
|
||||||
|
Additional support to generate a random sample and max rows.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mp_ds2cards(base_ds=sashelp.class
|
%mp_ds2cards(base_ds=sashelp.class
|
||||||
|
, tgt_ds=work.class
|
||||||
, cards_file= "C:\temp\class.sas"
|
, cards_file= "C:\temp\class.sas"
|
||||||
, maxobs=5)
|
, showlog=NO
|
||||||
|
, maxobs=5
|
||||||
|
)
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
- labelling the dataset
|
- labelling the dataset
|
||||||
@@ -3230,15 +3353,24 @@ run;
|
|||||||
that is converted to a cards file.
|
that is converted to a cards file.
|
||||||
@param [in] tgt_ds= Table that the generated cards file would create.
|
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||||
Optional - if omitted, will be same as BASE_DS.
|
Optional - if omitted, will be same as BASE_DS.
|
||||||
@param [out] cards_file= Location in which to write the (.sas) cards file
|
@param [out] cards_file= ("%sysfunc(pathname(work))/cardgen.sas") Location in
|
||||||
@param [in] maxobs= to limit output to the first <code>maxobs</code>
|
which to write the (.sas) cards file
|
||||||
observations
|
@param [in] maxobs= (max) To limit output to the first <code>maxobs</code>
|
||||||
@param [in] showlog= whether to show generated cards file in the SAS log
|
observations, enter an integer here.
|
||||||
(YES/NO)
|
@param [in] random_sample= (NO) Set to YES to generate a random sample of
|
||||||
@param [in] outencoding= provide encoding value for file statement (eg utf-8)
|
data. Can be quite slow.
|
||||||
@param [in] append= If NO then will rebuild the cards file if it already
|
@param [in] showlog= (YES) Whether to show generated cards file in the SAS
|
||||||
|
log. Valid values:
|
||||||
|
@li YES
|
||||||
|
@li NO
|
||||||
|
@param [in] outencoding= Provide encoding value for file statement (eg utf-8)
|
||||||
|
@param [in] append= (NO) If NO then will rebuild the cards file if it already
|
||||||
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_lib2cards.sas
|
||||||
|
@li mp_ds2inserts.sas
|
||||||
|
@li mp_mdtablewrite.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -3263,15 +3395,15 @@ run;
|
|||||||
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
||||||
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
||||||
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
||||||
%if ("&append" = "") %then %let append=;
|
%if ("&append" = "" or "&append" = "NO") %then %let append=;
|
||||||
%else %let append=mod;
|
%else %let append=mod;
|
||||||
|
|
||||||
/* get varcount */
|
/* get varcount */
|
||||||
%let nvars=0;
|
%let nvars=0;
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select count(*) into: nvars from dictionary.columns
|
select count(*) into: nvars from dictionary.columns
|
||||||
where libname="%scan(%upcase(&base_ds),1)"
|
where upcase(libname)="%scan(%upcase(&base_ds),1)"
|
||||||
and memname="%scan(%upcase(&base_ds),2)";
|
and upcase(memname)="%scan(%upcase(&base_ds),2)";
|
||||||
%if &nvars=0 %then %do;
|
%if &nvars=0 %then %do;
|
||||||
%put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
|
%put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
|
||||||
%return;
|
%return;
|
||||||
@@ -3327,8 +3459,8 @@ proc sql
|
|||||||
reset outobs=max;
|
reset outobs=max;
|
||||||
create table datalines1 as
|
create table datalines1 as
|
||||||
select name,type,length,varnum,format,label from dictionary.columns
|
select name,type,length,varnum,format,label from dictionary.columns
|
||||||
where libname="%upcase(%scan(&base_ds,1))"
|
where upcase(libname)="%upcase(%scan(&base_ds,1))"
|
||||||
and memname="%upcase(%scan(&base_ds,2))";
|
and upcase(memname)="%upcase(%scan(&base_ds,2))";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Due to long decimals cannot use best. format
|
Due to long decimals cannot use best. format
|
||||||
@@ -3349,7 +3481,18 @@ data datalines_2;
|
|||||||
,put(',name,',best32.-l)
|
,put(',name,',best32.-l)
|
||||||
,substrn(put(',name,',bestd32.-l),1
|
,substrn(put(',name,',bestd32.-l),1
|
||||||
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
||||||
else dataline=name;
|
/**
|
||||||
|
* binary data must be converted, to store in text format. It is identified
|
||||||
|
* by the presence of the $HEX keyword in the format.
|
||||||
|
*/
|
||||||
|
else if upcase(format)=:'$HEX' then
|
||||||
|
dataline=cats('put(trim(',name,'),',format,')');
|
||||||
|
/**
|
||||||
|
* 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;
|
run;
|
||||||
|
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
@@ -3374,7 +3517,8 @@ data _null_;
|
|||||||
|
|
||||||
|
|
||||||
/* Build input statement */
|
/* Build input statement */
|
||||||
if type='char' then type3=':$char.';
|
if upcase(format)=:'$HEX' then type3=':'!!format;
|
||||||
|
else if type='char' then type3=':$char.';
|
||||||
str2=put(name,$33.)||type3;
|
str2=put(name,$33.)||type3;
|
||||||
|
|
||||||
|
|
||||||
@@ -3396,11 +3540,12 @@ data _null_;
|
|||||||
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
||||||
length __attrib $32767;
|
length __attrib $32767;
|
||||||
if _n_=1 then do;
|
if _n_=1 then do;
|
||||||
put '/*******************************************************************';
|
put '/**';
|
||||||
put " Datalines for %upcase(%scan(&base_ds,2)) dataset ";
|
put ' @file';
|
||||||
put " Generated by %nrstr(%%)mp_ds2cards()";
|
put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
|
||||||
|
put " @details Generated by %nrstr(%%)mp_ds2cards()";
|
||||||
put " Available on github.com/sasjs/core";
|
put " Available on github.com/sasjs/core";
|
||||||
put '********************************************************************/';
|
put '**/';
|
||||||
put "data &tgt_ds &indexes;";
|
put "data &tgt_ds &indexes;";
|
||||||
put "attrib ";
|
put "attrib ";
|
||||||
%do i = 1 %to &nvars;
|
%do i = 1 %to &nvars;
|
||||||
@@ -3424,7 +3569,7 @@ data _null_;
|
|||||||
put 'run;';
|
put 'run;';
|
||||||
end;
|
end;
|
||||||
else do;
|
else do;
|
||||||
put "infile cards dsd delimiter=',';";
|
put "infile cards dsd;";
|
||||||
put "input ";
|
put "input ";
|
||||||
%do i = 1 %to &nvars.;
|
%do i = 1 %to &nvars.;
|
||||||
%if(%length(&&input_stmt_&i..)) %then
|
%if(%length(&&input_stmt_&i..)) %then
|
||||||
@@ -4207,6 +4352,70 @@ filename &fref1 clear;
|
|||||||
|
|
||||||
%mend mp_filtervalidate;
|
%mend mp_filtervalidate;
|
||||||
/**
|
/**
|
||||||
|
@file
|
||||||
|
@brief Creates a dataset with column metadata.
|
||||||
|
@details This macro takes the `proc contents` output and "tidies it up" in the
|
||||||
|
following ways:
|
||||||
|
|
||||||
|
@li Blank labels are filled in with column names
|
||||||
|
@li Formats are reconstructed with default values
|
||||||
|
@li Types such as DATE / TIME / DATETIME are inferred from the formats
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
%mp_getcols(sashelp.airline,outds=work.myds)
|
||||||
|
|
||||||
|
@param ds The dataset from which to obtain column metadata
|
||||||
|
@param outds= (work.cols) The output dataset to create. Sample data:
|
||||||
|
|NAME $|LENGTH 8|VARNUM 8|LABEL $|FORMAT $49|TYPE $1 |DDTYPE $|
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
|AIR|8|2|international airline travel (thousands)|8.|N|NUMERIC|
|
||||||
|
|DATE|8|1|DATE|MONYY.|N|DATE|
|
||||||
|
|REGION|3|3|REGION|$3.|C|CHARACTER|
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_getvarlist.sas
|
||||||
|
@li mm_getcols.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_getcols(ds, outds=work.cols);
|
||||||
|
|
||||||
|
proc contents noprint data=&ds
|
||||||
|
out=_data_ (keep=name type length label varnum format:);
|
||||||
|
run;
|
||||||
|
data &outds(keep=name type length varnum format label ddtype);
|
||||||
|
set &syslast(rename=(format=format2 type=type2));
|
||||||
|
name=upcase(name);
|
||||||
|
if type2=2 then do;
|
||||||
|
length format $49.;
|
||||||
|
if format2='' then format=cats('$',length,'.');
|
||||||
|
else if formatl=0 then format=cats(format2,'.');
|
||||||
|
else format=cats(format2,formatl,'.');
|
||||||
|
type='C';
|
||||||
|
ddtype='CHARACTER';
|
||||||
|
end;
|
||||||
|
else do;
|
||||||
|
if format2='' then format=cats(length,'.');
|
||||||
|
else if formatl=0 then format=cats(format2,'.');
|
||||||
|
else if formatd=0 then format=cats(format2,formatl,'.');
|
||||||
|
else format=cats(format2,formatl,'.',formatd);
|
||||||
|
type='N';
|
||||||
|
if format=:'DATETIME' then ddtype='DATETIME';
|
||||||
|
else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
|
||||||
|
or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
|
||||||
|
or format=:'MONYY'
|
||||||
|
then ddtype='DATE';
|
||||||
|
else if format=:'TIME' then ddtype='TIME';
|
||||||
|
else ddtype='NUMERIC';
|
||||||
|
end;
|
||||||
|
if label='' then label=name;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend mp_getcols;/**
|
||||||
@file mp_getconstraints.sas
|
@file mp_getconstraints.sas
|
||||||
@brief Get constraint details at column level
|
@brief Get constraint details at column level
|
||||||
@details Useful for capturing constraints before they are dropped / reapplied
|
@details Useful for capturing constraints before they are dropped / reapplied
|
||||||
@@ -4247,21 +4456,24 @@ filename &fref1 clear;
|
|||||||
/* must use SQL as proc datasets does not support length changes */
|
/* must use SQL as proc datasets does not support length changes */
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table &outds as
|
create table &outds as
|
||||||
select a.TABLE_CATALOG as libref
|
select upcase(a.TABLE_CATALOG) as libref
|
||||||
,a.TABLE_NAME
|
,upcase(a.TABLE_NAME) as TABLE_NAME
|
||||||
,a.constraint_type
|
,a.constraint_type
|
||||||
,a.constraint_name
|
,a.constraint_name
|
||||||
,b.column_name
|
,b.column_name
|
||||||
from dictionary.TABLE_CONSTRAINTS a
|
from dictionary.TABLE_CONSTRAINTS a
|
||||||
left join dictionary.constraint_column_usage b
|
left join dictionary.constraint_column_usage b
|
||||||
on a.TABLE_CATALOG=b.TABLE_CATALOG
|
on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
|
||||||
and a.TABLE_NAME=b.TABLE_NAME
|
and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
|
||||||
and a.constraint_name=b.constraint_name
|
and a.constraint_name=b.constraint_name
|
||||||
where a.TABLE_CATALOG="&lib"
|
/**
|
||||||
and 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;
|
%if "&ds" ne "" %then %do;
|
||||||
and a.TABLE_NAME="&ds"
|
and upcase(a.TABLE_NAME)="&ds"
|
||||||
and b.TABLE_NAME="&ds"
|
and upcase(b.TABLE_NAME)="&ds"
|
||||||
%end;
|
%end;
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -4812,7 +5024,7 @@ run;
|
|||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select sysvalue into: schemaactual
|
select sysvalue into: schemaactual
|
||||||
from dictionary.libnames
|
from dictionary.libnames
|
||||||
where libname="&libref" and engine='SQLSVR';
|
where upcase(libname)="&libref" and engine='SQLSVR';
|
||||||
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
||||||
|
|
||||||
%do x=1 %to %sysfunc(countw(&dsnlist));
|
%do x=1 %to %sysfunc(countw(&dsnlist));
|
||||||
@@ -4905,7 +5117,7 @@ run;
|
|||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select sysvalue into: schemaactual
|
select sysvalue into: schemaactual
|
||||||
from dictionary.libnames
|
from dictionary.libnames
|
||||||
where libname="&libref" and engine='POSTGRES';
|
where upcase(libname)="&libref" and engine='POSTGRES';
|
||||||
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
@@ -5071,6 +5283,59 @@ create table &outds (rename=(
|
|||||||
run;
|
run;
|
||||||
|
|
||||||
%mend mp_getmaxvarlengths;/**
|
%mend mp_getmaxvarlengths;/**
|
||||||
|
@file
|
||||||
|
@brief Performs a text substitution on a file
|
||||||
|
@details Makes use of the GSUB function in LUA to perform a text substitution
|
||||||
|
in a file - either in-place, or writing to a new location. The benefit of
|
||||||
|
using LUA is that the entire file can be loaded into a single variable,
|
||||||
|
thereby side stepping the 32767 character limit in a data step.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%let file=%sysfunc(pathname(work))/file.txt;
|
||||||
|
%let str=replace/me;
|
||||||
|
%let rep=with/this;
|
||||||
|
data _null_;
|
||||||
|
file "&file";
|
||||||
|
put "&str";
|
||||||
|
run;
|
||||||
|
%mp_gsubfile(file=&file, patternvar=str, replacevar=rep)
|
||||||
|
data _null_;
|
||||||
|
infile "&file";
|
||||||
|
input;
|
||||||
|
list;
|
||||||
|
run;
|
||||||
|
|
||||||
|
@param file= (0) The file to perform the substitution on
|
||||||
|
@param patternvar= A macro variable containing the Lua
|
||||||
|
[pattern](https://www.lua.org/pil/20.2.html) to search for. Due to the use
|
||||||
|
of special (magic) characters in Lua patterns, it is safer to pass the NAME
|
||||||
|
of the macro variable containing the string, rather than the value itself.
|
||||||
|
@param replacevar= The name of the macro variable containing the replacement
|
||||||
|
_string_.
|
||||||
|
@param outfile= (0) The file to write the output to. If zero, then the file
|
||||||
|
is overwritten in-place.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li ml_gsubfile.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_gsubfile.test.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_gsubfile(file=0,
|
||||||
|
patternvar=,
|
||||||
|
replacevar=,
|
||||||
|
outfile=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%ml_gsubfile()
|
||||||
|
|
||||||
|
%mend mp_gsubfile;
|
||||||
|
/**
|
||||||
@file mp_guesspk.sas
|
@file mp_guesspk.sas
|
||||||
@brief Guess the primary key of a table
|
@brief Guess the primary key of a table
|
||||||
@details Tries to guess the primary key of a table based on the following logic:
|
@details Tries to guess the primary key of a table based on the following logic:
|
||||||
@@ -5954,14 +6219,14 @@ select distinct lowcase(memname)
|
|||||||
We take the standard definition one step further by embedding the informat
|
We take the standard definition one step further by embedding the informat
|
||||||
in the table header row, like so:
|
in the table header row, like so:
|
||||||
|
|
||||||
|var1:$|var2:best.|var3:date9.|
|
|var1:$32|var2:best.|var3:date9.|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|some text|42|01JAN1960|
|
|some text|42|01JAN1960|
|
||||||
|blah|1|31DEC1999|
|
|blah|1|31DEC1999|
|
||||||
|
|
||||||
Which resolves to:
|
Which resolves to:
|
||||||
|
|
||||||
|var1:$|var2:best.|var3:date9.|
|
|var1:$32|var2:best.|var3:date9.|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|some text|42|01JAN1960|
|
|some text|42|01JAN1960|
|
||||||
|blah|1|31DEC1999|
|
|blah|1|31DEC1999|
|
||||||
@@ -7523,6 +7788,64 @@ alter table &libds modify &var char(&len);
|
|||||||
|
|
||||||
%mend mp_validatecol;
|
%mend mp_validatecol;
|
||||||
/**
|
/**
|
||||||
|
@file
|
||||||
|
@brief Fix the `_WEBIN` variables provided to SAS web services
|
||||||
|
@details When uploading files to SAS Stored Processes or Viya Jobs a number
|
||||||
|
of global macro variables are automatically created - however there are some
|
||||||
|
differences in behaviour both between SAS 9 and Viya, and also between a
|
||||||
|
single file upload and a multi-file upload.
|
||||||
|
|
||||||
|
This macro "straightens" up the global macro variables to make it easier /
|
||||||
|
simpler to write code that works in both environments and with a variable
|
||||||
|
number of file inputs.
|
||||||
|
|
||||||
|
After running this macro, the following global variables will *always* exist:
|
||||||
|
@li `_WEBIN_FILE_COUNT`
|
||||||
|
@li `_WEBIN_FILENAME1`
|
||||||
|
@li `_WEBIN_FILEREF1`
|
||||||
|
@li `_WEBIN_NAME1`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mp_webin()
|
||||||
|
|
||||||
|
This was created as a macro procedure (over a macro function) as it will also
|
||||||
|
use the filename statement in Viya environments (where `_webin_fileuri` is
|
||||||
|
provided).
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_webin();
|
||||||
|
|
||||||
|
/* prepare global variables */
|
||||||
|
%global _webin_file_count
|
||||||
|
_webin_filename _webin_filename1
|
||||||
|
_webin_fileref _webin_fileref1
|
||||||
|
_webin_fileuri _webin_fileuri1
|
||||||
|
_webin_name _webin_name1
|
||||||
|
;
|
||||||
|
|
||||||
|
/* create initial versions */
|
||||||
|
%let _webin_file_count=%eval(&_webin_file_count+0);
|
||||||
|
%let _webin_filename1=%sysfunc(coalescec(&_webin_filename1,&_webin_filename));
|
||||||
|
%let _webin_fileref1=%sysfunc(coalescec(&_webin_fileref1,&_webin_fileref));
|
||||||
|
%let _webin_fileuri1=%sysfunc(coalescec(&_webin_fileuri1,&_webin_fileuri));
|
||||||
|
%let _webin_name1=%sysfunc(coalescec(&_webin_name1,&_webin_name));
|
||||||
|
|
||||||
|
|
||||||
|
/* If Viya, create temporary fileref(s) */
|
||||||
|
%local i;
|
||||||
|
%if %mf_getplatform()=SASVIYA %then %do i=1 %to &_webin_file_count;
|
||||||
|
%let _webin_fileref&i=%mf_getuniquefileref();
|
||||||
|
filename &&_webin_fileref&i filesrvc "&&_webin_fileuri&i";
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
%mend mp_webin;/**
|
||||||
@file
|
@file
|
||||||
@brief Creates a zip file
|
@brief Creates a zip file
|
||||||
@details For DIRECTORY usage, will ignore subfolders. For DATASET usage,
|
@details For DIRECTORY usage, will ignore subfolders. For DATASET usage,
|
||||||
@@ -9852,6 +10175,7 @@ data _null_;
|
|||||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
||||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||||
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||||
|
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||||
@@ -13347,6 +13671,7 @@ run;
|
|||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
@@ -14818,6 +15143,7 @@ data _null_;
|
|||||||
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
||||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||||
|
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||||
@@ -15716,6 +16042,7 @@ options noquotelenmax;
|
|||||||
run;
|
run;
|
||||||
libname &libref2 JSON fileref=&fname2;
|
libname &libref2 JSON fileref=&fname2;
|
||||||
data &outds;
|
data &outds;
|
||||||
|
length id $36 name $128 uri $64 type $32 description $256;
|
||||||
set &libref2..items;
|
set &libref2..items;
|
||||||
run;
|
run;
|
||||||
filename &fname2 clear;
|
filename &fname2 clear;
|
||||||
@@ -18656,6 +18983,7 @@ filename &fref1 clear;
|
|||||||
put ",""SYSCC"" : ""&syscc"" ";
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
@@ -18670,6 +18998,50 @@ filename &fref1 clear;
|
|||||||
%end;
|
%end;
|
||||||
|
|
||||||
%mend mv_webout;
|
%mend mv_webout;
|
||||||
|
/**
|
||||||
|
@file ml_gsubfile.sas
|
||||||
|
@brief Compiles the gsubfile.lua lua file
|
||||||
|
@details Writes gsubfile.lua to the work directory
|
||||||
|
and then includes it.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%ml_gsubfile()
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ml_gsubfile();
|
||||||
|
data _null_;
|
||||||
|
file "%sysfunc(pathname(work))/ml_gsubfile.lua";
|
||||||
|
put 'local fpath, outpath, file, fcontent ';
|
||||||
|
put ' ';
|
||||||
|
put '-- configure in / out paths ';
|
||||||
|
put 'fpath = sas.symget("file") ';
|
||||||
|
put 'outpath = sas.symget("outfile") ';
|
||||||
|
put 'if ( outpath == 0 ) ';
|
||||||
|
put 'then ';
|
||||||
|
put ' outpath=fpath ';
|
||||||
|
put 'end ';
|
||||||
|
put ' ';
|
||||||
|
put '-- open file and perform the substitution ';
|
||||||
|
put 'file = io.open(fpath,"r") ';
|
||||||
|
put 'fcontent = file:read("*all") ';
|
||||||
|
put 'file:close() ';
|
||||||
|
put 'fcontent = string.gsub( ';
|
||||||
|
put ' fcontent, ';
|
||||||
|
put ' sas.symget(sas.symget("patternvar")), ';
|
||||||
|
put ' sas.symget(sas.symget("replacevar")) ';
|
||||||
|
put ') ';
|
||||||
|
put ' ';
|
||||||
|
put '-- write the file back out ';
|
||||||
|
put 'file = io.open(outpath, "w+") ';
|
||||||
|
put 'io.output(file) ';
|
||||||
|
put 'io.write(fcontent) ';
|
||||||
|
put 'io.close(file) ';
|
||||||
|
run;
|
||||||
|
|
||||||
|
%inc "%sysfunc(pathname(work))/ml_gsubfile.lua" /source2;
|
||||||
|
|
||||||
|
%mend ml_gsubfile;
|
||||||
/**
|
/**
|
||||||
@file ml_json.sas
|
@file ml_json.sas
|
||||||
@brief Compiles the json.lua lua file
|
@brief Compiles the json.lua lua file
|
||||||
@@ -19061,7 +19433,7 @@ data _null_;
|
|||||||
put '-- JSON.LUA ENDS HERE ';
|
put '-- JSON.LUA ENDS HERE ';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%inc "%sysfunc(pathname(work))/ml_json.lua";
|
%inc "%sysfunc(pathname(work))/ml_json.lua" /source2;
|
||||||
|
|
||||||
%mend ml_json;
|
%mend ml_json;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,37 +1,58 @@
|
|||||||
/**
|
/**
|
||||||
@file
|
@file
|
||||||
@brief Assigns and returns an unused fileref
|
@brief Assigns and returns an unused fileref
|
||||||
@details
|
@details Using the native approach for assigning filerefs fails as some
|
||||||
|
procedures (such as proc http) do not recognise the temporary names (starting
|
||||||
|
with a hash), returning a message such as:
|
||||||
|
|
||||||
|
> ERROR 22-322: Expecting a name.
|
||||||
|
|
||||||
|
This macro works by attempting a random fileref (with a prefix), seeing if it
|
||||||
|
is already assigned, and if not - returning the fileref.
|
||||||
|
|
||||||
|
If your process can accept filerefs with the hash (#) prefix, then set
|
||||||
|
`prefix=0` to revert to the native approach - which is significantly faster
|
||||||
|
when there are a lot of filerefs in a session.
|
||||||
|
|
||||||
Use as follows:
|
Use as follows:
|
||||||
|
|
||||||
%let fileref1=%mf_getuniquefileref();
|
%let fileref1=%mf_getuniquefileref();
|
||||||
%let fileref2=%mf_getuniquefileref();
|
%let fileref2=%mf_getuniquefileref(prefix=0);
|
||||||
%put &fileref1 &fileref2;
|
%put &fileref1 &fileref2;
|
||||||
|
|
||||||
which returns:
|
which returns filerefs similar to:
|
||||||
|
|
||||||
> mcref0 mcref1
|
> _7432233 #LN00070
|
||||||
|
|
||||||
@param prefix= first part of fileref. Remember that filerefs can only be 8
|
@param [in] prefix= (_) first part of fileref. Remember that filerefs can only
|
||||||
characters, so a 7 letter prefix would mean that `maxtries` should be 10.
|
be 8 characters, so a 7 letter prefix would mean `maxtries` should be 10.
|
||||||
@param maxtries= the last part of the libref. Provide an integer value.
|
if using zero (0) as the prefix, a native assignment is used.
|
||||||
|
@param [in] maxtries= (1000) the last part of the libref. Must be an integer.
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
**/
|
**/
|
||||||
|
|
||||||
%macro mf_getuniquefileref(prefix=mcref,maxtries=1000);
|
%macro mf_getuniquefileref(prefix=_,maxtries=1000);
|
||||||
%local x fname;
|
%local rc fname;
|
||||||
%let x=0;
|
%if &prefix=0 %then %do;
|
||||||
%do x=0 %to &maxtries;
|
|
||||||
%if %sysfunc(fileref(&prefix&x)) > 0 %then %do;
|
|
||||||
%let fname=&prefix&x;
|
|
||||||
%let rc=%sysfunc(filename(fname,,temp));
|
%let rc=%sysfunc(filename(fname,,temp));
|
||||||
%if &rc %then %put %sysfunc(sysmsg());
|
%if &rc %then %put %sysfunc(sysmsg());
|
||||||
&prefix&x
|
&fname
|
||||||
%*put &sysmacroname: Fileref &prefix&x was assigned and returned;
|
%end;
|
||||||
|
%else %do;
|
||||||
|
%local x len;
|
||||||
|
%let len=%eval(8-%length(&prefix));
|
||||||
|
%let x=0;
|
||||||
|
%do x=0 %to &maxtries;
|
||||||
|
%let fname=&prefix%substr(%sysfunc(ranuni(0)),3,&len);
|
||||||
|
%if %sysfunc(fileref(&fname)) > 0 %then %do;
|
||||||
|
%let rc=%sysfunc(filename(fname,,temp));
|
||||||
|
%if &rc %then %put %sysfunc(sysmsg());
|
||||||
|
&fname
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%put unable to find available fileref in range &prefix.0-&maxtries;
|
%put unable to find available fileref after &maxtries attempts;
|
||||||
|
%end;
|
||||||
%mend mf_getuniquefileref;
|
%mend mf_getuniquefileref;
|
||||||
@@ -51,7 +51,8 @@
|
|||||||
%end;
|
%end;
|
||||||
%end;
|
%end;
|
||||||
%else %do;
|
%else %do;
|
||||||
%put dataset &libds not opened! (rc=&dsid);
|
%put &sysmacroname: dataset &libds not opened! (rc=&dsid);
|
||||||
|
%put &sysmacroname: %sysfunc(sysmsg());
|
||||||
%return;
|
%return;
|
||||||
%end;
|
%end;
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,11 @@
|
|||||||
%let vlen = %str( );
|
%let vlen = %str( );
|
||||||
%end;
|
%end;
|
||||||
%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 */
|
/* Close dataset */
|
||||||
%let rc = %sysfunc(close(&dsid));
|
%let rc = %sysfunc(close(&dsid));
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ returns:
|
|||||||
%let vnum = %str( );
|
%let vnum = %str( );
|
||||||
%end;
|
%end;
|
||||||
%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 */
|
/* Close dataset */
|
||||||
%let rc = %sysfunc(close(&dsid));
|
%let rc = %sysfunc(close(&dsid));
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ Usage:
|
|||||||
%let vtype = %str( );
|
%let vtype = %str( );
|
||||||
%end;
|
%end;
|
||||||
%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 */
|
/* Close dataset */
|
||||||
%let rc = %sysfunc(close(&dsid));
|
%let rc = %sysfunc(close(&dsid));
|
||||||
|
|||||||
@@ -158,7 +158,7 @@
|
|||||||
/* send response in SASjs JSON format */
|
/* send response in SASjs JSON format */
|
||||||
data _null_;
|
data _null_;
|
||||||
file _webout mod lrecl=32000 encoding='utf-8';
|
file _webout mod lrecl=32000 encoding='utf-8';
|
||||||
length msg $32767 debug $8;
|
length msg $32767 ;
|
||||||
sasdatetime=datetime();
|
sasdatetime=datetime();
|
||||||
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
msg=cats(symget('msg'),'\n\nLog Extract:\n',symget('logmsg'));
|
||||||
/* escape the quotes */
|
/* escape the quotes */
|
||||||
@@ -169,7 +169,7 @@
|
|||||||
msg=cats('"',msg,'"');
|
msg=cats('"',msg,'"');
|
||||||
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
if symexist('_debug') then debug=quote(trim(symget('_debug')));
|
||||||
else debug='""';
|
else debug='""';
|
||||||
if debug ge '"131"' then put '>>weboutBEGIN<<';
|
put '>>weboutBEGIN<<';
|
||||||
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
put '{"START_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '"';
|
||||||
put ',"sasjsAbort" : [{';
|
put ',"sasjsAbort" : [{';
|
||||||
put ' "MSG":' msg ;
|
put ' "MSG":' msg ;
|
||||||
@@ -189,23 +189,26 @@
|
|||||||
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
|
||||||
put ',"_PROGRAM" : ' _PROGRAM ;
|
put ',"_PROGRAM" : ' _PROGRAM ;
|
||||||
put ",""SYSCC"" : ""&syscc"" ";
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
syserrortext=quote(trim(symget('syserrortext')));
|
||||||
|
put ",""SYSERRORTEXT"" : " syserrortext;
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
put ",""SYSWARNINGTEXT"" : ""&syswarningtext"" ";
|
syswarningtext=quote(trim(symget('syswarningtext')));
|
||||||
|
put ",""SYSWARNINGTEXT"" : " syswarningtext;
|
||||||
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
put ',"END_DTTM" : "' "%sysfunc(datetime(),datetime20.3)" '" ';
|
||||||
put "}" @;
|
put "}" @;
|
||||||
if debug ge '"131"' then put '>>weboutEND<<';
|
put '>>weboutEND<<';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%put _all_;
|
%put _all_;
|
||||||
|
|
||||||
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
||||||
data _null_;
|
data _null_;
|
||||||
putlog 'stpsrvset program error and syscc';
|
putlog 'stpsrvset program err and syscc';
|
||||||
rc=stpsrvset('program error', 0);
|
rc=stpsrvset('program error', 0);
|
||||||
call symputx("syscc",0,"g");
|
call symputx("syscc",0,"g");
|
||||||
run;
|
run;
|
||||||
|
|||||||
57
base/mp_appendfile.sas
Normal file
57
base/mp_appendfile.sas
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Append (concatenate) two or more files.
|
||||||
|
@details Will append one more more `appendrefs` (filerefs) to a `baseref`.
|
||||||
|
Uses a binary mechanism, so will work with any file type. For that reason -
|
||||||
|
use with care! And supply your own trailing carriage returns in each file..
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
filename tmp1 temp;
|
||||||
|
filename tmp2 temp;
|
||||||
|
filename tmp3 temp;
|
||||||
|
data _null_; file tmp1; put 'base file';
|
||||||
|
data _null_; file tmp2; put 'append1';
|
||||||
|
data _null_; file tmp3; put 'append2';
|
||||||
|
run;
|
||||||
|
%mp_appendfile(baseref=tmp1, appendrefs=tmp2 tmp3)
|
||||||
|
|
||||||
|
|
||||||
|
@param [in] baseref= Fileref of the base file (should exist)
|
||||||
|
@param [in] appendrefs= One or more filerefs to be appended to the base
|
||||||
|
fileref. Space separated.
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe, source: https://github.com/sasjs/core
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_abort.sas
|
||||||
|
@li mp_binarycopy.sas
|
||||||
|
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_appendfile(
|
||||||
|
baseref=0,
|
||||||
|
appendrefs=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%mp_abort(iftrue= (&baseref=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Baseref NOT specified!)
|
||||||
|
)
|
||||||
|
%mp_abort(iftrue= (&appendrefs=0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(Appendrefs NOT specified!)
|
||||||
|
)
|
||||||
|
|
||||||
|
%local i;
|
||||||
|
%do i=1 %to %sysfunc(countw(&appendrefs));
|
||||||
|
%mp_abort(iftrue= (&syscc>0)
|
||||||
|
,mac=&sysmacroname
|
||||||
|
,msg=%str(syscc=&syscc)
|
||||||
|
)
|
||||||
|
%mp_binarycopy(inref=%scan(&appendrefs,&i), outref=&baseref, mode=APPEND)
|
||||||
|
%end;
|
||||||
|
|
||||||
|
%mend mp_appendfile;
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
<h4> SAS Macros </h4>
|
<h4> SAS Macros </h4>
|
||||||
@li mf_existds.sas
|
@li mf_existds.sas
|
||||||
|
@li mf_getuniquename.sas
|
||||||
@li mf_nobs.sas
|
@li mf_nobs.sas
|
||||||
@li mp_abort.sas
|
@li mp_abort.sas
|
||||||
|
|
||||||
@@ -115,6 +116,26 @@
|
|||||||
select count(*) into: orig from &lib..&ds;
|
select count(*) into: orig from &lib..&ds;
|
||||||
quit;
|
quit;
|
||||||
|
|
||||||
|
%local notfound tmp1 tmp2;
|
||||||
|
%let tmp1=%mf_getuniquename();
|
||||||
|
%let tmp2=%mf_getuniquename();
|
||||||
|
|
||||||
|
/* this is a bit convoluted - but using sql outobs=10 throws warnings */
|
||||||
|
proc sql noprint;
|
||||||
|
create view &tmp1 as
|
||||||
|
select distinct &col
|
||||||
|
from &lib..&ds
|
||||||
|
where &col not in (
|
||||||
|
select &ccol from &clib..&cds
|
||||||
|
);
|
||||||
|
data &tmp2;
|
||||||
|
set &tmp1;
|
||||||
|
if _n_>10 then stop;
|
||||||
|
run;
|
||||||
|
proc sql;
|
||||||
|
select distinct &col into: notfound separated by ' ' from &tmp2;
|
||||||
|
|
||||||
|
|
||||||
%mp_abort(iftrue= (&syscc ne 0)
|
%mp_abort(iftrue= (&syscc ne 0)
|
||||||
,mac=&sysmacroname
|
,mac=&sysmacroname
|
||||||
,msg=%str(syscc=&syscc after macro query)
|
,msg=%str(syscc=&syscc after macro query)
|
||||||
@@ -125,7 +146,7 @@
|
|||||||
test_description=symget('desc');
|
test_description=symget('desc');
|
||||||
test_result='FAIL';
|
test_result='FAIL';
|
||||||
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
|
test_comments="&sysmacroname: &lib..&ds..&col has &result values "
|
||||||
!!"not in &clib..&cds..&ccol ";
|
!!"not in &clib..&cds..&ccol.. First 10 vals:"!!symget('notfound');
|
||||||
%if &test=ANYVAL %then %do;
|
%if &test=ANYVAL %then %do;
|
||||||
if &result < &orig then test_result='PASS';
|
if &result < &orig then test_result='PASS';
|
||||||
%end;
|
%end;
|
||||||
|
|||||||
@@ -2,12 +2,21 @@
|
|||||||
@file
|
@file
|
||||||
@brief Create a CARDS file from a SAS dataset.
|
@brief Create a CARDS file from a SAS dataset.
|
||||||
@details Uses dataset attributes to convert all data into datalines.
|
@details Uses dataset attributes to convert all data into datalines.
|
||||||
Running the generated file will rebuild the original dataset.
|
Running the generated file will rebuild the original dataset. Includes
|
||||||
|
support for large decimals, binary data, PROCESSED_DTTM columns, and
|
||||||
|
alternative encoding. If the input dataset is empty, the cards file will
|
||||||
|
still be created.
|
||||||
|
|
||||||
|
Additional support to generate a random sample and max rows.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
%mp_ds2cards(base_ds=sashelp.class
|
%mp_ds2cards(base_ds=sashelp.class
|
||||||
|
, tgt_ds=work.class
|
||||||
, cards_file= "C:\temp\class.sas"
|
, cards_file= "C:\temp\class.sas"
|
||||||
, maxobs=5)
|
, showlog=NO
|
||||||
|
, maxobs=5
|
||||||
|
)
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
- labelling the dataset
|
- labelling the dataset
|
||||||
@@ -18,15 +27,24 @@
|
|||||||
that is converted to a cards file.
|
that is converted to a cards file.
|
||||||
@param [in] tgt_ds= Table that the generated cards file would create.
|
@param [in] tgt_ds= Table that the generated cards file would create.
|
||||||
Optional - if omitted, will be same as BASE_DS.
|
Optional - if omitted, will be same as BASE_DS.
|
||||||
@param [out] cards_file= Location in which to write the (.sas) cards file
|
@param [out] cards_file= ("%sysfunc(pathname(work))/cardgen.sas") Location in
|
||||||
@param [in] maxobs= to limit output to the first <code>maxobs</code>
|
which to write the (.sas) cards file
|
||||||
observations
|
@param [in] maxobs= (max) To limit output to the first <code>maxobs</code>
|
||||||
@param [in] showlog= whether to show generated cards file in the SAS log
|
observations, enter an integer here.
|
||||||
(YES/NO)
|
@param [in] random_sample= (NO) Set to YES to generate a random sample of
|
||||||
@param [in] outencoding= provide encoding value for file statement (eg utf-8)
|
data. Can be quite slow.
|
||||||
@param [in] append= If NO then will rebuild the cards file if it already
|
@param [in] showlog= (YES) Whether to show generated cards file in the SAS
|
||||||
|
log. Valid values:
|
||||||
|
@li YES
|
||||||
|
@li NO
|
||||||
|
@param [in] outencoding= Provide encoding value for file statement (eg utf-8)
|
||||||
|
@param [in] append= (NO) If NO then will rebuild the cards file if it already
|
||||||
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
exists, otherwise will append to it. Used by the mp_lib2cards.sas macro.
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_lib2cards.sas
|
||||||
|
@li mp_ds2inserts.sas
|
||||||
|
@li mp_mdtablewrite.sas
|
||||||
|
|
||||||
@version 9.2
|
@version 9.2
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -51,15 +69,15 @@
|
|||||||
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
%if (&tgt_ds = ) %then %let tgt_ds=&base_ds;
|
||||||
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
%if %index(&tgt_ds,.)=0 %then %let tgt_ds=WORK.%scan(&base_ds,2,.);
|
||||||
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
%if ("&outencoding" ne "") %then %let outencoding=encoding="&outencoding";
|
||||||
%if ("&append" = "") %then %let append=;
|
%if ("&append" = "" or "&append" = "NO") %then %let append=;
|
||||||
%else %let append=mod;
|
%else %let append=mod;
|
||||||
|
|
||||||
/* get varcount */
|
/* get varcount */
|
||||||
%let nvars=0;
|
%let nvars=0;
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select count(*) into: nvars from dictionary.columns
|
select count(*) into: nvars from dictionary.columns
|
||||||
where libname="%scan(%upcase(&base_ds),1)"
|
where upcase(libname)="%scan(%upcase(&base_ds),1)"
|
||||||
and memname="%scan(%upcase(&base_ds),2)";
|
and upcase(memname)="%scan(%upcase(&base_ds),2)";
|
||||||
%if &nvars=0 %then %do;
|
%if &nvars=0 %then %do;
|
||||||
%put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
|
%put %str(WARN)ING: Dataset &base_ds has no variables, will not be converted.;
|
||||||
%return;
|
%return;
|
||||||
@@ -115,8 +133,8 @@ proc sql
|
|||||||
reset outobs=max;
|
reset outobs=max;
|
||||||
create table datalines1 as
|
create table datalines1 as
|
||||||
select name,type,length,varnum,format,label from dictionary.columns
|
select name,type,length,varnum,format,label from dictionary.columns
|
||||||
where libname="%upcase(%scan(&base_ds,1))"
|
where upcase(libname)="%upcase(%scan(&base_ds,1))"
|
||||||
and memname="%upcase(%scan(&base_ds,2))";
|
and upcase(memname)="%upcase(%scan(&base_ds,2))";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Due to long decimals cannot use best. format
|
Due to long decimals cannot use best. format
|
||||||
@@ -137,7 +155,18 @@ data datalines_2;
|
|||||||
,put(',name,',best32.-l)
|
,put(',name,',best32.-l)
|
||||||
,substrn(put(',name,',bestd32.-l),1
|
,substrn(put(',name,',bestd32.-l),1
|
||||||
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
,findc(put(',name,',bestd32.-l),"0","TBK")))');
|
||||||
else dataline=name;
|
/**
|
||||||
|
* binary data must be converted, to store in text format. It is identified
|
||||||
|
* by the presence of the $HEX keyword in the format.
|
||||||
|
*/
|
||||||
|
else if upcase(format)=:'$HEX' then
|
||||||
|
dataline=cats('put(trim(',name,'),',format,')');
|
||||||
|
/**
|
||||||
|
* 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;
|
run;
|
||||||
|
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
@@ -162,7 +191,8 @@ data _null_;
|
|||||||
|
|
||||||
|
|
||||||
/* Build input statement */
|
/* Build input statement */
|
||||||
if type='char' then type3=':$char.';
|
if upcase(format)=:'$HEX' then type3=':'!!format;
|
||||||
|
else if type='char' then type3=':$char.';
|
||||||
str2=put(name,$33.)||type3;
|
str2=put(name,$33.)||type3;
|
||||||
|
|
||||||
|
|
||||||
@@ -184,11 +214,12 @@ data _null_;
|
|||||||
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
file &cards_file. &outencoding lrecl=32767 termstr=nl &append;
|
||||||
length __attrib $32767;
|
length __attrib $32767;
|
||||||
if _n_=1 then do;
|
if _n_=1 then do;
|
||||||
put '/*******************************************************************';
|
put '/**';
|
||||||
put " Datalines for %upcase(%scan(&base_ds,2)) dataset ";
|
put ' @file';
|
||||||
put " Generated by %nrstr(%%)mp_ds2cards()";
|
put " @brief Datalines for %upcase(%scan(&base_ds,2)) dataset";
|
||||||
|
put " @details Generated by %nrstr(%%)mp_ds2cards()";
|
||||||
put " Available on github.com/sasjs/core";
|
put " Available on github.com/sasjs/core";
|
||||||
put '********************************************************************/';
|
put '**/';
|
||||||
put "data &tgt_ds &indexes;";
|
put "data &tgt_ds &indexes;";
|
||||||
put "attrib ";
|
put "attrib ";
|
||||||
%do i = 1 %to &nvars;
|
%do i = 1 %to &nvars;
|
||||||
@@ -212,7 +243,7 @@ data _null_;
|
|||||||
put 'run;';
|
put 'run;';
|
||||||
end;
|
end;
|
||||||
else do;
|
else do;
|
||||||
put "infile cards dsd delimiter=',';";
|
put "infile cards dsd;";
|
||||||
put "input ";
|
put "input ";
|
||||||
%do i = 1 %to &nvars.;
|
%do i = 1 %to &nvars.;
|
||||||
%if(%length(&&input_stmt_&i..)) %then
|
%if(%length(&&input_stmt_&i..)) %then
|
||||||
|
|||||||
65
base/mp_getcols.sas
Normal file
65
base/mp_getcols.sas
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Creates a dataset with column metadata.
|
||||||
|
@details This macro takes the `proc contents` output and "tidies it up" in the
|
||||||
|
following ways:
|
||||||
|
|
||||||
|
@li Blank labels are filled in with column names
|
||||||
|
@li Formats are reconstructed with default values
|
||||||
|
@li Types such as DATE / TIME / DATETIME are inferred from the formats
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
%mp_getcols(sashelp.airline,outds=work.myds)
|
||||||
|
|
||||||
|
@param ds The dataset from which to obtain column metadata
|
||||||
|
@param outds= (work.cols) The output dataset to create. Sample data:
|
||||||
|
|NAME $|LENGTH 8|VARNUM 8|LABEL $|FORMAT $49|TYPE $1 |DDTYPE $|
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
|AIR|8|2|international airline travel (thousands)|8.|N|NUMERIC|
|
||||||
|
|DATE|8|1|DATE|MONYY.|N|DATE|
|
||||||
|
|REGION|3|3|REGION|$3.|C|CHARACTER|
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mf_getvarlist.sas
|
||||||
|
@li mm_getcols.sas
|
||||||
|
|
||||||
|
@version 9.2
|
||||||
|
@author Allan Bowe
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_getcols(ds, outds=work.cols);
|
||||||
|
|
||||||
|
proc contents noprint data=&ds
|
||||||
|
out=_data_ (keep=name type length label varnum format:);
|
||||||
|
run;
|
||||||
|
data &outds(keep=name type length varnum format label ddtype);
|
||||||
|
set &syslast(rename=(format=format2 type=type2));
|
||||||
|
name=upcase(name);
|
||||||
|
if type2=2 then do;
|
||||||
|
length format $49.;
|
||||||
|
if format2='' then format=cats('$',length,'.');
|
||||||
|
else if formatl=0 then format=cats(format2,'.');
|
||||||
|
else format=cats(format2,formatl,'.');
|
||||||
|
type='C';
|
||||||
|
ddtype='CHARACTER';
|
||||||
|
end;
|
||||||
|
else do;
|
||||||
|
if format2='' then format=cats(length,'.');
|
||||||
|
else if formatl=0 then format=cats(format2,'.');
|
||||||
|
else if formatd=0 then format=cats(format2,formatl,'.');
|
||||||
|
else format=cats(format2,formatl,'.',formatd);
|
||||||
|
type='N';
|
||||||
|
if format=:'DATETIME' then ddtype='DATETIME';
|
||||||
|
else if format=:'DATE' or format=:'DDMMYY' or format=:'MMDDYY'
|
||||||
|
or format=:'YYMMDD' or format=:'E8601DA' or format=:'B8601DA'
|
||||||
|
or format=:'MONYY'
|
||||||
|
then ddtype='DATE';
|
||||||
|
else if format=:'TIME' then ddtype='TIME';
|
||||||
|
else ddtype='NUMERIC';
|
||||||
|
end;
|
||||||
|
if label='' then label=name;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mend mp_getcols;
|
||||||
@@ -39,21 +39,24 @@
|
|||||||
/* must use SQL as proc datasets does not support length changes */
|
/* must use SQL as proc datasets does not support length changes */
|
||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
create table &outds as
|
create table &outds as
|
||||||
select a.TABLE_CATALOG as libref
|
select upcase(a.TABLE_CATALOG) as libref
|
||||||
,a.TABLE_NAME
|
,upcase(a.TABLE_NAME) as TABLE_NAME
|
||||||
,a.constraint_type
|
,a.constraint_type
|
||||||
,a.constraint_name
|
,a.constraint_name
|
||||||
,b.column_name
|
,b.column_name
|
||||||
from dictionary.TABLE_CONSTRAINTS a
|
from dictionary.TABLE_CONSTRAINTS a
|
||||||
left join dictionary.constraint_column_usage b
|
left join dictionary.constraint_column_usage b
|
||||||
on a.TABLE_CATALOG=b.TABLE_CATALOG
|
on upcase(a.TABLE_CATALOG)=upcase(b.TABLE_CATALOG)
|
||||||
and a.TABLE_NAME=b.TABLE_NAME
|
and upcase(a.TABLE_NAME)=upcase(b.TABLE_NAME)
|
||||||
and a.constraint_name=b.constraint_name
|
and a.constraint_name=b.constraint_name
|
||||||
where a.TABLE_CATALOG="&lib"
|
/**
|
||||||
and 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;
|
%if "&ds" ne "" %then %do;
|
||||||
and a.TABLE_NAME="&ds"
|
and upcase(a.TABLE_NAME)="&ds"
|
||||||
and b.TABLE_NAME="&ds"
|
and upcase(b.TABLE_NAME)="&ds"
|
||||||
%end;
|
%end;
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ run;
|
|||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select sysvalue into: schemaactual
|
select sysvalue into: schemaactual
|
||||||
from dictionary.libnames
|
from dictionary.libnames
|
||||||
where libname="&libref" and engine='SQLSVR';
|
where upcase(libname)="&libref" and engine='SQLSVR';
|
||||||
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
||||||
|
|
||||||
%do x=1 %to %sysfunc(countw(&dsnlist));
|
%do x=1 %to %sysfunc(countw(&dsnlist));
|
||||||
@@ -304,7 +304,7 @@ run;
|
|||||||
proc sql noprint;
|
proc sql noprint;
|
||||||
select sysvalue into: schemaactual
|
select sysvalue into: schemaactual
|
||||||
from dictionary.libnames
|
from dictionary.libnames
|
||||||
where libname="&libref" and engine='POSTGRES';
|
where upcase(libname)="&libref" and engine='POSTGRES';
|
||||||
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
%let schema=%sysfunc(coalescec(&schemaactual,&schema,&libref));
|
||||||
data _null_;
|
data _null_;
|
||||||
file &fref mod;
|
file &fref mod;
|
||||||
|
|||||||
53
base/mp_gsubfile.sas
Normal file
53
base/mp_gsubfile.sas
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Performs a text substitution on a file
|
||||||
|
@details Makes use of the GSUB function in LUA to perform a text substitution
|
||||||
|
in a file - either in-place, or writing to a new location. The benefit of
|
||||||
|
using LUA is that the entire file can be loaded into a single variable,
|
||||||
|
thereby side stepping the 32767 character limit in a data step.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%let file=%sysfunc(pathname(work))/file.txt;
|
||||||
|
%let str=replace/me;
|
||||||
|
%let rep=with/this;
|
||||||
|
data _null_;
|
||||||
|
file "&file";
|
||||||
|
put "&str";
|
||||||
|
run;
|
||||||
|
%mp_gsubfile(file=&file, patternvar=str, replacevar=rep)
|
||||||
|
data _null_;
|
||||||
|
infile "&file";
|
||||||
|
input;
|
||||||
|
list;
|
||||||
|
run;
|
||||||
|
|
||||||
|
@param file= (0) The file to perform the substitution on
|
||||||
|
@param patternvar= A macro variable containing the Lua
|
||||||
|
[pattern](https://www.lua.org/pil/20.2.html) to search for. Due to the use
|
||||||
|
of special (magic) characters in Lua patterns, it is safer to pass the NAME
|
||||||
|
of the macro variable containing the string, rather than the value itself.
|
||||||
|
@param replacevar= The name of the macro variable containing the replacement
|
||||||
|
_string_.
|
||||||
|
@param outfile= (0) The file to write the output to. If zero, then the file
|
||||||
|
is overwritten in-place.
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li ml_gsubfile.sas
|
||||||
|
|
||||||
|
<h4> Related Macros </h4>
|
||||||
|
@li mp_gsubfile.test.sas
|
||||||
|
|
||||||
|
@version 9.4
|
||||||
|
@author Allan Bowe
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_gsubfile(file=0,
|
||||||
|
patternvar=,
|
||||||
|
replacevar=,
|
||||||
|
outfile=0
|
||||||
|
)/*/STORE SOURCE*/;
|
||||||
|
|
||||||
|
%ml_gsubfile()
|
||||||
|
|
||||||
|
%mend mp_gsubfile;
|
||||||
@@ -14,14 +14,14 @@
|
|||||||
We take the standard definition one step further by embedding the informat
|
We take the standard definition one step further by embedding the informat
|
||||||
in the table header row, like so:
|
in the table header row, like so:
|
||||||
|
|
||||||
|var1:$|var2:best.|var3:date9.|
|
|var1:$32|var2:best.|var3:date9.|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|some text|42|01JAN1960|
|
|some text|42|01JAN1960|
|
||||||
|blah|1|31DEC1999|
|
|blah|1|31DEC1999|
|
||||||
|
|
||||||
Which resolves to:
|
Which resolves to:
|
||||||
|
|
||||||
|var1:$|var2:best.|var3:date9.|
|
|var1:$32|var2:best.|var3:date9.|
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|some text|42|01JAN1960|
|
|some text|42|01JAN1960|
|
||||||
|blah|1|31DEC1999|
|
|blah|1|31DEC1999|
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
@li mf_mkdir.sas
|
@li mf_mkdir.sas
|
||||||
@li mf_getuniquefileref.sas
|
@li mf_getuniquefileref.sas
|
||||||
|
|
||||||
@param ziploc= fileref or quoted full path to zip file ("/path/to/file.zip")
|
@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 outdir= (%sysfunc(pathname(work))) Directory in which to write the
|
||||||
|
outputs (created if non existant)
|
||||||
|
|
||||||
@version 9.4
|
@version 9.4
|
||||||
@author Allan Bowe
|
@author Allan Bowe
|
||||||
@@ -35,7 +36,8 @@
|
|||||||
%let fname2=%mf_getuniquefileref();
|
%let fname2=%mf_getuniquefileref();
|
||||||
%let fname3=%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 */
|
/* Read the "members" (files) from the ZIP file */
|
||||||
data _data_(keep=memname isFolder);
|
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;
|
||||||
59
base/mp_webin.sas
Normal file
59
base/mp_webin.sas
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Fix the `_WEBIN` variables provided to SAS web services
|
||||||
|
@details When uploading files to SAS Stored Processes or Viya Jobs a number
|
||||||
|
of global macro variables are automatically created - however there are some
|
||||||
|
differences in behaviour both between SAS 9 and Viya, and also between a
|
||||||
|
single file upload and a multi-file upload.
|
||||||
|
|
||||||
|
This macro "straightens" up the global macro variables to make it easier /
|
||||||
|
simpler to write code that works in both environments and with a variable
|
||||||
|
number of file inputs.
|
||||||
|
|
||||||
|
After running this macro, the following global variables will *always* exist:
|
||||||
|
@li `_WEBIN_FILE_COUNT`
|
||||||
|
@li `_WEBIN_FILENAME1`
|
||||||
|
@li `_WEBIN_FILEREF1`
|
||||||
|
@li `_WEBIN_NAME1`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%mp_webin()
|
||||||
|
|
||||||
|
This was created as a macro procedure (over a macro function) as it will also
|
||||||
|
use the filename statement in Viya environments (where `_webin_fileuri` is
|
||||||
|
provided).
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getplatform.sas
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro mp_webin();
|
||||||
|
|
||||||
|
/* prepare global variables */
|
||||||
|
%global _webin_file_count
|
||||||
|
_webin_filename _webin_filename1
|
||||||
|
_webin_fileref _webin_fileref1
|
||||||
|
_webin_fileuri _webin_fileuri1
|
||||||
|
_webin_name _webin_name1
|
||||||
|
;
|
||||||
|
|
||||||
|
/* create initial versions */
|
||||||
|
%let _webin_file_count=%eval(&_webin_file_count+0);
|
||||||
|
%let _webin_filename1=%sysfunc(coalescec(&_webin_filename1,&_webin_filename));
|
||||||
|
%let _webin_fileref1=%sysfunc(coalescec(&_webin_fileref1,&_webin_fileref));
|
||||||
|
%let _webin_fileuri1=%sysfunc(coalescec(&_webin_fileuri1,&_webin_fileuri));
|
||||||
|
%let _webin_name1=%sysfunc(coalescec(&_webin_name1,&_webin_name));
|
||||||
|
|
||||||
|
|
||||||
|
/* If Viya, create temporary fileref(s) */
|
||||||
|
%local i;
|
||||||
|
%if %mf_getplatform()=SASVIYA %then %do i=1 %to &_webin_file_count;
|
||||||
|
%let _webin_fileref&i=%mf_getuniquefileref();
|
||||||
|
filename &&_webin_fileref&i filesrvc "&&_webin_fileuri&i";
|
||||||
|
%end;
|
||||||
|
|
||||||
|
|
||||||
|
%mend mp_webin;
|
||||||
2
build.py
2
build.py
@@ -22,7 +22,7 @@ for file in files:
|
|||||||
for line in infile:
|
for line in infile:
|
||||||
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
ml.write(" put '" + line.rstrip().replace("'","''") + " ';\n")
|
||||||
ml.write("run;\n\n")
|
ml.write("run;\n\n")
|
||||||
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\";\n\n")
|
ml.write("%inc \"%sysfunc(pathname(work))/" + name + ".lua\" /source2;\n\n")
|
||||||
ml.write("%mend " + name + ";\n")
|
ml.write("%mend " + name + ";\n")
|
||||||
|
|
||||||
ml.close()
|
ml.close()
|
||||||
|
|||||||
25
lua/gsubfile.lua
Normal file
25
lua/gsubfile.lua
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
local fpath, outpath, file, fcontent
|
||||||
|
|
||||||
|
-- configure in / out paths
|
||||||
|
fpath = sas.symget("file")
|
||||||
|
outpath = sas.symget("outfile")
|
||||||
|
if ( outpath == 0 )
|
||||||
|
then
|
||||||
|
outpath=fpath
|
||||||
|
end
|
||||||
|
|
||||||
|
-- open file and perform the substitution
|
||||||
|
file = io.open(fpath,"r")
|
||||||
|
fcontent = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
fcontent = string.gsub(
|
||||||
|
fcontent,
|
||||||
|
sas.symget(sas.symget("patternvar")),
|
||||||
|
sas.symget(sas.symget("replacevar"))
|
||||||
|
)
|
||||||
|
|
||||||
|
-- write the file back out
|
||||||
|
file = io.open(outpath, "w+")
|
||||||
|
io.output(file)
|
||||||
|
io.write(fcontent)
|
||||||
|
io.close(file)
|
||||||
44
lua/ml_gsubfile.sas
Normal file
44
lua/ml_gsubfile.sas
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
@file ml_gsubfile.sas
|
||||||
|
@brief Compiles the gsubfile.lua lua file
|
||||||
|
@details Writes gsubfile.lua to the work directory
|
||||||
|
and then includes it.
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
%ml_gsubfile()
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%macro ml_gsubfile();
|
||||||
|
data _null_;
|
||||||
|
file "%sysfunc(pathname(work))/ml_gsubfile.lua";
|
||||||
|
put 'local fpath, outpath, file, fcontent ';
|
||||||
|
put ' ';
|
||||||
|
put '-- configure in / out paths ';
|
||||||
|
put 'fpath = sas.symget("file") ';
|
||||||
|
put 'outpath = sas.symget("outfile") ';
|
||||||
|
put 'if ( outpath == 0 ) ';
|
||||||
|
put 'then ';
|
||||||
|
put ' outpath=fpath ';
|
||||||
|
put 'end ';
|
||||||
|
put ' ';
|
||||||
|
put '-- open file and perform the substitution ';
|
||||||
|
put 'file = io.open(fpath,"r") ';
|
||||||
|
put 'fcontent = file:read("*all") ';
|
||||||
|
put 'file:close() ';
|
||||||
|
put 'fcontent = string.gsub( ';
|
||||||
|
put ' fcontent, ';
|
||||||
|
put ' sas.symget(sas.symget("patternvar")), ';
|
||||||
|
put ' sas.symget(sas.symget("replacevar")) ';
|
||||||
|
put ') ';
|
||||||
|
put ' ';
|
||||||
|
put '-- write the file back out ';
|
||||||
|
put 'file = io.open(outpath, "w+") ';
|
||||||
|
put 'io.output(file) ';
|
||||||
|
put 'io.write(fcontent) ';
|
||||||
|
put 'io.close(file) ';
|
||||||
|
run;
|
||||||
|
|
||||||
|
%inc "%sysfunc(pathname(work))/ml_gsubfile.lua" /source2;
|
||||||
|
|
||||||
|
%mend ml_gsubfile;
|
||||||
@@ -389,6 +389,6 @@ data _null_;
|
|||||||
put '-- JSON.LUA ENDS HERE ';
|
put '-- JSON.LUA ENDS HERE ';
|
||||||
run;
|
run;
|
||||||
|
|
||||||
%inc "%sysfunc(pathname(work))/ml_json.lua";
|
%inc "%sysfunc(pathname(work))/ml_json.lua" /source2;
|
||||||
|
|
||||||
%mend ml_json;
|
%mend ml_json;
|
||||||
|
|||||||
@@ -385,6 +385,7 @@ data _null_;
|
|||||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
||||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||||
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
|
||||||
|
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||||
|
|||||||
@@ -155,6 +155,7 @@
|
|||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
put ",""SYSJOBID"" : ""&sysjobid"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
|
|||||||
606
package-lock.json
generated
606
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,6 @@
|
|||||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sasjs/cli": "^2.37.2"
|
"@sasjs/cli": "^2.39.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,10 @@
|
|||||||
"appLoc": "/Shared Data/temp/macrocore",
|
"appLoc": "/Shared Data/temp/macrocore",
|
||||||
"macroFolders": [
|
"macroFolders": [
|
||||||
"tests/sas9only"
|
"tests/sas9only"
|
||||||
]
|
],
|
||||||
|
"deployConfig": {
|
||||||
|
"deployServicePack": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "docsonly",
|
"name": "docsonly",
|
||||||
|
|||||||
36
tests/crossplatform/mf_getuniquefileref.test.sas
Normal file
36
tests/crossplatform/mf_getuniquefileref.test.sas
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mf_getuniquefileref macro
|
||||||
|
@details To test performance you can also use the following macro:
|
||||||
|
|
||||||
|
%macro x(prefix);
|
||||||
|
%let now=%sysfunc(datetime());
|
||||||
|
%do x=1 %to 1000;
|
||||||
|
%let rc=%mf_getuniquefileref(prefix=&prefix);
|
||||||
|
%end;
|
||||||
|
%put %sysevalf(%sysfunc(datetime())-&now);
|
||||||
|
%mend;
|
||||||
|
%x(_)
|
||||||
|
%x(0)
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mf_getuniquefileref.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%substr(%mf_getuniquefileref(prefix=0),1,1)"="#"
|
||||||
|
),
|
||||||
|
desc=Checking for a natively assigned fileref,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
"%substr(%mf_getuniquefileref(),1,1)"="_"
|
||||||
|
),
|
||||||
|
desc=Checking for a default fileref,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
41
tests/crossplatform/mp_appendfile.test.sas
Normal file
41
tests/crossplatform/mp_appendfile.test.sas
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_appendfile.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_appendfile.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
filename tmp1 temp;
|
||||||
|
filename tmp2 temp;
|
||||||
|
filename tmp3 temp;
|
||||||
|
data _null_; file tmp1; put 'base file';
|
||||||
|
data _null_; file tmp2; put 'append1';
|
||||||
|
data _null_; file tmp3; put 'append2';
|
||||||
|
run;
|
||||||
|
%mp_appendfile(baseref=tmp1, appendrefs=tmp2 tmp3)
|
||||||
|
data _null_;
|
||||||
|
infile tmp1;
|
||||||
|
input;
|
||||||
|
put _infile_;
|
||||||
|
call symputx(cats('check',_n_),_infile_);
|
||||||
|
run;
|
||||||
|
%global check1 check2 check3;
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&check1"="base file"),
|
||||||
|
desc=Line 1 of file tmp1 is correct,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&check2"="append1"),
|
||||||
|
desc=Line 2 of file tmp1 is correct,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&check3"="append2"),
|
||||||
|
desc=Line 3 of file tmp1 is correct,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
60
tests/crossplatform/mp_ds2cards.test.sas
Normal file
60
tests/crossplatform/mp_ds2cards.test.sas
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_ds2cards.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_ds2cards.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test 1 - rebuild an existing dataset
|
||||||
|
* Cars is a great dataset - it contains leading spaces, and formatted numerics
|
||||||
|
*/
|
||||||
|
|
||||||
|
%mp_ds2cards(base_ds=sashelp.cars
|
||||||
|
, tgt_ds=work.test
|
||||||
|
, cards_file= "%sysfunc(pathname(work))/cars.sas"
|
||||||
|
, showlog=NO
|
||||||
|
)
|
||||||
|
%inc "%sysfunc(pathname(work))/cars.sas"/source2;
|
||||||
|
|
||||||
|
proc compare base=sashelp.cars compare=work.test;
|
||||||
|
quit;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&sysinfo=1),
|
||||||
|
desc=sashelp.cars is identical except for ds label,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test 2 - binary data compare
|
||||||
|
*/
|
||||||
|
data work.binarybase;
|
||||||
|
format bin $hex500. z $hex.;
|
||||||
|
do x=1 to 250;
|
||||||
|
z=byte(x);
|
||||||
|
bin=trim(bin)!!z;
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_ds2cards(base_ds=work.binarybase
|
||||||
|
, showlog=YES
|
||||||
|
, cards_file="%sysfunc(pathname(work))/c2.sas"
|
||||||
|
, tgt_ds=work.binarycompare
|
||||||
|
, append=
|
||||||
|
)
|
||||||
|
|
||||||
|
%inc "%sysfunc(pathname(work))/c2.sas"/source2;
|
||||||
|
|
||||||
|
proc compare base=work.binarybase compare=work.binarycompare;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(&sysinfo=0),
|
||||||
|
desc=work.binarybase dataset is identical,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
33
tests/crossplatform/mp_getcols.test.sas
Normal file
33
tests/crossplatform/mp_getcols.test.sas
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_getcols macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_getcols.sas
|
||||||
|
@li mp_assertcolvals.sas
|
||||||
|
@li mp_assertdsobs.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
/* valid filter */
|
||||||
|
%mp_getcols(sashelp.airline,outds=work.info)
|
||||||
|
|
||||||
|
|
||||||
|
%mp_assertdsobs(work.info,
|
||||||
|
desc=Has 3 records,
|
||||||
|
test=EQUALS 3,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
data work.check;
|
||||||
|
length val $10;
|
||||||
|
do val='NUMERIC','DATE','CHARACTER';
|
||||||
|
output;
|
||||||
|
end;
|
||||||
|
run;
|
||||||
|
%mp_assertcolvals(work.info.ddtype,
|
||||||
|
checkvals=work.check.val,
|
||||||
|
desc=All values have a match,
|
||||||
|
test=ALLVALS
|
||||||
|
)
|
||||||
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
|
||||||
|
)
|
||||||
66
tests/crossplatform/mp_gsubfile.test.sas
Normal file
66
tests/crossplatform/mp_gsubfile.test.sas
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_gsubfile.sas macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_gsubfile.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test 1 - simple replace
|
||||||
|
*/
|
||||||
|
%global str1;
|
||||||
|
%let file=%sysfunc(pathname(work))/file.txt;
|
||||||
|
%let pat=replace/me;
|
||||||
|
%let str=with/this;
|
||||||
|
data _null_;
|
||||||
|
file "&file";
|
||||||
|
put "&pat";
|
||||||
|
run;
|
||||||
|
%mp_gsubfile(file=&file, patternvar=pat, replacevar=str)
|
||||||
|
data _null_;
|
||||||
|
infile "&file";
|
||||||
|
input;
|
||||||
|
call symputx('str1',_infile_);
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&str1"="&str"),
|
||||||
|
desc=Check that simple replacement was successful,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test 2 - replace from additional line
|
||||||
|
*/
|
||||||
|
%global str2 strcheck2 strcheck2b;
|
||||||
|
%let file2=%sysfunc(pathname(work))/file2.txt;
|
||||||
|
%let pat2=replace/me;
|
||||||
|
%let str2=with/this;
|
||||||
|
data _null_;
|
||||||
|
file "&file2";
|
||||||
|
put 'line1';output;
|
||||||
|
put "&pat2";output;
|
||||||
|
put "&pat2";output;
|
||||||
|
run;
|
||||||
|
%mp_gsubfile(file=&file2, patternvar=pat2, replacevar=str2)
|
||||||
|
data _null_;
|
||||||
|
infile "&file2";
|
||||||
|
input;
|
||||||
|
if _n_=2 then call symputx('strcheck2',_infile_);
|
||||||
|
if _n_=3 then call symputx('strcheck2b',_infile_);
|
||||||
|
putlog _infile_;
|
||||||
|
run;
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&strcheck2"="&str2"),
|
||||||
|
desc=Check that multi line replacement was successful (line2),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=("&strcheck2b"="&str2"),
|
||||||
|
desc=Check that multi line replacement was successful (line3),
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
39
tests/crossplatform/mp_webin.test.sas
Normal file
39
tests/crossplatform/mp_webin.test.sas
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
@file
|
||||||
|
@brief Testing mp_webin macro
|
||||||
|
|
||||||
|
<h4> SAS Macros </h4>
|
||||||
|
@li mp_webin.sas
|
||||||
|
@li mp_assert.sas
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
/* force SAS9 tests as we don't have a valid URI available */
|
||||||
|
%macro mf_getplatform();
|
||||||
|
SAS9
|
||||||
|
%mend mf_getplatform;
|
||||||
|
|
||||||
|
/* TEST 1 */
|
||||||
|
%let _webin_file_count=1;
|
||||||
|
%let _webin_filename=test;
|
||||||
|
%mp_webin()
|
||||||
|
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%symexist(_WEBIN_FILEREF1)
|
||||||
|
),
|
||||||
|
desc=Checking if the macvar exists,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
/* TEST 2 */
|
||||||
|
%global _WEBIN_FILENAME1;
|
||||||
|
%mp_assert(
|
||||||
|
iftrue=(
|
||||||
|
%str(&_WEBIN_FILENAME1)=%str(test)
|
||||||
|
),
|
||||||
|
desc=Checking if the macvar exists,
|
||||||
|
outds=work.test_results
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -590,6 +590,7 @@ data _null_;
|
|||||||
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
put ' put ",""SYSCC"" : ""&syscc"" "; ';
|
||||||
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
put ' put ",""SYSERRORTEXT"" : ""&syserrortext"" "; ';
|
||||||
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
|
||||||
|
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
|
||||||
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
put ' put ",""SYSSITE"" : ""&syssite"" "; ';
|
||||||
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
|
||||||
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
put ' put '',"SYSVLONG" : '' sysvlong; ';
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ options noquotelenmax;
|
|||||||
run;
|
run;
|
||||||
libname &libref2 JSON fileref=&fname2;
|
libname &libref2 JSON fileref=&fname2;
|
||||||
data &outds;
|
data &outds;
|
||||||
|
length id $36 name $128 uri $64 type $32 description $256;
|
||||||
set &libref2..items;
|
set &libref2..items;
|
||||||
run;
|
run;
|
||||||
filename &fname2 clear;
|
filename &fname2 clear;
|
||||||
|
|||||||
@@ -215,6 +215,7 @@
|
|||||||
put ",""SYSCC"" : ""&syscc"" ";
|
put ",""SYSCC"" : ""&syscc"" ";
|
||||||
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
put ",""SYSERRORTEXT"" : ""&syserrortext"" ";
|
||||||
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
put ",""SYSHOSTNAME"" : ""&syshostname"" ";
|
||||||
|
put ",""SYSSCPL"" : ""&sysscpl"" ";
|
||||||
put ",""SYSSITE"" : ""&syssite"" ";
|
put ",""SYSSITE"" : ""&syssite"" ";
|
||||||
sysvlong=quote(trim(symget('sysvlong')));
|
sysvlong=quote(trim(symget('sysvlong')));
|
||||||
put ',"SYSVLONG" : ' sysvlong;
|
put ',"SYSVLONG" : ' sysvlong;
|
||||||
|
|||||||
Reference in New Issue
Block a user