1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-11 06:24:35 +00:00

Compare commits

...

28 Commits

Author SHA1 Message Date
Allan Bowe
984ea44f5d Merge pull request #311 from sasjs/allanbowe/mv-createfile-needs-a-310
feat: adding ctype option to mv_createfile.sas macro
2022-09-13 20:37:28 +01:00
Allan Bowe
88f1222abd Merge branch 'main' into allanbowe/mv-createfile-needs-a-310 2022-09-13 20:37:03 +01:00
Allan Bowe
d88f028ee3 chore: removing ovpn from pipeline 2022-09-06 22:27:40 +00:00
Allan Bowe
07d7c9df4b feat: adding ctype option to mv_createfile.sas macro 2022-09-06 21:20:00 +00:00
munja
6765a1d025 chore(docs): image link 2022-09-03 18:00:00 +01:00
Allan Bowe
952f28a872 Merge pull request #309 from sasjs/dictionary
feat: new mp_dictionary() table
2022-09-03 16:53:05 +01:00
munja
8246b5a42c feat: new mp_dictionary() table 2022-09-03 16:50:11 +01:00
Allan Bowe
72123aeeb7 Merge pull request #305 from sasjs/cli1229
Making _addjesbeginendmacros configurable
2022-08-25 14:21:04 +01:00
Allan Bowe
236d1ae25f Merge branch 'main' into cli1229 2022-08-25 14:20:57 +01:00
munja
b75369b28d fix: pgm uninitialised in mm_getstpinfo 2022-08-23 16:00:42 +01:00
Allan Bowe
63871db170 Merge pull request #308 from sasjs/allanbowe/mp-jsonout-does-not-replace-307
fix: support for SUB (1A) hex char in DATASTEP generated JSON.
2022-08-22 14:16:13 +01:00
Allan Bowe
6456c2f6e2 fix: support for SUB (1A) hex char in DATASTEP generated JSON. Closes #307 2022-08-22 13:14:20 +00:00
munja
36faa194a8 chore(docs): more related files in mp_dsmeta.sas 2022-08-21 21:15:24 +01:00
munja
093dc87aad chore(docs): crediting louise 2022-08-21 19:55:02 +01:00
munja
ca045e3ebf chore(docs): typo 2022-08-21 19:27:52 +01:00
Allan Bowe
be5e2f371d Merge pull request #306 from sasjs/mp_dsmeta
feat: new mp_dsmeta macro
2022-08-21 19:18:56 +01:00
munja
6d15465bac fix: generating all.sas and fixing failing test 2022-08-21 19:17:56 +01:00
munja
2031a5b0c0 feat: new mp_dsmeta macro 2022-08-21 19:01:01 +01:00
Allan Bowe
7b3844a391 chore: updating all.sas 2022-08-21 16:02:20 +00:00
Allan Bowe
202de36042 fix: options to remove _addjesbeginendmacros from Viya Jobs 2022-08-21 16:01:50 +00:00
Allan Bowe
62837b512b feat: mm_getstpinfo.sas
Actually this came from a previous commit but the message was squashed out:  1b5effd584
2022-08-19 11:28:15 +01:00
Allan Bowe
5d5a99fd77 Merge pull request #304 from sasjs/allanbowe/need-a-macro-to-extract-303
chore(lint): reduce length
2022-08-19 11:00:00 +01:00
Allan Bowe
1b5effd584 chore(lint): reduce length 2022-08-19 09:58:42 +00:00
Allan Bowe
1613ab2c9e Merge pull request #302 from sasjs/allanbowe/proc-format-max-can-be-300
fix: switching MAX for LENGTH to get max label value.  Closes #300
2022-08-17 21:59:14 +01:00
Allan Bowe
a2df4e35be fix: switching MAX for LENGTH to get max label value. Closes #300 2022-08-17 20:54:14 +00:00
Allan Bowe
aabbcfdf6b Merge pull request #299 from sasjs/allanbowe/remove-work-tables-from-298
fix: removing automatic dump of WORK tables in mX_webout macros.  Clo…
2022-08-15 18:48:06 +01:00
Allan Bowe
7b7759e1ce chore: fix renegade closing bracket 2022-08-15 17:44:24 +00:00
Allan Bowe
e5a3053600 fix: removing automatic dump of WORK tables in mX_webout macros. Closes 298 2022-08-15 17:21:00 +00:00
20 changed files with 803 additions and 235 deletions

View File

@@ -1,30 +0,0 @@
cipher AES-256-CBC
setenv FORWARD_COMPATIBLE 1
client
server-poll-timeout 4
nobind
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 443 tcp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
remote vpn.analytium.co.uk 1194 udp
dev tun
dev-type tun
ns-cert-type server
setenv opt tls-version-min 1.0 or-highest
reneg-sec 604800
sndbuf 0
rcvbuf 0
# NOTE: LZO commands are pushed by the Access Server at connect time.
# NOTE: The below line doesn't disable LZO.
comp-lzo no
verb 3
setenv PUSH_PEER_INFO
ca ca.crt
cert user.crt
key user.key
tls-auth tls.key 1

View File

@@ -21,31 +21,6 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Write VPN Files
run: |
echo "$CA_CRT" > .github/vpn/ca.crt
echo "$USER_CRT" > .github/vpn/user.crt
echo "$USER_KEY" > .github/vpn/user.key
echo "$TLS_KEY" > .github/vpn/tls.key
shell: bash
env:
CA_CRT: ${{ secrets.CA_CRT}}
USER_CRT: ${{ secrets.USER_CRT }}
USER_KEY: ${{ secrets.USER_KEY }}
TLS_KEY: ${{ secrets.TLS_KEY }}
- name: Install Open VPN
run: |
sudo apt install apt-transport-https
sudo wget https://swupdate.openvpn.net/repos/openvpn-repo-pkg-key.pub
sudo apt-key add openvpn-repo-pkg-key.pub
sudo wget -O /etc/apt/sources.list.d/openvpn3.list https://swupdate.openvpn.net/community/openvpn3/repos/openvpn3-focal.list
sudo apt update
sudo apt install openvpn3
- name: Start Open VPN 3
run: openvpn3 session-start --config .github/vpn/config.ovpn
- name: Install Doxygen
run: sudo apt-get install doxygen
@@ -78,7 +53,5 @@ jobs:
SECRET: ${{secrets.SECRET}}
SAS_USERNAME: ${{secrets.SAS_USERNAME}}
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
SERVER_URL: ${{secrets.SERVER_URL}}
SERVER_TYPE: ${{secrets.SERVER_TYPE}}
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}

View File

@@ -36,21 +36,21 @@ Documentation: https://core.sasjs.io
- OS independent
- Works on all SAS Platforms
- No X command
- Prefixes: _mf_, _mp_
- Prefixes: `mf_`, `mp_`
### DDL folder (All Platforms)
- OS independent
- Works on all SAS Platforms
- No X command
- Prefixes: _mddl_(lib)_ -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component)
- Prefixes: `mddl_(lib)_` -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component)
This library will not be used for storing data entries (such as formats or datalines). Where this becomes necessary in the future, a new repo will be created, in order to keep the NPM bundle size down (for the benefit of those looking to embed purely macros in their applications).
### FCMP folder (All Platforms)
- Function and macro names are identical, except for special cases
- Prefixes: _mcf_
- Prefixes: `mcf_`
The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper.
@@ -72,7 +72,7 @@ endsubmit;
run;
```
- Prefixes: _ml_
- Prefixes: `ml_`
### META folder (SAS9 only)
@@ -81,14 +81,14 @@ Macros used in SAS EBI, which connect to the metadata server.
- OS independent
- Metadata aware
- No X command
- Prefixes: _mm_
- Prefixes: `mm_`
### METAX folder (SAS9 only)
- OS specific
- Metadata aware
- X command enabled
- Prefixes: _mmw_,_mmu_,_mmx_
- Prefixes: `mmx_`
### SERVER folder (@sasjs/server only)
These macros are used for building applications using [@sasjs/server](https://server.sasjs.io) - an open source REST API for Desktop SAS.
@@ -96,7 +96,7 @@ These macros are used for building applications using [@sasjs/server](https://se
- OS independent
- @sasjs/server aware
- No X command
- Prefixes: _ms_
- Prefixes: `ms_`
### VIYA folder (Viya only)
@@ -104,7 +104,7 @@ Macros used for interfacing with SAS Viya.
- OS independent
- No X command
- Prefixes: _mv_, _mvf_
- Prefixes: `mv_`, `mvf_`
### XPLATFORM folder (Viya, Meta, and Server)
@@ -112,7 +112,7 @@ Sometimes it is helpful to use a macro that can be used interchangeably regardle
- OS independent
- No X command
- Prefixes: _mx_
- Prefixes: `mx_`
## Installation

452
all.sas
View File

@@ -4112,11 +4112,14 @@ proc sql;
%mp_deleteconstraints(inds=work.constraints,outds=dropped,execute=YES)
%mp_createconstraints(inds=work.constraints,outds=created,execute=YES)
@param inds= The input table containing the constraint info
@param outds= a table containing the create statements (create_statement column)
@param execute= `YES|NO` - default is NO. To actually create, use YES.
@param inds= (work.mp_getconstraints) The input table containing the
constraint info
@param outds= (work.mp_createconstraints) A table containing the create
statements (create_statement column)
@param execute= (NO) To actually create, use YES.
<h4> SAS Macros </h4>
<h4> Related Files </h4>
@li mp_getconstraints.sas
@version 9.2
@author Allan Bowe
@@ -4124,7 +4127,7 @@ proc sql;
**/
%macro mp_createconstraints(inds=mp_getconstraints
,outds=mp_createconstraints
,outds=work.mp_createconstraints
,execute=NO
)/*/STORE SOURCE*/;
@@ -4158,7 +4161,8 @@ data &outds;
output;
run;
%mend mp_createconstraints;/**
%mend mp_createconstraints;
/**
@file mp_createwebservice.sas
@brief Create a web service in SAS 9, Viya or SASjs Server
@details This is actually a wrapper for mx_createwebservice.sas, remaining
@@ -4450,6 +4454,58 @@ run;
%end;
%else %put &sysmacroname: &folder: is not a valid / accessible folder. ;
%mend mp_deletefolder;/**
@file mp_dictionary.sas
@brief Creates a portal (libref) into the SQL Dictionary Views
@details Provide a libref and the macro will create a series of views against
each view in the special PROC SQL dictionary libref.
This is useful if you would like to visualise (navigate) the views in a SAS
client such as Base SAS, Enterprise Guide, or Studio (or [Data Controller](
https://datacontroller.io)).
It works by extracting the dictionary.dictionaries view into
YOURLIB.dictionaries, then uses that to create a YOURLIB.{viewName} for every
other dictionary.view, eg:
proc sql;
create view YOURLIB.columns as select * from dictionary.columns;
Usage:
libname demo "/lib/directory";
%mp_dictionary(lib=demo)
Or, to just create them in WORK:
%mp_dictionary()
If you'd just like to browse the dictionary data model, you can also check
out [this article](https://rawsas.com/dictionary-of-dictionaries/).
![](https://user-images.githubusercontent.com/4420615/188278365-2987db97-0594-4a39-ac81-dbacdef5cdc8.png)
@param lib= (WORK) The libref in which to create the views
<h4> Related Files </h4>
@li mp_dictionary.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_dictionary(lib=WORK)/*/STORE SOURCE*/;
%local list i mem;
proc sql noprint;
create view &lib..dictionaries as select * from dictionary.dictionaries;
select distinct memname into: list separated by ' ' from &lib..dictionaries;
%do i=1 %to %sysfunc(countw(&list,%str( )));
%let mem=%scan(&list,&i,%str( ));
create view &lib..&mem as select * from dictionary.&mem;
%end;
quit;
%mend mp_dictionary;
/**
@file
@brief Returns all files and subdirectories within a specified parent
@details When used with getattrs=NO, is not OS specific (uses dopen / dread).
@@ -5826,6 +5882,116 @@ options varlenchk=&optval;
%put &sysmacroname: &outds is %mf_getfilesize(libds=&outds,format=yes);
%mend mp_ds2squeeze;/**
@file
@brief Export dataset metadata to a single output table
@details Exports the dataset attributes and enginehost information, then
converts the datasets into a single output table in the following format:
|ODS_TABLE:$10.|NAME:$100.|VALUE:$1000.|
|---|---|---|
|`ATTRIBUTES `|`Data Set Name `|`SASHELP.CLASS `|
|`ATTRIBUTES `|`Observations `|`19 `|
|`ATTRIBUTES `|`Member Type `|`DATA `|
|`ATTRIBUTES `|`Variables `|`5 `|
|`ATTRIBUTES `|`Engine `|`V9 `|
|`ATTRIBUTES `|`Indexes `|`0 `|
|`ATTRIBUTES `|`Created `|`06/08/2020 00:59:14 `|
|`ATTRIBUTES `|`Observation Length `|`40 `|
|`ATTRIBUTES `|`Last Modified `|`06/08/2020 00:59:14 `|
|`ATTRIBUTES `|`Deleted Observations `|`0 `|
|`ATTRIBUTES `|`Protection `|`. `|
|`ATTRIBUTES `|`Compressed `|`NO `|
|`ATTRIBUTES `|`Data Set Type `|`. `|
|`ATTRIBUTES `|`Sorted `|`NO `|
|`ATTRIBUTES `|`Label `|`Student Data `|
|`ATTRIBUTES `|`Data Representation `|`SOLARIS_X86_64, LINUX_X86_64, ALPHA_TRU64, LINUX_IA64 `|
|`ATTRIBUTES `|`Encoding `|`us-ascii ASCII (ANSI) `|
|`ENGINEHOST `|`Data Set Page Size `|`65536 `|
|`ENGINEHOST `|`Number of Data Set Pages `|`1 `|
|`ENGINEHOST `|`First Data Page `|`1 `|
|`ENGINEHOST `|`Max Obs per Page `|`1632 `|
|`ENGINEHOST `|`Obs in First Data Page `|`19 `|
|`ENGINEHOST `|`Number of Data Set Repairs `|`0 `|
|`ENGINEHOST `|`Filename `|`/opt/sas/sas9/SASHome/SASFoundation/9.4/sashelp/class.sas7bdat `|
|`ENGINEHOST `|`Release Created `|`9.0401M7 `|
|`ENGINEHOST `|`Host Created `|`Linux `|
|`ENGINEHOST `|`Inode Number `|`28314616 `|
|`ENGINEHOST `|`Access Permission `|`rw-r--r-- `|
|`ENGINEHOST `|`Owner Name `|`sas `|
|`ENGINEHOST `|`File Size `|`128KB `|
|`ENGINEHOST `|`File Size (bytes) `|`131072 `|
Example usage:
%mp_dsmeta(sashelp.class,outds=work.mymeta)
proc print data=work.mymeta;
run;
For more details on creating datasets from PROC CONTENTS check out this
excellent [paper](
https://support.sas.com/resources/papers/proceedings14/1549-2014.pdf) by
[Louise Hadden](https://www.linkedin.com/in/louisehadden/).
@param libds The library.dataset to export the metadata for
@param outds= (work.dsmeta) The output table to contain the metadata
<h4> Related Files </h4>
@li mp_dsmeta.test.sas
@li mp_getcols.sas
@li mp_getdbml.sas
@li mp_getddl.sas
@li mp_getformats.sas
@li mp_getpk.sas
@li mp_guesspk.sas
**/
%macro mp_dsmeta(libds,outds=work.dsmeta);
%local ds1 ds2;
data;run; %let ds1=&syslast;
data;run; %let ds2=&syslast;
/* setup the ODS capture */
ods output attributes=&ds1 enginehost=&ds2;
/* export the metadata */
proc contents data=&libds;
run;
/* load it into a single table */
data &outds (keep=ods_table name value);
length ods_table $10 name label2 label1 label $100
value cvalue cvalue1 cvalue2 $1000
nvalue nvalue1 nvalue2 8;
if _n_=1 then call missing (of _all_);
* putlog (_all_)(=);
set &ds1 (in=atrs) &ds2 (in=eng);
if atrs then do;
ods_table='ATTRIBUTES';
name=coalescec(label1,label);
value=coalescec(cvalue1,cvalue,put(coalesce(nvalue1,nvalue),best.));
output;
if label2 ne '' then do;
name=label2;
value=coalescec(cvalue2,put(nvalue2,best.));
output;
end;
end;
else if eng then do;
ods_table='ENGINEHOST';
name=coalescec(label1,label);
value=coalescec(cvalue1,cvalue,put(coalesce(nvalue1,nvalue),best.));
output;
end;
run;
proc sql;
drop table &ds1, &ds2;
%mend mp_dsmeta;
/**
@file
@brief Checks an input filter table for validity
@details Performs checks on the input table to ensure it arrives in the
@@ -8709,7 +8875,7 @@ options
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to JSON
<h4> Related Macros </h4>
<h4> Related Files </h4>
@li mp_ds2fmtds.sas
@version 9.2
@@ -8793,7 +8959,7 @@ options
call symputx(cats('label',_n_),coalescec(label,name),'l');
/* overwritten when fmt=Y and a custom format exists in catalog */
if typelong='num' then call symputx(cats('fmtlen',_n_),200,'l');
else call symputx(cats('fmtlen',_n_),min(32767,ceil((length+3)*1.5)),'l');
else call symputx(cats('fmtlen',_n_),min(32767,ceil((length+10)*1.5)),'l');
run;
%let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
@@ -8849,15 +9015,15 @@ options
%let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
proc sql noprint;
create table &tmpds1 as
select cats(libname,'.',memname) as fmtcat,
fmtname
select cats(libname,'.',memname) as FMTCAT,
FMTNAME
from dictionary.formats
where fmttype='F' and libname is not null
and fmtname in (select format from &colinfo where format is not null)
order by 1;
create table &tmpds2(
FMTNAME char(32),
MAX num length=3
LENGTH num
);
%local catlist cat fmtlist i;
select distinct fmtcat into: catlist separated by ' ' from &tmpds1;
@@ -8866,16 +9032,16 @@ options
proc sql;
select distinct fmtname into: fmtlist separated by ' '
from &tmpds1 where fmtcat="&cat";
proc format lib=&cat cntlout=&tmpds3(keep=fmtname max);
proc format lib=&cat cntlout=&tmpds3(keep=fmtname length);
select &fmtlist;
run;
proc sql;
insert into &tmpds2 select distinct fmtname,max from &tmpds3;
insert into &tmpds2 select distinct fmtname,length from &tmpds3;
%end;
proc sql;
create table &tmpds4 as
select a.*, b.max as maxw
select a.*, b.length as MAXW
from &colinfo a
left join &tmpds2 b
on cats(a.format)=cats(upcase(b.fmtname))
@@ -8886,7 +9052,7 @@ options
call symputx(
cats('fmtlen',_n_),
/* vars need extra padding due to JSON escaping of special chars */
min(32767,ceil((max(length,maxw)+3)*1.5))
min(32767,ceil((max(length,maxw)+10)*1.5))
,'l'
);
run;
@@ -8961,7 +9127,7 @@ options
format _numeric_ bart.;
%do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do;
if findc(&&name&i,'"\'!!'0A0D09000E0F01021011'x) then do;
if findc(&&name&i,'"\'!!'0A0D09000E0F010210111A'x) then do;
&&name&i='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */
@@ -8974,8 +9140,9 @@ options
prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,&&name&i)
))))))))))))!!'"';
)))))))))))))!!'"';
end;
else &&name&i=quote(cats(&&name&i));
%end;
@@ -15288,7 +15455,7 @@ data _null_;
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+10)*1.5)),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -15344,15 +15511,15 @@ data _null_;
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' select cats(libname,''.'',memname) as FMTCAT, ';
put ' FMTNAME ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' MAX num length=3 ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
@@ -15361,16 +15528,16 @@ data _null_;
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname max); ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,max from &tmpds3; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.max as maxw ';
put ' select a.*, b.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
@@ -15381,7 +15548,7 @@ data _null_;
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' min(32767,ceil((max(length,maxw)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
@@ -15456,7 +15623,7 @@ data _null_;
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F010210111A''x) then do; ';
put ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -15469,8 +15636,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
@@ -15566,7 +15734,7 @@ data _null_;
put ' ';
put '%mend mf_getuser; ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX ';
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; ';
@@ -15639,8 +15807,8 @@ data _null_;
put '%else %if &action=CLOSE %then %do; ';
put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
put ' filename _sjsref temp lrecl=131068; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ ';
put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
@@ -15664,7 +15832,9 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; ';
put ' %end; ';
@@ -17657,6 +17827,73 @@ filename __getdoc clear;
%end;
%mend mm_getstpcode;
/**
@file
@brief Get the properties of a Stored Process
@details Extracts various properties and creates an output table in the
structure below:
|STP_URI:$200.|SERVERCONTEXT:$200.|STOREDPROCESSCONFIGURATION:$1000.|SOURCECODE_FIRST32K:$32767.|PATH:$76.|
|---|---|---|---|---|
|`A5DN9TDQ.BH0000C8 `|`SASApp `|`<?xml version="1.0" encoding="UTF-8"?><StoredProcess><ServerContext LogicalServerType="Sps" OtherAllowed="false"/><ResultCapabilities Package="false" Streaming="true"/><OutputParameters/></StoredProcess> `|`%put first 32767 bytes of code; `|`/path/to/my/stp`|
@param [in] pgm The metadata path of the Stored Process
@param [out] outds= (work.mm_getstpinfo) The output table to create
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
<h4> Related Files </h4>
@li mm_getstpcode.sas
@li mm_getstps.sas
@li mm_createstp.sas
@li mm_deletestp.sas
**/
%macro mm_getstpinfo(pgm
,outds=work.mm_getstpinfo
,mDebug=0
);
%local mD;
%if &mDebug=1 %then %let mD=;
%else %let mD=%str(*);
%&mD.put Executing &sysmacroname..sas;
%&mD.put _local_;
data &outds;
length type stp_uri tsuri servercontext value $200
StoredProcessConfiguration $1000 sourcecode_first32k $32767;
keep path stp_uri sourcecode_first32k StoredProcessConfiguration
servercontext;
call missing (of _all_);
path="&pgm(StoredProcess)";
/* first, find the STP ID */
if metadata_pathobj("",path,"StoredProcess",type,stp_uri)>0 then do;
/* get attributes */
cnt=1;
do while (metadata_getnasn(stp_uri,"Notes",cnt,tsuri)>0);
rc1=metadata_getattr(tsuri,"Name",value);
&mD.put tsuri= value=;
if value="SourceCode" then do;
rc2=metadata_getattr(tsuri,"StoredText",sourcecode_first32k);
end;
else if value="Stored Process" then do;
rc3=metadata_getattr(tsuri,"StoredText",StoredProcessConfiguration);
end;
cnt+1;
end;
/* get context (should only be one) */
rc4=metadata_getnasn(stp_uri,"ComputeLocations",1,tsuri);
rc5=metadata_getattr(tsuri,"Name",servercontext);
end;
else do;
put "%str(ERR)OR: could not find " path;
put (_all_)(=);
end;
&md.put (_all_)(=);
run;
%mend mm_getstpinfo ;
/**
@file
@brief Returns a dataset with all Stored Processes, or just those in a
@@ -19125,6 +19362,9 @@ run;
such as the column formats and types. The metadata is contained inside an
object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON
@@ -19140,7 +19380,7 @@ run;
**/
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N,maxobs=MAX
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables;
@@ -19213,8 +19453,8 @@ run;
%else %if &action=CLOSE %then %do;
/* To avoid issues with _webout on EBI we use a temporary file */
filename _sjsref temp lrecl=131068;
%if %str(&_debug) ge 131 %then %do;
/* if debug mode, send back first 10 records of each work table also */
%if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds;
proc datasets library=WORK memtype=data;
@@ -19238,7 +19478,9 @@ run;
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10)
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10
,maxobs=&workobs
)
data _null_; file _sjsref mod encoding='utf-8';
put "}";
%end;
@@ -20235,7 +20477,7 @@ data _null_;
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+10)*1.5)),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -20291,15 +20533,15 @@ data _null_;
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' select cats(libname,''.'',memname) as FMTCAT, ';
put ' FMTNAME ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' MAX num length=3 ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
@@ -20308,16 +20550,16 @@ data _null_;
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname max); ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,max from &tmpds3; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.max as maxw ';
put ' select a.*, b.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
@@ -20328,7 +20570,7 @@ data _null_;
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' min(32767,ceil((max(length,maxw)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
@@ -20403,7 +20645,7 @@ data _null_;
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F010210111A''x) then do; ';
put ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -20416,8 +20658,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
@@ -20514,7 +20757,7 @@ data _null_;
put '%mend mf_getuser; ';
put ' ';
put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX ';
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; ';
@@ -20577,8 +20820,8 @@ data _null_;
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ ';
put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
@@ -20603,7 +20846,9 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
put ' put "}"; ';
put ' %end; ';
@@ -21488,6 +21733,9 @@ run;
such as the column formats and types. The metadata is contained inside an
object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON
@@ -21506,7 +21754,7 @@ run;
**/
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N,maxobs=MAX
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables;
@@ -21569,8 +21817,8 @@ run;
)
%end;
%else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do;
/* if debug mode, send back first 10 records of each work table also */
%if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds;
proc datasets library=WORK memtype=data;
@@ -21595,7 +21843,9 @@ run;
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10
,maxobs=&workobs
)
data _null_; file &fref mod encoding='utf-8' termstr=lf;
put "}";
%end;
@@ -21765,7 +22015,8 @@ run;
@param [in] contentdisp= (inline) Content Disposition. Example values:
@li inline
@li attachment
@param [in] ctype= (0) Set a default HTTP Content-Type header to be returned
with the file when the content is retrieved from the Files service.
@param [in] access_token_var= The global macro variable to contain the access
token, if using authorization_code grant type.
@param [in] grant_type= (sas_services) Valid values are:
@@ -21793,6 +22044,7 @@ run;
,inref=
,intype=BINARY
,contentdisp=inline
,ctype=0
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,mdebug=0
@@ -21844,8 +22096,10 @@ filename &fref filesrvc
folderPath="&path"
filename="&name"
cdisp="&contentdisp"
%if "&ctype" ne "0" %then %do;
ctype="&ctype"
%end;
lrecl=1048544;
%if &intype=BINARY %then %do;
%mp_binarycopy(inref=&inref, outref=&fref)
%end;
@@ -22082,14 +22336,25 @@ options noquotelenmax;
@param path= The full path (on SAS Drive) where the job will be created
@param name= The name of the job
@param desc= The description of the job
@param desc= (Created by the mv_createjob.sas macro) The job description
@param precode= Space separated list of filerefs, pointing to the code that
needs to be attached to the beginning of the job
@param code= Fileref(s) of the actual code to be added
@param access_token_var= The global macro variable to contain the access token
@param grant_type= valid values are "password" or "authorization_code"
(unquoted). The default is authorization_code.
@param replace= select NO to avoid replacing any existing job in that location
@param code= (ft15f001) Fileref(s) of the actual code to be added
@param access_token_var= (ACCESS_TOKEN) Global macro variable containing the
access token
@param grant_type= (sas_services) Valid values:
@li sas_services
@li detect
@li authorization_code
@li password
@param replace= (YES) select NO to avoid replacing any existing job
@param addjesbeginendmacros= (false) Relates to the `_addjesbeginendmacros`
setting. Normally this would always be false however due to a Viya bug
(https://github.com/sasjs/cli/issues/1229) this is now configurable. Valid
values:
@li true
@li false
@li 0 - this will prevent the flag from being set (job will default to true)
@param contextname= Choose a specific context on which to run the Job. Leave
blank to use the default context. From Viya 3.5 it is possible to configure
a shared context - see
@@ -22110,6 +22375,7 @@ https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5p
,replace=YES
,debug=0
,contextname=
,addjesbeginendmacros=false
);
%local oauth_bearer;
%if &grant_type=detect %then %do;
@@ -22233,19 +22499,29 @@ run;
%end;
/* set up the body of the request to create the service */
%local fname3;
%local fname3 comma;
%let fname3=%mf_getuniquefileref();
data _null_;
file &fname3 TERMSTR=' ';
length string $32767;
string=cats('{"version": 0,"name":"'
,"&name"
,'","type":"Compute","parameters":[{"name":"_addjesbeginendmacros"'
,',"type":"CHARACTER","defaultValue":"false"}');
,'","type":"Compute","parameters":['
%if &addjesbeginendmacros ne 0 %then %do;
,'{"name":"_addjesbeginendmacros"'
,',"type":"CHARACTER","defaultValue":"'
,"&addjesbeginendmacros"
,'"}'
%let comma=%str(,);
%end;
);
context=quote(cats(symget('contextname')));
if context ne '""' then do;
string=cats(string,',{"version": 1,"name": "_contextName","defaultValue":'
,context,',"type":"CHARACTER","label":"Context Name","required": false}');
string=cats(string
,"&comma"
,'{"version": 1,"name": "_contextName","defaultValue":'
,context,',"type":"CHARACTER","label":"Context Name","required": false}'
);
end;
string=cats(string,'],"code":"');
put string;
@@ -22691,7 +22967,7 @@ data _null_;
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+10)*1.5)),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -22747,15 +23023,15 @@ data _null_;
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' select cats(libname,''.'',memname) as FMTCAT, ';
put ' FMTNAME ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' MAX num length=3 ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
@@ -22764,16 +23040,16 @@ data _null_;
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname max); ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,max from &tmpds3; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.max as maxw ';
put ' select a.*, b.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
@@ -22784,7 +23060,7 @@ data _null_;
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' min(32767,ceil((max(length,maxw)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
@@ -22859,7 +23135,7 @@ data _null_;
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F010210111A''x) then do; ';
put ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -22872,8 +23148,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
@@ -22969,7 +23246,7 @@ data _null_;
put ' ';
put '%mend mf_getuser; ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX ';
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI; ';
@@ -23076,8 +23353,8 @@ data _null_;
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* send back first 10 records of each work table for debugging */ ';
put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* send back first XX records of each work table for debugging */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
@@ -23100,7 +23377,9 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file &fref mod;put "}"; ';
put ' %end; ';
put ' data _null_; file &fref mod;put "}";run; ';
@@ -26757,6 +27036,9 @@ filename &fref1 clear;
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@@ -26771,7 +27053,7 @@ filename &fref1 clear;
**/
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL
,showmeta=N,maxobs=MAX
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI;
@@ -26878,8 +27160,8 @@ filename &fref1 clear;
)
%end;
%else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do;
/* send back first 10 records of each work table for debugging */
%if %str(&workobs) > 0 %then %do;
/* send back first XX records of each work table for debugging */
data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds;
proc datasets library=WORK memtype=data;
@@ -26902,7 +27184,9 @@ filename &fref1 clear;
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs
)
data _null_; file &fref mod;put "}";
%end;
data _null_; file &fref mod;put "}";run;

View File

@@ -18,11 +18,14 @@
%mp_deleteconstraints(inds=work.constraints,outds=dropped,execute=YES)
%mp_createconstraints(inds=work.constraints,outds=created,execute=YES)
@param inds= The input table containing the constraint info
@param outds= a table containing the create statements (create_statement column)
@param execute= `YES|NO` - default is NO. To actually create, use YES.
@param inds= (work.mp_getconstraints) The input table containing the
constraint info
@param outds= (work.mp_createconstraints) A table containing the create
statements (create_statement column)
@param execute= (NO) To actually create, use YES.
<h4> SAS Macros </h4>
<h4> Related Files </h4>
@li mp_getconstraints.sas
@version 9.2
@author Allan Bowe
@@ -30,7 +33,7 @@
**/
%macro mp_createconstraints(inds=mp_getconstraints
,outds=mp_createconstraints
,outds=work.mp_createconstraints
,execute=NO
)/*/STORE SOURCE*/;
@@ -64,4 +67,4 @@ data &outds;
output;
run;
%mend mp_createconstraints;
%mend mp_createconstraints;

52
base/mp_dictionary.sas Normal file
View File

@@ -0,0 +1,52 @@
/**
@file mp_dictionary.sas
@brief Creates a portal (libref) into the SQL Dictionary Views
@details Provide a libref and the macro will create a series of views against
each view in the special PROC SQL dictionary libref.
This is useful if you would like to visualise (navigate) the views in a SAS
client such as Base SAS, Enterprise Guide, or Studio (or [Data Controller](
https://datacontroller.io)).
It works by extracting the dictionary.dictionaries view into
YOURLIB.dictionaries, then uses that to create a YOURLIB.{viewName} for every
other dictionary.view, eg:
proc sql;
create view YOURLIB.columns as select * from dictionary.columns;
Usage:
libname demo "/lib/directory";
%mp_dictionary(lib=demo)
Or, to just create them in WORK:
%mp_dictionary()
If you'd just like to browse the dictionary data model, you can also check
out [this article](https://rawsas.com/dictionary-of-dictionaries/).
![](https://user-images.githubusercontent.com/4420615/188278365-2987db97-0594-4a39-ac81-dbacdef5cdc8.png)
@param lib= (WORK) The libref in which to create the views
<h4> Related Files </h4>
@li mp_dictionary.test.sas
@version 9.2
@author Allan Bowe
**/
%macro mp_dictionary(lib=WORK)/*/STORE SOURCE*/;
%local list i mem;
proc sql noprint;
create view &lib..dictionaries as select * from dictionary.dictionaries;
select distinct memname into: list separated by ' ' from &lib..dictionaries;
%do i=1 %to %sysfunc(countw(&list,%str( )));
%let mem=%scan(&list,&i,%str( ));
create view &lib..&mem as select * from dictionary.&mem;
%end;
quit;
%mend mp_dictionary;

110
base/mp_dsmeta.sas Normal file
View File

@@ -0,0 +1,110 @@
/**
@file
@brief Export dataset metadata to a single output table
@details Exports the dataset attributes and enginehost information, then
converts the datasets into a single output table in the following format:
|ODS_TABLE:$10.|NAME:$100.|VALUE:$1000.|
|---|---|---|
|`ATTRIBUTES `|`Data Set Name `|`SASHELP.CLASS `|
|`ATTRIBUTES `|`Observations `|`19 `|
|`ATTRIBUTES `|`Member Type `|`DATA `|
|`ATTRIBUTES `|`Variables `|`5 `|
|`ATTRIBUTES `|`Engine `|`V9 `|
|`ATTRIBUTES `|`Indexes `|`0 `|
|`ATTRIBUTES `|`Created `|`06/08/2020 00:59:14 `|
|`ATTRIBUTES `|`Observation Length `|`40 `|
|`ATTRIBUTES `|`Last Modified `|`06/08/2020 00:59:14 `|
|`ATTRIBUTES `|`Deleted Observations `|`0 `|
|`ATTRIBUTES `|`Protection `|`. `|
|`ATTRIBUTES `|`Compressed `|`NO `|
|`ATTRIBUTES `|`Data Set Type `|`. `|
|`ATTRIBUTES `|`Sorted `|`NO `|
|`ATTRIBUTES `|`Label `|`Student Data `|
|`ATTRIBUTES `|`Data Representation `|`SOLARIS_X86_64, LINUX_X86_64, ALPHA_TRU64, LINUX_IA64 `|
|`ATTRIBUTES `|`Encoding `|`us-ascii ASCII (ANSI) `|
|`ENGINEHOST `|`Data Set Page Size `|`65536 `|
|`ENGINEHOST `|`Number of Data Set Pages `|`1 `|
|`ENGINEHOST `|`First Data Page `|`1 `|
|`ENGINEHOST `|`Max Obs per Page `|`1632 `|
|`ENGINEHOST `|`Obs in First Data Page `|`19 `|
|`ENGINEHOST `|`Number of Data Set Repairs `|`0 `|
|`ENGINEHOST `|`Filename `|`/opt/sas/sas9/SASHome/SASFoundation/9.4/sashelp/class.sas7bdat `|
|`ENGINEHOST `|`Release Created `|`9.0401M7 `|
|`ENGINEHOST `|`Host Created `|`Linux `|
|`ENGINEHOST `|`Inode Number `|`28314616 `|
|`ENGINEHOST `|`Access Permission `|`rw-r--r-- `|
|`ENGINEHOST `|`Owner Name `|`sas `|
|`ENGINEHOST `|`File Size `|`128KB `|
|`ENGINEHOST `|`File Size (bytes) `|`131072 `|
Example usage:
%mp_dsmeta(sashelp.class,outds=work.mymeta)
proc print data=work.mymeta;
run;
For more details on creating datasets from PROC CONTENTS check out this
excellent [paper](
https://support.sas.com/resources/papers/proceedings14/1549-2014.pdf) by
[Louise Hadden](https://www.linkedin.com/in/louisehadden/).
@param libds The library.dataset to export the metadata for
@param outds= (work.dsmeta) The output table to contain the metadata
<h4> Related Files </h4>
@li mp_dsmeta.test.sas
@li mp_getcols.sas
@li mp_getdbml.sas
@li mp_getddl.sas
@li mp_getformats.sas
@li mp_getpk.sas
@li mp_guesspk.sas
**/
%macro mp_dsmeta(libds,outds=work.dsmeta);
%local ds1 ds2;
data;run; %let ds1=&syslast;
data;run; %let ds2=&syslast;
/* setup the ODS capture */
ods output attributes=&ds1 enginehost=&ds2;
/* export the metadata */
proc contents data=&libds;
run;
/* load it into a single table */
data &outds (keep=ods_table name value);
length ods_table $10 name label2 label1 label $100
value cvalue cvalue1 cvalue2 $1000
nvalue nvalue1 nvalue2 8;
if _n_=1 then call missing (of _all_);
* putlog (_all_)(=);
set &ds1 (in=atrs) &ds2 (in=eng);
if atrs then do;
ods_table='ATTRIBUTES';
name=coalescec(label1,label);
value=coalescec(cvalue1,cvalue,put(coalesce(nvalue1,nvalue),best.));
output;
if label2 ne '' then do;
name=label2;
value=coalescec(cvalue2,put(nvalue2,best.));
output;
end;
end;
else if eng then do;
ods_table='ENGINEHOST';
name=coalescec(label1,label);
value=coalescec(cvalue1,cvalue,put(coalesce(nvalue1,nvalue),best.));
output;
end;
run;
proc sql;
drop table &ds1, &ds2;
%mend mp_dsmeta;

View File

@@ -62,7 +62,7 @@
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to JSON
<h4> Related Macros </h4>
<h4> Related Files </h4>
@li mp_ds2fmtds.sas
@version 9.2
@@ -146,7 +146,7 @@
call symputx(cats('label',_n_),coalescec(label,name),'l');
/* overwritten when fmt=Y and a custom format exists in catalog */
if typelong='num' then call symputx(cats('fmtlen',_n_),200,'l');
else call symputx(cats('fmtlen',_n_),min(32767,ceil((length+3)*1.5)),'l');
else call symputx(cats('fmtlen',_n_),min(32767,ceil((length+10)*1.5)),'l');
run;
%let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
@@ -202,15 +202,15 @@
%let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
proc sql noprint;
create table &tmpds1 as
select cats(libname,'.',memname) as fmtcat,
fmtname
select cats(libname,'.',memname) as FMTCAT,
FMTNAME
from dictionary.formats
where fmttype='F' and libname is not null
and fmtname in (select format from &colinfo where format is not null)
order by 1;
create table &tmpds2(
FMTNAME char(32),
MAX num length=3
LENGTH num
);
%local catlist cat fmtlist i;
select distinct fmtcat into: catlist separated by ' ' from &tmpds1;
@@ -219,16 +219,16 @@
proc sql;
select distinct fmtname into: fmtlist separated by ' '
from &tmpds1 where fmtcat="&cat";
proc format lib=&cat cntlout=&tmpds3(keep=fmtname max);
proc format lib=&cat cntlout=&tmpds3(keep=fmtname length);
select &fmtlist;
run;
proc sql;
insert into &tmpds2 select distinct fmtname,max from &tmpds3;
insert into &tmpds2 select distinct fmtname,length from &tmpds3;
%end;
proc sql;
create table &tmpds4 as
select a.*, b.max as maxw
select a.*, b.length as MAXW
from &colinfo a
left join &tmpds2 b
on cats(a.format)=cats(upcase(b.fmtname))
@@ -239,7 +239,7 @@
call symputx(
cats('fmtlen',_n_),
/* vars need extra padding due to JSON escaping of special chars */
min(32767,ceil((max(length,maxw)+3)*1.5))
min(32767,ceil((max(length,maxw)+10)*1.5))
,'l'
);
run;
@@ -314,7 +314,7 @@
format _numeric_ bart.;
%do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do;
if findc(&&name&i,'"\'!!'0A0D09000E0F01021011'x) then do;
if findc(&&name&i,'"\'!!'0A0D09000E0F010210111A'x) then do;
&&name&i='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */
@@ -327,8 +327,9 @@
prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,&&name&i)
))))))))))))!!'"';
)))))))))))))!!'"';
end;
else &&name&i=quote(cats(&&name&i));
%end;

View File

@@ -169,7 +169,7 @@ data _null_;
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+10)*1.5)),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -225,15 +225,15 @@ data _null_;
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' select cats(libname,''.'',memname) as FMTCAT, ';
put ' FMTNAME ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' MAX num length=3 ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
@@ -242,16 +242,16 @@ data _null_;
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname max); ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,max from &tmpds3; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.max as maxw ';
put ' select a.*, b.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
@@ -262,7 +262,7 @@ data _null_;
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' min(32767,ceil((max(length,maxw)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
@@ -337,7 +337,7 @@ data _null_;
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F010210111A''x) then do; ';
put ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -350,8 +350,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
@@ -447,7 +448,7 @@ data _null_;
put ' ';
put '%mend mf_getuser; ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX ';
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; ';
@@ -520,8 +521,8 @@ data _null_;
put '%else %if &action=CLOSE %then %do; ';
put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
put ' filename _sjsref temp lrecl=131068; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ ';
put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
@@ -545,7 +546,9 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
put ' %mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10 ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; ';
put ' %end; ';

67
meta/mm_getstpinfo.sas Normal file
View File

@@ -0,0 +1,67 @@
/**
@file
@brief Get the properties of a Stored Process
@details Extracts various properties and creates an output table in the
structure below:
|STP_URI:$200.|SERVERCONTEXT:$200.|STOREDPROCESSCONFIGURATION:$1000.|SOURCECODE_FIRST32K:$32767.|PATH:$76.|
|---|---|---|---|---|
|`A5DN9TDQ.BH0000C8 `|`SASApp `|`<?xml version="1.0" encoding="UTF-8"?><StoredProcess><ServerContext LogicalServerType="Sps" OtherAllowed="false"/><ResultCapabilities Package="false" Streaming="true"/><OutputParameters/></StoredProcess> `|`%put first 32767 bytes of code; `|`/path/to/my/stp`|
@param [in] pgm The metadata path of the Stored Process
@param [out] outds= (work.mm_getstpinfo) The output table to create
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages
<h4> Related Files </h4>
@li mm_getstpcode.sas
@li mm_getstps.sas
@li mm_createstp.sas
@li mm_deletestp.sas
**/
%macro mm_getstpinfo(pgm
,outds=work.mm_getstpinfo
,mDebug=0
);
%local mD;
%if &mDebug=1 %then %let mD=;
%else %let mD=%str(*);
%&mD.put Executing &sysmacroname..sas;
%&mD.put _local_;
data &outds;
length type stp_uri tsuri servercontext value $200
StoredProcessConfiguration $1000 sourcecode_first32k $32767;
keep path stp_uri sourcecode_first32k StoredProcessConfiguration
servercontext;
call missing (of _all_);
path="&pgm(StoredProcess)";
/* first, find the STP ID */
if metadata_pathobj("",path,"StoredProcess",type,stp_uri)>0 then do;
/* get attributes */
cnt=1;
do while (metadata_getnasn(stp_uri,"Notes",cnt,tsuri)>0);
rc1=metadata_getattr(tsuri,"Name",value);
&mD.put tsuri= value=;
if value="SourceCode" then do;
rc2=metadata_getattr(tsuri,"StoredText",sourcecode_first32k);
end;
else if value="Stored Process" then do;
rc3=metadata_getattr(tsuri,"StoredText",StoredProcessConfiguration);
end;
cnt+1;
end;
/* get context (should only be one) */
rc4=metadata_getnasn(stp_uri,"ComputeLocations",1,tsuri);
rc5=metadata_getattr(tsuri,"Name",servercontext);
end;
else do;
put "%str(ERR)OR: could not find " path;
put (_all_)(=);
end;
&md.put (_all_)(=);
run;
%mend mm_getstpinfo ;

View File

@@ -34,6 +34,9 @@
such as the column formats and types. The metadata is contained inside an
object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON
@@ -49,7 +52,7 @@
**/
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N,maxobs=MAX
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables;
@@ -122,8 +125,8 @@
%else %if &action=CLOSE %then %do;
/* To avoid issues with _webout on EBI we use a temporary file */
filename _sjsref temp lrecl=131068;
%if %str(&_debug) ge 131 %then %do;
/* if debug mode, send back first 10 records of each work table also */
%if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds;
proc datasets library=WORK memtype=data;
@@ -147,7 +150,9 @@
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10)
%mp_jsonout(OBJ,&wt,jref=_sjsref,dslabel=first10rows,showmeta=Y,maxobs=10
,maxobs=&workobs
)
data _null_; file _sjsref mod encoding='utf-8';
put "}";
%end;

View File

@@ -107,4 +107,4 @@
"contextName": "SAS Job Execution compute context"
}
]
}
}

View File

@@ -170,7 +170,7 @@ data _null_;
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+10)*1.5)),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -226,15 +226,15 @@ data _null_;
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' select cats(libname,''.'',memname) as FMTCAT, ';
put ' FMTNAME ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' MAX num length=3 ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
@@ -243,16 +243,16 @@ data _null_;
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname max); ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,max from &tmpds3; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.max as maxw ';
put ' select a.*, b.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
@@ -263,7 +263,7 @@ data _null_;
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' min(32767,ceil((max(length,maxw)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
@@ -338,7 +338,7 @@ data _null_;
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F010210111A''x) then do; ';
put ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -351,8 +351,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
@@ -449,7 +450,7 @@ data _null_;
put '%mend mf_getuser; ';
put ' ';
put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX ';
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; ';
@@ -512,8 +513,8 @@ data _null_;
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ ';
put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
@@ -538,7 +539,9 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10 ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
put ' put "}"; ';
put ' %end; ';

View File

@@ -31,6 +31,9 @@
such as the column formats and types. The metadata is contained inside an
object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON
@@ -49,7 +52,7 @@
**/
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N,maxobs=MAX
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables;
@@ -112,8 +115,8 @@
)
%end;
%else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do;
/* if debug mode, send back first 10 records of each work table also */
%if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds;
proc datasets library=WORK memtype=data;
@@ -138,7 +141,9 @@
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10
,maxobs=&workobs
)
data _null_; file &fref mod encoding='utf-8' termstr=lf;
put "}";
%end;

View File

@@ -0,0 +1,26 @@
/**
@file
@brief Testing mp_dictionary.sas macro
<h4> SAS Macros </h4>
@li mp_dictionary.sas
@li mp_assert.sas
**/
libname test (work);
%mp_dictionary(lib=test)
proc sql;
create table work.compare1 as select * from test.styles;
create table work.compare2 as select * from dictionary.styles;
proc compare base=compare1 compare=compare2;
run;
%put _all_;
%mp_assert(
iftrue=(%mf_existds(&sysinfo)=0),
desc=Compare was exact,
outds=work.test_results
)

View File

@@ -0,0 +1,32 @@
/**
@file
@brief Testing mp_dsmeta.sas macro
<h4> SAS Macros </h4>
@li mp_assert.sas
@li mp_assertscope.sas
@li mp_dsmeta.sas
**/
data work.Example;
set sashelp.vmacro;
run;
%mp_assertscope(SNAPSHOT)
%mp_dsmeta(work.example,outds=work.test)
%mp_assertscope(COMPARE)
proc sql noprint;
select count(*) into: nobs from work.test;
select count(distinct ods_table) into: tnobs from work.test;
%mp_assert(
iftrue=(&tnobs=2),
desc=Check that both ATTRIBUTES and ENGINEHOST are provided
)
%mp_assert(
iftrue=(&nobs>10),
desc=Check that sufficient details are provided
)

View File

@@ -24,7 +24,8 @@
@param [in] contentdisp= (inline) Content Disposition. Example values:
@li inline
@li attachment
@param [in] ctype= (0) Set a default HTTP Content-Type header to be returned
with the file when the content is retrieved from the Files service.
@param [in] access_token_var= The global macro variable to contain the access
token, if using authorization_code grant type.
@param [in] grant_type= (sas_services) Valid values are:
@@ -52,6 +53,7 @@
,inref=
,intype=BINARY
,contentdisp=inline
,ctype=0
,access_token_var=ACCESS_TOKEN
,grant_type=sas_services
,mdebug=0
@@ -103,8 +105,10 @@ filename &fref filesrvc
folderPath="&path"
filename="&name"
cdisp="&contentdisp"
%if "&ctype" ne "0" %then %do;
ctype="&ctype"
%end;
lrecl=1048544;
%if &intype=BINARY %then %do;
%mp_binarycopy(inref=&inref, outref=&fref)
%end;

View File

@@ -34,14 +34,25 @@
@param path= The full path (on SAS Drive) where the job will be created
@param name= The name of the job
@param desc= The description of the job
@param desc= (Created by the mv_createjob.sas macro) The job description
@param precode= Space separated list of filerefs, pointing to the code that
needs to be attached to the beginning of the job
@param code= Fileref(s) of the actual code to be added
@param access_token_var= The global macro variable to contain the access token
@param grant_type= valid values are "password" or "authorization_code"
(unquoted). The default is authorization_code.
@param replace= select NO to avoid replacing any existing job in that location
@param code= (ft15f001) Fileref(s) of the actual code to be added
@param access_token_var= (ACCESS_TOKEN) Global macro variable containing the
access token
@param grant_type= (sas_services) Valid values:
@li sas_services
@li detect
@li authorization_code
@li password
@param replace= (YES) select NO to avoid replacing any existing job
@param addjesbeginendmacros= (false) Relates to the `_addjesbeginendmacros`
setting. Normally this would always be false however due to a Viya bug
(https://github.com/sasjs/cli/issues/1229) this is now configurable. Valid
values:
@li true
@li false
@li 0 - this will prevent the flag from being set (job will default to true)
@param contextname= Choose a specific context on which to run the Job. Leave
blank to use the default context. From Viya 3.5 it is possible to configure
a shared context - see
@@ -62,6 +73,7 @@ https://go.documentation.sas.com/?docsetId=calcontexts&docsetTarget=n1hjn8eobk5p
,replace=YES
,debug=0
,contextname=
,addjesbeginendmacros=false
);
%local oauth_bearer;
%if &grant_type=detect %then %do;
@@ -185,19 +197,29 @@ run;
%end;
/* set up the body of the request to create the service */
%local fname3;
%local fname3 comma;
%let fname3=%mf_getuniquefileref();
data _null_;
file &fname3 TERMSTR=' ';
length string $32767;
string=cats('{"version": 0,"name":"'
,"&name"
,'","type":"Compute","parameters":[{"name":"_addjesbeginendmacros"'
,',"type":"CHARACTER","defaultValue":"false"}');
,'","type":"Compute","parameters":['
%if &addjesbeginendmacros ne 0 %then %do;
,'{"name":"_addjesbeginendmacros"'
,',"type":"CHARACTER","defaultValue":"'
,"&addjesbeginendmacros"
,'"}'
%let comma=%str(,);
%end;
);
context=quote(cats(symget('contextname')));
if context ne '""' then do;
string=cats(string,',{"version": 1,"name": "_contextName","defaultValue":'
,context,',"type":"CHARACTER","label":"Context Name","required": false}');
string=cats(string
,"&comma"
,'{"version": 1,"name": "_contextName","defaultValue":'
,context,',"type":"CHARACTER","label":"Context Name","required": false}'
);
end;
string=cats(string,'],"code":"');
put string;

View File

@@ -312,7 +312,7 @@ data _null_;
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); ';
put ' /* overwritten when fmt=Y and a custom format exists in catalog */ ';
put ' if typelong=''num'' then call symputx(cats(''fmtlen'',_n_),200,''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.5)),''l''); ';
put ' else call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+10)*1.5)),''l''); ';
put ' run; ';
put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
@@ -368,15 +368,15 @@ data _null_;
put ' %let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql noprint; ';
put ' create table &tmpds1 as ';
put ' select cats(libname,''.'',memname) as fmtcat, ';
put ' fmtname ';
put ' select cats(libname,''.'',memname) as FMTCAT, ';
put ' FMTNAME ';
put ' from dictionary.formats ';
put ' where fmttype=''F'' and libname is not null ';
put ' and fmtname in (select format from &colinfo where format is not null) ';
put ' order by 1; ';
put ' create table &tmpds2( ';
put ' FMTNAME char(32), ';
put ' MAX num length=3 ';
put ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
@@ -385,16 +385,16 @@ data _null_;
put ' proc sql; ';
put ' select distinct fmtname into: fmtlist separated by '' '' ';
put ' from &tmpds1 where fmtcat="&cat"; ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname max); ';
put ' proc format lib=&cat cntlout=&tmpds3(keep=fmtname length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
put ' insert into &tmpds2 select distinct fmtname,max from &tmpds3; ';
put ' insert into &tmpds2 select distinct fmtname,length from &tmpds3; ';
put ' %end; ';
put ' ';
put ' proc sql; ';
put ' create table &tmpds4 as ';
put ' select a.*, b.max as maxw ';
put ' select a.*, b.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
@@ -405,7 +405,7 @@ data _null_;
put ' call symputx( ';
put ' cats(''fmtlen'',_n_), ';
put ' /* vars need extra padding due to JSON escaping of special chars */ ';
put ' min(32767,ceil((max(length,maxw)+3)*1.5)) ';
put ' min(32767,ceil((max(length,maxw)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
@@ -480,7 +480,7 @@ data _null_;
put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F01021011''x) then do; ';
put ' if findc(&&name&i,''"\''!!''0A0D09000E0F010210111A''x) then do; ';
put ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -493,8 +493,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; ';
put ' )))))))))))))!!''"''; ';
put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; ';
@@ -590,7 +591,7 @@ data _null_;
put ' ';
put '%mend mf_getuser; ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL ';
put ' ,showmeta=N,maxobs=MAX ';
put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI; ';
@@ -697,8 +698,8 @@ data _null_;
put ' ) ';
put '%end; ';
put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; ';
put ' /* send back first 10 records of each work table for debugging */ ';
put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* send back first XX records of each work table for debugging */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; ';
@@ -721,7 +722,9 @@ data _null_;
put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10) ';
put ' %mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y ';
put ' ,maxobs=&workobs ';
put ' ) ';
put ' data _null_; file &fref mod;put "}"; ';
put ' %end; ';
put ' data _null_; file &fref mod;put "}";run; ';

View File

@@ -35,6 +35,9 @@
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to output JSON
@param [in] workobs= (0) When set to a positive integer, will create a new
output object (WORK) which contains this number of observations from all
tables in the WORK library.
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@@ -49,7 +52,7 @@
**/
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL
,showmeta=N,maxobs=MAX
,showmeta=N,maxobs=MAX,workobs=0
);
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI;
@@ -156,8 +159,8 @@
)
%end;
%else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do;
/* send back first 10 records of each work table for debugging */
%if %str(&workobs) > 0 %then %do;
/* send back first XX records of each work table for debugging */
data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds;
proc datasets library=WORK memtype=data;
@@ -180,7 +183,9 @@
put " ""&wt"" : {";
put '"nlobs":' nlobs;
put ',"nvars":' nvars;
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y,maxobs=10)
%mp_jsonout(OBJ,&wt,jref=&fref,dslabel=first10rows,showmeta=Y
,maxobs=&workobs
)
data _null_; file &fref mod;put "}";
%end;
data _null_; file &fref mod;put "}";run;