1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-05 08:30:06 +00:00

Compare commits

..

59 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
Allan Bowe
9856d0ef58 Merge pull request #297 from sasjs/allanbowe/improve-efficiency-of-295
Further improvements to mp_jsonout
2022-08-15 00:48:23 +01:00
Allan Bowe
77b37e5503 chore: regenerated web service macros 2022-08-14 23:43:42 +00:00
Allan Bowe
793319fe38 fix: improved JSON performance for wide tables with a lot of formatted values. 50% improvement! 2022-08-14 23:43:19 +00:00
Allan Bowe
594a895ddd Merge pull request #296 from sasjs/allanbowe/improve-efficiency-of-295
fix: performance optimisations. closes #295
2022-08-12 19:54:04 +01:00
Allan Bowe
0d59266b8d fix: performance optimisations. closes #295 2022-08-12 18:13:35 +00:00
Allan Bowe
4863aafaa8 Merge pull request #294 from sasjs/allanbowe/add-maxobs-parameter-293
feat: adding maxobs param to mX_webout macros
2022-08-12 14:14:45 +01:00
Allan Bowe
6015320145 feat: adding maxobs param to mX_webout macros 2022-08-12 13:12:06 +00:00
Allan Bowe
8c09c0bce0 Merge pull request #292 from sasjs/allanbowe/increase-length-for-syswarningtext-291
fix: adding length statement for SYSWARNINGTEXT. Closes #291
2022-08-01 11:43:53 +01:00
Allan Bowe
437943b779 fix: adding length statement for SYSWARNINGTEXT. Closes #291 2022-08-01 10:40:55 +00:00
Allan Bowe
6a090e45b6 Merge pull request #290 from sasjs/allanbowe/mp-cleancsv-does-not-289
fix: enable embedded blanks in mp_cleancsv, closes #289
2022-07-21 23:47:18 +01:00
Allan Bowe
a7dc314204 fix: enable embedded blanks in mp_cleancsv, closes #289 2022-07-21 22:40:43 +00:00
munja
37076eae89 feat: new mmx_createmetafolder macro 2022-07-20 19:17:06 +01:00
munja
9a9f8dc847 chore(docs): adding matomo analytics 2022-07-15 16:06:03 +01:00
Allan Bowe
719b657267 Merge pull request #288 from sasjs/allanbowe/mp-jsonout-truncates-287
fix: avoid truncation for formatted outputs
2022-07-14 15:22:20 +01:00
Allan Bowe
671a615501 chore(docs): updated label 2022-07-14 14:18:27 +00:00
Allan Bowe
884b45bf12 fix: avoid truncation for formatted outputs
Closes #287
2022-07-14 14:16:42 +00:00
Allan Bowe
ff6ae1b066 Merge pull request #286 from sasjs/ddlfix
fix: comment issue in DDL generation
2022-07-14 14:03:29 +01:00
Allan Bowe
d581fec55e fix: comment issue in DDL generation 2022-07-14 13:02:15 +00:00
Allan Bowe
a5613a79bb chore(docs): adding SASJedi link to README 2022-07-14 11:42:34 +01:00
Allan Bowe
c6703e16e8 Merge pull request #285 from sasjs/mf_increment
feat: new mf_increment macro
2022-07-14 08:57:49 +01:00
munja
6587dce95b feat: new mf_increment macro 2022-07-13 23:57:02 +01:00
Allan Bowe
b60e6448b9 Merge pull request #284 from sasjs/allanbowe/dictionary-table-constraints-283
fix: avoid exceptions from dictionary.table_constraints.
2022-07-13 19:05:33 +01:00
Allan Bowe
46d9b58b32 fix: avoid exceptions from dictionary.table_constraints.
Closes #283
2022-07-13 18:01:52 +00:00
Allan Bowe
349cbabc94 Merge pull request #282 from sasjs/allanbowe/error-multiple-lengths-281
fix: prevent warning from `_label_` variable with different lengths
2022-07-12 23:29:47 +01:00
Allan Bowe
9de056a3fc fix: prevent warning from _label_ variable with different lengths
Closes #281
2022-07-12 22:18:01 +00:00
Allan Bowe
ad497b322f chore(tests): adding some extra test cases 2022-07-12 15:03:41 +00:00
Allan Bowe
7a6408ee44 Merge pull request #280 from sasjs/allanbowe/support-special-missings-279
fix: supporting special missings in BETWEEN and IN operators
2022-07-08 00:25:53 +01:00
Allan Bowe
336743f2b4 fix: applying logic to BETWEEN as well as IN 2022-07-07 23:24:24 +00:00
Allan Bowe
6e32eb3bd6 fix: supporting special missings in BETWEEN and IN operators
Impacts mp_filtercheck.sas.  Tests added.  Closes #279
2022-07-07 22:47:04 +00:00
Allan Bowe
b377b83442 Merge pull request #278 from sasjs/allanbowe/add-iftrue-parameter-277
fix: iftrue parameter for mp_binarycopy.  Closes #277
2022-07-07 11:29:25 +01:00
Allan Bowe
899b94bb6e fix: iftrue parameter for mp_binarycopy. Closes #277 2022-07-07 10:28:24 +00:00
36 changed files with 2049 additions and 452 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: with:
node-version: ${{ matrix.node-version }} 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 - name: Install Doxygen
run: sudo apt-get install doxygen run: sudo apt-get install doxygen
@@ -78,7 +53,5 @@ jobs:
SECRET: ${{secrets.SECRET}} SECRET: ${{secrets.SECRET}}
SAS_USERNAME: ${{secrets.SAS_USERNAME}} SAS_USERNAME: ${{secrets.SAS_USERNAME}}
SAS_PASSWORD: ${{secrets.SAS_PASSWORD}} SAS_PASSWORD: ${{secrets.SAS_PASSWORD}}
SERVER_URL: ${{secrets.SERVER_URL}}
SERVER_TYPE: ${{secrets.SERVER_TYPE}}
ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}} ACCESS_TOKEN: ${{secrets.ACCESS_TOKEN}}
REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}} REFRESH_TOKEN: ${{secrets.REFRESH_TOKEN}}

View File

@@ -36,21 +36,21 @@ Documentation: https://core.sasjs.io
- OS independent - OS independent
- Works on all SAS Platforms - Works on all SAS Platforms
- No X command - No X command
- Prefixes: _mf_, _mp_ - Prefixes: `mf_`, `mp_`
### DDL folder (All Platforms) ### DDL folder (All Platforms)
- OS independent - OS independent
- Works on all SAS Platforms - Works on all SAS Platforms
- No X command - 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). 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) ### FCMP folder (All Platforms)
- Function and macro names are identical, except for special cases - 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. 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; run;
``` ```
- Prefixes: _ml_ - Prefixes: `ml_`
### META folder (SAS9 only) ### META folder (SAS9 only)
@@ -81,14 +81,14 @@ Macros used in SAS EBI, which connect to the metadata server.
- OS independent - OS independent
- Metadata aware - Metadata aware
- No X command - No X command
- Prefixes: _mm_ - Prefixes: `mm_`
### METAX folder (SAS9 only) ### METAX folder (SAS9 only)
- OS specific - OS specific
- Metadata aware - Metadata aware
- X command enabled - X command enabled
- Prefixes: _mmw_,_mmu_,_mmx_ - Prefixes: `mmx_`
### SERVER folder (@sasjs/server only) ### 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. 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 - OS independent
- @sasjs/server aware - @sasjs/server aware
- No X command - No X command
- Prefixes: _ms_ - Prefixes: `ms_`
### VIYA folder (Viya only) ### VIYA folder (Viya only)
@@ -104,7 +104,7 @@ Macros used for interfacing with SAS Viya.
- OS independent - OS independent
- No X command - No X command
- Prefixes: _mv_, _mvf_ - Prefixes: `mv_`, `mvf_`
### XPLATFORM folder (Viya, Meta, and Server) ### 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 - OS independent
- No X command - No X command
- Prefixes: _mx_ - Prefixes: `mx_`
## Installation ## Installation
@@ -237,6 +237,7 @@ If you find this library useful, please leave a [star](https://github.com/sasjs/
The following repositories are also worth checking out: The following repositories are also worth checking out:
* [SASJedi/sas-macros](https://github.com/SASJedi/sas-macros)
* [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros) * [chris-swenson/sasmacros](https://github.com/chris-swenson/sasmacros)
* [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs) * [greg-wotton/sas-programs](https://github.com/greg-wootton/sas-programs)
* [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros) * [KatjaGlassConsulting/SMILE-SmartSASMacros](https://github.com/KatjaGlassConsulting/SMILE-SmartSASMacros)

1120
all.sas

File diff suppressed because it is too large Load Diff

29
base/mf_increment.sas Normal file
View File

@@ -0,0 +1,29 @@
/**
@file
@brief Increments a macro variable
@details Useful outside of do-loops - will increment a macro variable every
time it is called.
Example:
%let cnt=1;
%put We have run %mf_increment(cnt) lines;
%put Now we have run %mf_increment(cnt) lines;
%put There are %mf_increment(cnt) lines in total;
@param [in] MACRO_NAME the name of the macro variable to increment
@param [in] ITER= The amount to add or subtract to the macro
<h4> Related Files </h4>
@li mf_increment.test.sas
**/
%macro mf_increment(macro_name,incr=1);
/* iterate the value */
%let &macro_name=%eval(&&&macro_name+&incr);
/* return the value */
&&&macro_name
%mend mf_increment;

View File

@@ -31,6 +31,7 @@
@param [in] mode (CREATE) Valid values: @param [in] mode (CREATE) Valid values:
@li CREATE - Create the file (even if it already exists) @li CREATE - Create the file (even if it already exists)
@li APPEND - Append to the file (don't overwrite) @li APPEND - Append to the file (don't overwrite)
@param iftrue= (1=1) Supply a condition for which the macro should be executed
@returns nothing @returns nothing
@@ -44,8 +45,12 @@
,inref=____in /* override default to use own filerefs */ ,inref=____in /* override default to use own filerefs */
,outref=____out /* override default to use own filerefs */ ,outref=____out /* override default to use own filerefs */
,mode=CREATE ,mode=CREATE
,iftrue=%str(1=1)
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local mod; %local mod;
%if not(%eval(%unquote(&iftrue))) %then %return;
%if &mode=APPEND %then %let mod=mod; %if &mode=APPEND %then %let mod=mod;
/* these IN and OUT filerefs can point to anything */ /* these IN and OUT filerefs can point to anything */

View File

@@ -1,5 +1,5 @@
/** /**
@file mp_cleancsv.sas @file
@brief Fixes embedded cr / lf / crlf in CSV @brief Fixes embedded cr / lf / crlf in CSV
@details CSVs will sometimes contain lf or crlf within quotes (eg when @details CSVs will sometimes contain lf or crlf within quotes (eg when
saved by excel). When the termstr is ALSO lf or crlf that can be tricky saved by excel). When the termstr is ALSO lf or crlf that can be tricky
@@ -7,14 +7,16 @@
This macro converts any csv to follow the convention of a windows excel file, This macro converts any csv to follow the convention of a windows excel file,
applying CRLF line endings and converting embedded cr and crlf to lf. applying CRLF line endings and converting embedded cr and crlf to lf.
usage: Usage:
fileref mycsv "/path/your/csv"; fileref mycsv "/path/your/csv";
%mp_cleancsv(in=mycsv,out=/path/new.csv) %mp_cleancsv(in=mycsv,out=/path/new.csv)
@param in= provide path or fileref to input csv @param in= (NOTPROVIDED) Provide path or fileref to input csv. If a period is
@param out= output path or fileref to output csv found, it is assumed to be a file.
@param qchar= quote char - hex code 22 is the double quote. @param out= (NOTPROVIDED) Output path or fileref to output csv. If a period
is found, it is assumed to be a file.
@param qchar= ('22'x) Quote char - hex code 22 is the double quote.
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@@ -56,9 +58,14 @@
else do; else do;
/* outside a quote, change cr and lf to crlf */ /* outside a quote, change cr and lf to crlf */
if inchar='0D'x then do; if inchar='0D'x then do;
crblank:
put '0D0A'x; put '0D0A'x;
input inchar $char1.; input inchar $char1.;
if inchar ne '0A'x then do; if inchar='0D'x then do;
/* multiple CR indicates CR formatted file with blank lines */
goto crblank;
end;
else if inchar ne '0A'x then do;
put inchar $char1.; put inchar $char1.;
if inchar=qchar then isq = mod(isq+1,2); if inchar=qchar then isq = mod(isq+1,2);
end; end;

View File

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

View File

@@ -17,7 +17,7 @@
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_existds.sas @li mf_existds.sas
<h4> Related Macros <h4> <h4> Related Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@version 9.2 @version 9.2

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

@@ -92,7 +92,8 @@ data &outds;
/*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32 /*length GROUP_LOGIC SUBGROUP_LOGIC $3 SUBGROUP_ID 8 VARIABLE_NM $32
OPERATOR_NM $10 RAW_VALUE $4000;*/ OPERATOR_NM $10 RAW_VALUE $4000;*/
set &inds; set &inds;
length reason_cd $4032 vtype $1 vnum dsid 8; length reason_cd $4032 vtype $1 vnum dsid 8 tmp $4000;
drop tmp;
/* quick check to ensure column exists */ /* quick check to ensure column exists */
if upcase(VARIABLE_NM) not in if upcase(VARIABLE_NM) not in
@@ -168,18 +169,32 @@ data &outds;
end; end;
/* special logic */ /* special logic */
if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ',''); if OPERATOR_NM in ('IN','NOT IN','BETWEEN') then do;
else if OPERATOR_NM in ('IN','NOT IN') then do; if OPERATOR_NM='BETWEEN' then raw_value1=tranwrd(raw_value,' AND ',',');
if substr(raw_value,1,1) ne '(' else do;
or substr(cats(reverse(raw_value)),1,1) ne ')' if substr(raw_value,1,1) ne '('
then do; or substr(cats(reverse(raw_value)),1,1) ne ')'
REASON_CD='Missing start/end bracket in RAW_VALUE'; then do;
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ; REASON_CD='Missing start/end bracket in RAW_VALUE';
call symputx('reason_cd',reason_cd,'l'); putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
call symputx('nobs',_n_,'l'); call symputx('reason_cd',reason_cd,'l');
output; call symputx('nobs',_n_,'l');
output;
end;
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
end;
/* we now have a comma seperated list of values */
if vtype='N' then do i=1 to countc(raw_value1, ',')+1;
tmp=scan(raw_value1,i,',');
if cats(tmp) ne '.' and input(tmp, ?? 8.) eq . then do;
REASON_CD='Non Numeric value provided';
putlog REASON_CD= OPERATOR_NM= raw_value= raw_value1= ;
call symputx('reason_cd',reason_cd,'l');
call symputx('nobs',_n_,'l');
output;
end;
return;
end; end;
else raw_value1=substr(raw_value,2,max(length(raw_value)-2,0));
end; end;
else raw_value1=raw_value; else raw_value1=raw_value;

View File

@@ -94,8 +94,11 @@ create table &outds as
/** /**
* We cannot apply this clause to the underlying dictionary table. See: * 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 * https://communities.sas.com/t5/SAS-Programming/Unexpected-Where-Clause-behaviour-in-dictionary-TABLE/m-p/771554#M244867
* cannot use`where calculated libref="&lib"` either as it will STILL execute
* all the underlying constraint queries, causing exception errors in some
* cases: https://github.com/sasjs/core/issues/283
*/ */
where calculated libref="&lib" where a.TABLE_CATALOG="&lib"
%if "&ds" ne "" %then %do; %if "&ds" ne "" %then %do;
and upcase(a.TABLE_NAME)="&ds" and upcase(a.TABLE_NAME)="&ds"
and upcase(b.TABLE_NAME)="&ds" and upcase(b.TABLE_NAME)="&ds"

View File

@@ -130,13 +130,13 @@ run;
%local x curds; %local x curds;
%if &flavour=SAS %then %do; %if &flavour=SAS %then %do;
data _null_;
file &fref mod;
put "/* SAS Flavour DDL for %upcase(&libref).&curds */";
put "proc sql;";
run;
%do x=1 %to %sysfunc(countw(&dsnlist)); %do x=1 %to %sysfunc(countw(&dsnlist));
%let curds=%scan(&dsnlist,&x); %let curds=%scan(&dsnlist,&x);
data _null_;
file &fref mod;
put "/* SAS Flavour DDL for %upcase(&libref).&curds */";
put "proc sql;";
run;
data _null_; data _null_;
file &fref mod; file &fref mod;
length lab $1024 typ $20; length lab $1024 typ $20;

View File

@@ -62,7 +62,7 @@
@param [in] maxobs= (MAX) Provide an integer to limit the number of input rows @param [in] maxobs= (MAX) Provide an integer to limit the number of input rows
that should be converted to JSON that should be converted to JSON
<h4> Related Macros <h4> <h4> Related Files </h4>
@li mp_ds2fmtds.sas @li mp_ds2fmtds.sas
@version 9.2 @version 9.2
@@ -70,14 +70,14 @@
@source https://github.com/sasjs/core @source https://github.com/sasjs/core
**/ **/
%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y %macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y
,engine=DATASTEP ,engine=DATASTEP
,missing=NULL ,missing=NULL
,showmeta=N ,showmeta=N
,maxobs=MAX ,maxobs=MAX
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local tempds colinfo fmtds i numcols stmt_obs; %local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval
tmpds1 tmpds2 tmpds3 tmpds4;
%let numcols=0; %let numcols=0;
%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); %if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;);
@@ -115,7 +115,7 @@
by varnum; by varnum;
run; run;
/* move meta to mac vars */ /* move meta to mac vars */
data _null_; data &colinfo;
if _n_=1 then call symputx('numcols',nobs,'l'); if _n_=1 then call symputx('numcols',nobs,'l');
set &colinfo end=last nobs=nobs; set &colinfo end=last nobs=nobs;
name=upcase(name); name=upcase(name);
@@ -126,7 +126,6 @@
if format='' then fmt=cats('$',length,'.'); if format='' then fmt=cats('$',length,'.');
else if formatl=0 then fmt=cats(format,'.'); else if formatl=0 then fmt=cats(format,'.');
else fmt=cats(format,formatl,'.'); else fmt=cats(format,formatl,'.');
newlen=max(formatl,length);
end; end;
else do; else do;
typelong='num'; typelong='num';
@@ -134,23 +133,26 @@
else if formatl=0 then fmt=cats(format,'.'); else if formatl=0 then fmt=cats(format,'.');
else if formatd=0 then fmt=cats(format,formatl,'.'); else if formatd=0 then fmt=cats(format,formatl,'.');
else fmt=cats(format,formatl,'.',formatd); else fmt=cats(format,formatl,'.',formatd);
/* needs to be wide, for datetimes etc */
newlen=max(length,formatl,24);
end; end;
/* 32 char unique name */ /* 32 char unique name */
newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27); newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27);
call symputx(cats('name',_n_),name,'l'); call symputx(cats('name',_n_),name,'l');
call symputx(cats('newname',_n_),newname,'l'); call symputx(cats('newname',_n_),newname,'l');
call symputx(cats('len',_n_),newlen,'l');
call symputx(cats('length',_n_),length,'l'); call symputx(cats('length',_n_),length,'l');
call symputx(cats('fmt',_n_),fmt,'l'); call symputx(cats('fmt',_n_),fmt,'l');
call symputx(cats('type',_n_),type,'l'); call symputx(cats('type',_n_),type,'l');
call symputx(cats('typelong',_n_),typelong,'l'); call symputx(cats('typelong',_n_),typelong,'l');
call symputx(cats('label',_n_),coalescec(label,name),'l'); 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+10)*1.5)),'l');
run; run;
%let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
proc sql;
select count(*) into: lastobs from &ds;
%if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs));
%if &engine=PROCJSON %then %do; %if &engine=PROCJSON %then %do;
%if &missing=STRING %then %do; %if &missing=STRING %then %do;
@@ -187,26 +189,99 @@
%end; %end;
%if &fmt=Y %then %do; %if &fmt=Y %then %do;
data _data_; /**
* Extract format definitions
* First, by getting library locations from dictionary.formats
* Then, by exporting the width using proc format
* Cannot use maxw from sashelp.vformat as not always populated
* Cannot use fmtinfo() as not supported in all flavours
*/
%let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
%let tmpds4=%substr(col%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32);
proc sql noprint;
create table &tmpds1 as
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),
LENGTH num
);
%local catlist cat fmtlist i;
select distinct fmtcat into: catlist separated by ' ' from &tmpds1;
%do i=1 %to %sysfunc(countw(&catlist,%str( )));
%let cat=%scan(&catlist,&i,%str( ));
proc sql;
select distinct fmtname into: fmtlist separated by ' '
from &tmpds1 where fmtcat="&cat";
proc format lib=&cat cntlout=&tmpds3(keep=fmtname length);
select &fmtlist;
run;
proc sql;
insert into &tmpds2 select distinct fmtname,length from &tmpds3;
%end;
proc sql;
create table &tmpds4 as
select a.*, b.length as MAXW
from &colinfo a
left join &tmpds2 b
on cats(a.format)=cats(upcase(b.fmtname))
order by a.varnum;
data _null_;
set &tmpds4;
if not missing(maxw);
call symputx(
cats('fmtlen',_n_),
/* vars need extra padding due to JSON escaping of special chars */
min(32767,ceil((max(length,maxw)+10)*1.5))
,'l'
);
run;
/* configure varlenchk - as we are explicitly shortening the variables */
%let optval=%sysfunc(getoption(varlenchk));
options varlenchk=NOWARN;
data _data_(compress=char);
/* shorten the new vars */
length
%do i=1 %to &numcols;
&&name&i $&&fmtlen&i
%end;
;
/* rename on entry */ /* rename on entry */
set &ds(rename=( set &ds(rename=(
%do i=1 %to &numcols; %do i=1 %to &numcols;
&&name&i=&&newname&i &&name&i=&&newname&i
%end; %end;
)); ));
&stmt_obs;
drop
%do i=1 %to &numcols;
&&newname&i
%end;
;
%do i=1 %to &numcols; %do i=1 %to &numcols;
length &&name&i $&&len&i;
%if &&typelong&i=num %then %do; %if &&typelong&i=num %then %do;
&&name&i=left(put(&&newname&i,&&fmt&i)); &&name&i=cats(put(&&newname&i,&&fmt&i));
%end; %end;
%else %do; %else %do;
&&name&i=put(&&newname&i,&&fmt&i); &&name&i=put(&&newname&i,&&fmt&i);
%end; %end;
drop &&newname&i;
%end; %end;
if _error_ then call symputx('syscc',1012); if _error_ then do;
call symputx('syscc',1012);
stop;
end;
run; run;
%let fmtds=&syslast; %let fmtds=&syslast;
options varlenchk=&optval;
%end; %end;
proc format; /* credit yabwon for special null removal */ proc format; /* credit yabwon for special null removal */
@@ -225,8 +300,8 @@
attrib _all_ label=''; attrib _all_ label='';
%do i=1 %to &numcols; %do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do; %if &&typelong&i=char or &fmt=Y %then %do;
length &&name&i $32767; length &&name&i $&&fmtlen&i...;
format &&name&i $32767.; format &&name&i $&&fmtlen&i...;
%end; %end;
%end; %end;
%if &fmt=Y %then %do; %if &fmt=Y %then %do;
@@ -239,7 +314,7 @@
format _numeric_ bart.; format _numeric_ bart.;
%do i=1 %to &numcols; %do i=1 %to &numcols;
%if &&typelong&i=char or &fmt=Y %then %do; %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( &&name&i='"'!!trim(
prxchange('s/"/\\"/',-1, /* double quote */ prxchange('s/"/\\"/',-1, /* double quote */
prxchange('s/\x0A/\n/',-1, /* new line */ prxchange('s/\x0A/\n/',-1, /* new line */
@@ -252,8 +327,9 @@
prxchange('s/\x02/\\u0002/',-1, /* STX */ prxchange('s/\x02/\\u0002/',-1, /* STX */
prxchange('s/\x10/\\u0010/',-1, /* DLE */ prxchange('s/\x10/\\u0010/',-1, /* DLE */
prxchange('s/\x11/\\u0011/',-1, /* DC1 */ prxchange('s/\x11/\\u0011/',-1, /* DC1 */
prxchange('s/\x1A/\\u001A/',-1, /* SUB */
prxchange('s/\\/\\\\/',-1,&&name&i) prxchange('s/\\/\\\\/',-1,&&name&i)
))))))))))))!!'"'; )))))))))))))!!'"';
end; end;
else &&name&i=quote(cats(&&name&i)); else &&name&i=quote(cats(&&name&i));
%end; %end;

View File

@@ -1,13 +1,14 @@
/** /**
@file @file
@brief Logs the time the macro was executed in a control dataset. @brief Logs a message in a dataset every time it is invoked
@details If the dataset does not exist, it is created. Usage: @details If the dataset does not exist, it is created.
Usage:
%mp_perflog(started) %mp_perflog(started)
%mp_perflog() %mp_perflog()
%mp_perflog(startanew,libds=work.newdataset) %mp_perflog(startanew,libds=work.newdataset)
%mp_perflog(finished,libds=work.newdataset) %mp_perflog(finished,libds=work.newdataset)
%mp_perflog(finished) %mp_perflog(finished)
@param label Provide label to go into the control dataset @param label Provide label to go into the control dataset

View File

@@ -154,7 +154,9 @@ run;
%else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE); %else %let vlist=%mf_getvarlist(&libds,dlm=%str(,),quote=DOUBLE);
data &ds4; data &ds4;
length &inds_keep $41 tgtvar_nm $32; length &inds_keep $41 tgtvar_nm $32 _label_ $256;
if _n_=1 then call missing(_label_);
drop _label_;
set &ds2 &ds3 indsname=&inds_auto; set &ds2 &ds3 indsname=&inds_auto;
tgtvar_nm=upcase(tgtvar_nm); tgtvar_nm=upcase(tgtvar_nm);

View File

@@ -93,14 +93,14 @@ data _null_;
file sasjs lrecl=3000 ; file sasjs lrecl=3000 ;
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
/* WEBOUT BEGIN */ /* WEBOUT BEGIN */
put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP '; put ' ,engine=DATASTEP ';
put ' ,missing=NULL '; put ' ,missing=NULL ';
put ' ,showmeta=N '; put ' ,showmeta=N ';
put ' ,maxobs=MAX '; put ' ,maxobs=MAX ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols stmt_obs; '; put '%local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval ';
put ' tmpds1 tmpds2 tmpds3 tmpds4; ';
put '%let numcols=0; '; put '%let numcols=0; ';
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
put ' '; put ' ';
@@ -138,7 +138,7 @@ data _null_;
put ' by varnum; '; put ' by varnum; ';
put ' run; '; put ' run; ';
put ' /* move meta to mac vars */ '; put ' /* move meta to mac vars */ ';
put ' data _null_; '; put ' data &colinfo; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; '; put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); '; put ' name=upcase(name); ';
@@ -149,7 +149,6 @@ data _null_;
put ' if format='''' then fmt=cats(''$'',length,''.''); '; put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); '; put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); '; put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; '; put ' end; ';
put ' else do; '; put ' else do; ';
put ' typelong=''num''; '; put ' typelong=''num''; ';
@@ -157,23 +156,26 @@ data _null_;
put ' else if formatl=0 then fmt=cats(format,''.''); '; put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); '; put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); '; put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; '; put ' end; ';
put ' /* 32 char unique name */ '; put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' '; put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; 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+10)*1.5)),''l''); ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql; ';
put ' select count(*) into: lastobs from &ds; ';
put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); ';
put ' '; put ' ';
put ' %if &engine=PROCJSON %then %do; '; put ' %if &engine=PROCJSON %then %do; ';
put ' %if &missing=STRING %then %do; '; put ' %if &missing=STRING %then %do; ';
@@ -210,26 +212,99 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
put ' data _data_; '; put ' /** ';
put ' * Extract format definitions ';
put ' * First, by getting library locations from dictionary.formats ';
put ' * Then, by exporting the width using proc format ';
put ' * Cannot use maxw from sashelp.vformat as not always populated ';
put ' * Cannot use fmtinfo() as not supported in all flavours ';
put ' */ ';
put ' %let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
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 ' 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 ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
put ' %do i=1 %to %sysfunc(countw(&catlist,%str( ))); ';
put ' %let cat=%scan(&catlist,&i,%str( )); ';
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 length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
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.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
put ' order by a.varnum; ';
put ' data _null_; ';
put ' set &tmpds4; ';
put ' if not missing(maxw); ';
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)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data _data_(compress=char); ';
put ' /* shorten the new vars */ ';
put ' length ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i $&&fmtlen&i ';
put ' %end; ';
put ' ; ';
put ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' &stmt_obs; ';
put ' ';
put ' drop ';
put ' %do i=1 %to &numcols; ';
put ' &&newname&i ';
put ' %end; ';
put ' ; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
put ' %if &&typelong&i=num %then %do; '; put ' %if &&typelong&i=num %then %do; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); ';
put ' %end; '; put ' %end; ';
put ' %else %do; '; put ' %else %do; ';
put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' &&name&i=put(&&newname&i,&&fmt&i); ';
put ' %end; '; put ' %end; ';
put ' drop &&newname&i; ';
put ' %end; '; put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); '; put ' if _error_ then do; ';
put ' call symputx(''syscc'',1012); ';
put ' stop; ';
put ' end; ';
put ' run; '; put ' run; ';
put ' %let fmtds=&syslast; '; put ' %let fmtds=&syslast; ';
put ' options varlenchk=&optval; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
@@ -248,8 +323,8 @@ data _null_;
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; '; put ' length &&name&i $&&fmtlen&i...; ';
put ' format &&name&i $32767.; '; put ' format &&name&i $&&fmtlen&i...; ';
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
@@ -262,7 +337,7 @@ data _null_;
put ' format _numeric_ bart.; '; put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; 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 ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ '; put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ '; put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -275,8 +350,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ '; put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ '; put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ '; put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) '; put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); '; put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; '; put ' %end; ';
@@ -371,8 +447,8 @@ data _null_;
put ' %quote(&user) '; put ' %quote(&user) ';
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; put '%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N '; put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); '; put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug '; put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; '; put ' sasjs_tables; ';
@@ -439,14 +515,14 @@ data _null_;
put ' '; put ' ';
put '%else %if &action=ARR or &action=OBJ %then %do; '; put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref '; put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
put ' ,engine=&jsonengine,missing=&missing,showmeta=&showmeta '; put ' ,engine=&jsonengine,missing=&missing,showmeta=&showmeta,maxobs=&maxobs ';
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' /* To avoid issues with _webout on EBI we use a temporary file */ '; put ' /* To avoid issues with _webout on EBI we use a temporary file */ ';
put ' filename _sjsref temp lrecl=131068; '; put ' filename _sjsref temp lrecl=131068; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ '; put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); '; put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; '; put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; '; put ' proc datasets library=WORK memtype=data; ';
@@ -470,7 +546,9 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&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 ' data _null_; file _sjsref mod encoding=''utf-8''; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';
@@ -480,23 +558,30 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' /* close off json */ '; put ' /* close off json */ ';
put ' data _null_;file _sjsref mod encoding=''utf-8''; '; put ' data _null_;file _sjsref mod encoding=''utf-8''; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; put ' length SYSPROCESSNAME syserrortext syswarningtext autoexec $512; ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
put ' put ",""_DEBUG"" : ""&_debug"" "; '; put ' put ",""_DEBUG"" : ""&_debug"" "; ';
put ' _METAUSER=quote(trim(symget(''_METAUSER''))); '; put ' _METAUSER=quote(trim(symget(''_METAUSER''))); ';
put ' put ",""_METAUSER"": " _METAUSER; '; put ' put ",""_METAUSER"": " _METAUSER; ';
put ' _METAPERSON=quote(trim(symget(''_METAPERSON''))); '; put ' _METAPERSON=quote(trim(symget(''_METAPERSON''))); ';
put ' put '',"_METAPERSON": '' _METAPERSON; '; put ' put '',"_METAPERSON": '' _METAPERSON; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
put ' put '',"_PROGRAM" : '' _PROGRAM ; '; put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); ';
put ' put '',"AUTOEXEC" : '' autoexec; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
put ' put ",""SYSCC"" : ""&syscc"" "; '; put ' put ",""SYSCC"" : ""&syscc"" "; ';
put ' put ",""SYSENCODING"" : ""&sysencoding"" "; '; put ' put ",""SYSENCODING"" : ""&sysencoding"" "; ';
put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); '; put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; ';
put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); ';
put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; ';
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; '; put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; '; put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
put ' put ",""SYSSITE"" : ""&syssite"" "; '; put ' put ",""SYSSITE"" : ""&syssite"" "; ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' sysvlong=quote(trim(symget(''sysvlong''))); '; put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
put ' put '',"SYSVLONG" : '' sysvlong; '; put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); '; put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); ';

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

@@ -26,7 +26,7 @@
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@param [in] ds The dataset to send back to the frontend @param [in] ds The dataset to send back to the frontend
@param [out] dslabel= Value to use instead of table name for sending to JSON @param [out] dslabel= Value to use instead of table name for sending to JSON
@param [in] fmt=(Y) Set to N to send back unformatted values @param [in] fmt= (N) Setting Y converts all vars to their formatted values
@param [out] fref= (_webout) The fileref to which to write the JSON @param [out] fref= (_webout) The fileref to which to write the JSON
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`) (eg `null`) or as STRING values (eg `".a"` or `".b"`)
@@ -34,17 +34,25 @@
such as the column formats and types. The metadata is contained inside an 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, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$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
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
<h4> Related Macros </h4>
@li ms_webout.sas
@li mv_webout.sas
@version 9.3 @version 9.3
@author Allan Bowe @author Allan Bowe
**/ **/
%macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL %macro mm_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N ,showmeta=N,maxobs=MAX,workobs=0
); );
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables; sasjs_tables;
@@ -111,14 +119,14 @@
%else %if &action=ARR or &action=OBJ %then %do; %else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,engine=&jsonengine,missing=&missing,showmeta=&showmeta ,engine=&jsonengine,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
/* To avoid issues with _webout on EBI we use a temporary file */ /* To avoid issues with _webout on EBI we use a temporary file */
filename _sjsref temp lrecl=131068; filename _sjsref temp lrecl=131068;
%if %str(&_debug) ge 131 %then %do; %if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first 10 records of each work table also */ /* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.); data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds; ods output Members=&tempds;
proc datasets library=WORK memtype=data; proc datasets library=WORK memtype=data;
@@ -142,7 +150,9 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; 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'; data _null_; file _sjsref mod encoding='utf-8';
put "}"; put "}";
%end; %end;
@@ -152,23 +162,30 @@
%end; %end;
/* close off json */ /* close off json */
data _null_;file _sjsref mod encoding='utf-8'; data _null_;file _sjsref mod encoding='utf-8';
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
put ",""SYSUSERID"" : ""&sysuserid"" ";
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
put ",""_DEBUG"" : ""&_debug"" "; put ",""_DEBUG"" : ""&_debug"" ";
_METAUSER=quote(trim(symget('_METAUSER'))); _METAUSER=quote(trim(symget('_METAUSER')));
put ",""_METAUSER"": " _METAUSER; put ",""_METAUSER"": " _METAUSER;
_METAPERSON=quote(trim(symget('_METAPERSON'))); _METAPERSON=quote(trim(symget('_METAPERSON')));
put ',"_METAPERSON": ' _METAPERSON; put ',"_METAPERSON": ' _METAPERSON;
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ',"_PROGRAM" : ' _PROGRAM ; put ',"_PROGRAM" : ' _PROGRAM ;
autoexec=quote(urlencode(trim(getoption('autoexec'))));
put ',"AUTOEXEC" : ' autoexec;
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
put ",""SYSCC"" : ""&syscc"" "; put ",""SYSCC"" : ""&syscc"" ";
put ",""SYSENCODING"" : ""&sysencoding"" "; put ",""SYSENCODING"" : ""&sysencoding"" ";
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"'); syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSSCPL"" : ""&sysscpl"" "; put ",""SYSSCPL"" : ""&sysscpl"" ";
put ",""SYSSITE"" : ""&syssite"" "; put ",""SYSSITE"" : ""&syssite"" ";
put ",""SYSUSERID"" : ""&sysuserid"" ";
sysvlong=quote(trim(symget('sysvlong'))); sysvlong=quote(trim(symget('sysvlong')));
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"'); syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');

View File

@@ -0,0 +1,50 @@
/**
@file
@brief Creates a metadata folder
@details Creates a metadata folder using the batch tools
Usage:
%mmx_createmetafolder(loc=/some/meta/folder,user=sasdemo,pass=mars345)
<h4> SAS Macros </h4>
@li mf_loc.sas
@li mp_abort.sas
@param loc= the metadata folder to delete
@param user= username
@param pass= password
@version 9.4
@author Allan Bowe
**/
%macro mmx_createmetafolder(loc=,user=,pass=);
%local host port path connx_string msg;
%let host=%sysfunc(getoption(metaserver));
%let port=%sysfunc(getoption(metaport));
%let path=%mf_loc(POF)/tools;
%let connx_string= -host &host -port &port -user '&user' -password '&pass';
/* remove directory */
data _null_;
infile " &path/sas-make-folder &connx_string ""&loc"" -makeFullPath 2>&1"
pipe lrecl=10000;
input;
putlog _infile_;
run;
data _null_; /* check tree exists */
length type uri $256;
rc=metadata_pathobj("","&loc","Folder",type,uri);
call symputx('foldertype',type,'l');
run;
%let msg=Location (&loc) was not created!!;
%mp_abort(iftrue= (&foldertype ne Tree)
,mac=&_program..sas
,msg=%superq(msg)
)
%mend mmx_createmetafolder;

View File

@@ -7,7 +7,7 @@
Usage: Usage:
%mmx_deletemetafolder(loc=/some/meta/folder,user=sasdemo,pass=mars345) %mmx_deletemetafolder(loc=/some/meta/folder,user=sasdemo,pass=mars345)
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_loc.sas @li mf_loc.sas
@@ -37,4 +37,4 @@ data _null_;
putlog _infile_; putlog _infile_;
run; run;
%mend mmx_deletemetafolder; %mend mmx_deletemetafolder;

View File

@@ -25,6 +25,21 @@
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" /> <link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
<link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="$relpath^favicon.ico" type="image/x-icon" />
$extrastylesheet $extrastylesheet
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = "https://analytics.4gl.io/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '6']);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true; g.src = u + 'matomo.js'; s.parentNode.insertBefore(g, s);
})();
</script>
<!-- End Matomo Code -->
</head> </head>
<body> <body>
@@ -68,4 +83,4 @@
</div> </div>
</body> </body>
</html> </html>

View File

@@ -67,7 +67,7 @@
}, },
{ {
"name": "server", "name": "server",
"serverUrl": "", "serverUrl": "https://sas.4gl.io",
"serverType": "SASJS", "serverType": "SASJS",
"httpsAgentOptions": { "httpsAgentOptions": {
"allowInsecureRequests": false "allowInsecureRequests": false

View File

@@ -94,14 +94,14 @@ data _null_;
file &sasjsref termstr=crlf lrecl=512; file &sasjsref termstr=crlf lrecl=512;
put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */";
/* WEBOUT BEGIN */ /* WEBOUT BEGIN */
put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP '; put ' ,engine=DATASTEP ';
put ' ,missing=NULL '; put ' ,missing=NULL ';
put ' ,showmeta=N '; put ' ,showmeta=N ';
put ' ,maxobs=MAX '; put ' ,maxobs=MAX ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols stmt_obs; '; put '%local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval ';
put ' tmpds1 tmpds2 tmpds3 tmpds4; ';
put '%let numcols=0; '; put '%let numcols=0; ';
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
put ' '; put ' ';
@@ -139,7 +139,7 @@ data _null_;
put ' by varnum; '; put ' by varnum; ';
put ' run; '; put ' run; ';
put ' /* move meta to mac vars */ '; put ' /* move meta to mac vars */ ';
put ' data _null_; '; put ' data &colinfo; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; '; put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); '; put ' name=upcase(name); ';
@@ -150,7 +150,6 @@ data _null_;
put ' if format='''' then fmt=cats(''$'',length,''.''); '; put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); '; put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); '; put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; '; put ' end; ';
put ' else do; '; put ' else do; ';
put ' typelong=''num''; '; put ' typelong=''num''; ';
@@ -158,23 +157,26 @@ data _null_;
put ' else if formatl=0 then fmt=cats(format,''.''); '; put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); '; put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); '; put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; '; put ' end; ';
put ' /* 32 char unique name */ '; put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' '; put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; 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+10)*1.5)),''l''); ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql; ';
put ' select count(*) into: lastobs from &ds; ';
put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); ';
put ' '; put ' ';
put ' %if &engine=PROCJSON %then %do; '; put ' %if &engine=PROCJSON %then %do; ';
put ' %if &missing=STRING %then %do; '; put ' %if &missing=STRING %then %do; ';
@@ -211,26 +213,99 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
put ' data _data_; '; put ' /** ';
put ' * Extract format definitions ';
put ' * First, by getting library locations from dictionary.formats ';
put ' * Then, by exporting the width using proc format ';
put ' * Cannot use maxw from sashelp.vformat as not always populated ';
put ' * Cannot use fmtinfo() as not supported in all flavours ';
put ' */ ';
put ' %let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
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 ' 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 ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
put ' %do i=1 %to %sysfunc(countw(&catlist,%str( ))); ';
put ' %let cat=%scan(&catlist,&i,%str( )); ';
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 length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
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.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
put ' order by a.varnum; ';
put ' data _null_; ';
put ' set &tmpds4; ';
put ' if not missing(maxw); ';
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)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data _data_(compress=char); ';
put ' /* shorten the new vars */ ';
put ' length ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i $&&fmtlen&i ';
put ' %end; ';
put ' ; ';
put ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' &stmt_obs; ';
put ' ';
put ' drop ';
put ' %do i=1 %to &numcols; ';
put ' &&newname&i ';
put ' %end; ';
put ' ; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
put ' %if &&typelong&i=num %then %do; '; put ' %if &&typelong&i=num %then %do; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); ';
put ' %end; '; put ' %end; ';
put ' %else %do; '; put ' %else %do; ';
put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' &&name&i=put(&&newname&i,&&fmt&i); ';
put ' %end; '; put ' %end; ';
put ' drop &&newname&i; ';
put ' %end; '; put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); '; put ' if _error_ then do; ';
put ' call symputx(''syscc'',1012); ';
put ' stop; ';
put ' end; ';
put ' run; '; put ' run; ';
put ' %let fmtds=&syslast; '; put ' %let fmtds=&syslast; ';
put ' options varlenchk=&optval; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
@@ -249,8 +324,8 @@ data _null_;
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; '; put ' length &&name&i $&&fmtlen&i...; ';
put ' format &&name&i $32767.; '; put ' format &&name&i $&&fmtlen&i...; ';
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
@@ -263,7 +338,7 @@ data _null_;
put ' format _numeric_ bart.; '; put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; 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 ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ '; put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ '; put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -276,8 +351,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ '; put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ '; put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ '; put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) '; put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); '; put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; '; put ' %end; ';
@@ -373,8 +449,8 @@ data _null_;
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
put ' '; put ' ';
put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL '; put '%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL ';
put ' ,showmeta=N '; put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); '; put '); ';
put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug '; put '%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug ';
put ' sasjs_tables; '; put ' sasjs_tables; ';
@@ -433,12 +509,12 @@ data _null_;
put ' %let missing=NULL; '; put ' %let missing=NULL; ';
put ' %end; '; put ' %end; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref '; put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta '; put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs ';
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* if debug mode, send back first 10 records of each work table also */ '; put ' /* if debug mode, send back first XX records of each work table also */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); '; put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; '; put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; '; put ' proc datasets library=WORK memtype=data; ';
@@ -463,7 +539,9 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&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 ' data _null_; file &fref mod encoding=''utf-8'' termstr=lf; ';
put ' put "}"; '; put ' put "}"; ';
put ' %end; '; put ' %end; ';
@@ -473,11 +551,13 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' /* close off json */ '; put ' /* close off json */ ';
put ' data _null_;file &fref mod encoding=''utf-8'' termstr=lf lrecl=32767; '; put ' data _null_;file &fref mod encoding=''utf-8'' termstr=lf lrecl=32767; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; put ' length SYSPROCESSNAME syserrortext syswarningtext autoexec $512; ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
put ' put ",""_DEBUG"" : ""&_debug"" "; '; put ' put ",""_DEBUG"" : ""&_debug"" "; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
put ' put '',"_PROGRAM" : '' _PROGRAM ; '; put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); ';
put ' put '',"AUTOEXEC" : '' autoexec; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
put ' put ",""SYSCC"" : ""&syscc"" "; '; put ' put ",""SYSCC"" : ""&syscc"" "; ';
put ' put ",""SYSENCODING"" : ""&sysencoding"" "; '; put ' put ",""SYSENCODING"" : ""&sysencoding"" "; ';
put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); '; put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); ';
@@ -487,21 +567,18 @@ data _null_;
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; '; put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; '; put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; ';
put ' length SYSPROCESSNAME $512; ';
put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); '; put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); ';
put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; '; put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; ';
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; '; put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; '; put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
put ' put ",""SYSSITE"" : ""&syssite"" "; '; put ' put ",""SYSSITE"" : ""&syssite"" "; ';
put ' put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; '; put ' put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' sysvlong=quote(trim(symget(''sysvlong''))); '; put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
put ' put '',"SYSVLONG" : '' sysvlong; '; put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); '; put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); ';
put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; '; put ' put '',"SYSWARNINGTEXT" : '' syswarningtext; ';
put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; '; put ' put '',"END_DTTM" : "'' "%sysfunc(datetime(),E8601DT26.6)" ''" ''; ';
put ' length autoexec $512; ';
put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); ';
put ' put '',"AUTOEXEC" : '' autoexec; ';
put ' length memsize $32; '; put ' length memsize $32; ';
put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; '; put ' memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; ';
put ' memsize=quote(cats(memsize)); '; put ' memsize=quote(cats(memsize)); ';

View File

@@ -23,7 +23,7 @@
@param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE @param [in] action Either FETCH, OPEN, ARR, OBJ or CLOSE
@param [in] ds The dataset to send back to the frontend @param [in] ds The dataset to send back to the frontend
@param [out] dslabel= value to use instead of table name for sending to JSON @param [out] dslabel= value to use instead of table name for sending to JSON
@param [in] fmt= (Y) Set to N to send back unformatted values @param [in] fmt= (N) Setting Y converts all vars to their formatted values
@param [out] fref= (_webout) The fileref to which to write the JSON @param [out] fref= (_webout) The fileref to which to write the JSON
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`) (eg `null`) or as STRING values (eg `".a"` or `".b"`)
@@ -31,6 +31,11 @@
such as the column formats and types. The metadata is contained inside an 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, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$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
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuser.sas @li mf_getuser.sas
@@ -46,8 +51,8 @@
**/ **/
%macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=Y,missing=NULL %macro ms_webout(action,ds,dslabel=,fref=_webout,fmt=N,missing=NULL
,showmeta=N ,showmeta=N,maxobs=MAX,workobs=0
); );
%global _webin_file_count _webin_fileref1 _webin_name1 _program _debug %global _webin_file_count _webin_fileref1 _webin_name1 _program _debug
sasjs_tables; sasjs_tables;
@@ -106,12 +111,12 @@
%let missing=NULL; %let missing=NULL;
%end; %end;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,engine=DATASTEP,missing=&missing,showmeta=&showmeta ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do; %if %str(&workobs) > 0 %then %do;
/* if debug mode, send back first 10 records of each work table also */ /* if debug mode, send back first XX records of each work table also */
data;run;%let tempds=%scan(&syslast,2,.); data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds; ods output Members=&tempds;
proc datasets library=WORK memtype=data; proc datasets library=WORK memtype=data;
@@ -136,7 +141,9 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; 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; data _null_; file &fref mod encoding='utf-8' termstr=lf;
put "}"; put "}";
%end; %end;
@@ -146,11 +153,13 @@
%end; %end;
/* close off json */ /* close off json */
data _null_;file &fref mod encoding='utf-8' termstr=lf lrecl=32767; data _null_;file &fref mod encoding='utf-8' termstr=lf lrecl=32767;
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
put ",""SYSUSERID"" : ""&sysuserid"" ";
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
put ",""_DEBUG"" : ""&_debug"" "; put ",""_DEBUG"" : ""&_debug"" ";
_PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ',"_PROGRAM" : ' _PROGRAM ; put ',"_PROGRAM" : ' _PROGRAM ;
autoexec=quote(urlencode(trim(getoption('autoexec'))));
put ',"AUTOEXEC" : ' autoexec;
put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
put ",""SYSCC"" : ""&syscc"" "; put ",""SYSCC"" : ""&syscc"" ";
put ",""SYSENCODING"" : ""&sysencoding"" "; put ",""SYSENCODING"" : ""&sysencoding"" ";
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"'); syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
@@ -160,21 +169,18 @@
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
length SYSPROCESSNAME $512;
SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSSCPL"" : ""&sysscpl"" "; put ",""SYSSCPL"" : ""&sysscpl"" ";
put ",""SYSSITE"" : ""&syssite"" "; put ",""SYSSITE"" : ""&syssite"" ";
put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" "; put ",""SYSTCPIPHOSTNAME"" : ""&SYSTCPIPHOSTNAME"" ";
put ",""SYSUSERID"" : ""&sysuserid"" ";
sysvlong=quote(trim(symget('sysvlong'))); sysvlong=quote(trim(symget('sysvlong')));
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"'); syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');
put ',"SYSWARNINGTEXT" : ' syswarningtext; put ',"SYSWARNINGTEXT" : ' syswarningtext;
put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" '; put ',"END_DTTM" : "' "%sysfunc(datetime(),E8601DT26.6)" '" ';
length autoexec $512;
autoexec=quote(urlencode(trim(getoption('autoexec'))));
put ',"AUTOEXEC" : ' autoexec;
length memsize $32; length memsize $32;
memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)"; memsize="%sysfunc(INPUTN(%sysfunc(getoption(memsize)), best.),sizekmg.)";
memsize=quote(cats(memsize)); memsize=quote(cats(memsize));

View File

@@ -0,0 +1,35 @@
/**
@file
@brief Testing mf_increment macro
<h4> SAS Macros </h4>
@li mf_increment.sas
@li mp_assert.sas
**/
%let var=0;
%mp_assert(
iftrue=(
"%mf_increment(var)"="1"
),
desc=Checking basic mf_increment usage 1,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_increment(var)"="2"
),
desc=Checking basic mf_increment usage 2,
outds=work.test_results
)
%mp_assert(
iftrue=(
"%mf_increment(var,incr=2)"="4"
),
desc=Checking incr option,
outds=work.test_results
)

View File

@@ -0,0 +1,43 @@
/**
@file
@brief Testing mp_cleancsv.sas macro
@details Credit for test 1 goes to
[Tom](https://communities.sas.com/t5/user/viewprofilepage/user-id/159) from
SAS Communities:
https://communities.sas.com/t5/SAS-Programming/Removing-embedded-carriage-returns/m-p/824790#M325761
<h4> SAS Macros </h4>
@li mf_nobs.sas
@li mp_cleancsv.sas
@li mp_assert.sas
@li mp_assertscope.sas
**/
/* test 1 - cope with empty rows on CR formatted file */
filename crlf "%sysfunc(pathname(work))/crlf";
filename cr "%sysfunc(pathname(work))/cr";
data _null_;
file cr termstr=cr ;
put 'line 1'///'line 4'/'line 5';
run;
%mp_assertscope(SNAPSHOT)
%mp_cleancsv(in=cr,out=crlf)
%mp_assertscope(COMPARE)
/* 5 rows as all converted to OD0A */
data test1;
infile "%sysfunc(pathname(work))/crlf" lrecl=100 termstr=crlf;
input;
list;
run;
%put test1=%mf_nobs(test1);
%mp_assert(
iftrue=(%mf_nobs(work.test1)=5),
desc=Checking blank rows on CR formatted file,
outds=work.test_results
)

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

@@ -38,7 +38,7 @@ William,M,15,66.5,112
;;;; ;;;;
run; run;
/* valid filter conditions */ /* VALID filter conditions */
data work.inds; data work.inds;
infile datalines4 dsd; infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32. input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
@@ -51,6 +51,9 @@ AND,OR,2,Weight,>=,77.7
AND,OR,2,Weight,NE,77.7 AND,OR,2,Weight,NE,77.7
AND,AND,1,age,=,.A AND,AND,1,age,=,.A
AND,AND,1,height,<,.B AND,AND,1,height,<,.B
AND,AND,1,age,IN,"(.a,.b,.)"
AND,AND,1,age,IN,"(.A)"
;;;; ;;;;
run; run;
@@ -115,6 +118,28 @@ run;
outds=work.test_results outds=work.test_results
) )
/* invalid IN value */
data work.inds;
infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$10. RAW_VALUE:$4000.;
datalines4;
AND,OR,2,age,IN,"(.,.a,X)"
;;;;
run;
%mp_filtercheck(work.inds,
targetds=work.class,
outds=work.badrecords,
abort=NO
)
%let syscc=0;
%mp_assertdsobs(work.badrecords,
desc=Invalid IN value,
test=HASOBS,
outds=work.test_results
)
/* Code injection - column name */ /* Code injection - column name */
data work.inds; data work.inds;
infile datalines4 dsd; infile datalines4 dsd;
@@ -163,7 +188,7 @@ run;
data work.inds; data work.inds;
infile datalines4 dsd; infile datalines4 dsd;
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32. input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
OPERATOR_NM:$10. RAW_VALUE:8; OPERATOR_NM:$10. RAW_VALUE:8.;
datalines4; datalines4;
AND,AND,1,age,=,0 AND,AND,1,age,=,0
;;;; ;;;;

View File

@@ -56,6 +56,9 @@ AND,AND,1,SEX,<=,"'M'"
AND,OR,2,Name,NOT IN,"('Jane','Alfred')" AND,OR,2,Name,NOT IN,"('Jane','Alfred')"
AND,OR,2,Weight,>=,77.7 AND,OR,2,Weight,>=,77.7
AND,OR,2,Weight,NE,77.7 AND,OR,2,Weight,NE,77.7
AND,AND,3,age,NOT IN,"(.a,.b,.)"
AND,AND,3,age,NOT IN,"(.A)"
AND,AND,4,Name,=,"'Jeremiah'"
;;;; ;;;;
run; run;

View File

@@ -24,7 +24,8 @@
@param [in] contentdisp= (inline) Content Disposition. Example values: @param [in] contentdisp= (inline) Content Disposition. Example values:
@li inline @li inline
@li attachment @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 @param [in] access_token_var= The global macro variable to contain the access
token, if using authorization_code grant type. token, if using authorization_code grant type.
@param [in] grant_type= (sas_services) Valid values are: @param [in] grant_type= (sas_services) Valid values are:
@@ -52,6 +53,7 @@
,inref= ,inref=
,intype=BINARY ,intype=BINARY
,contentdisp=inline ,contentdisp=inline
,ctype=0
,access_token_var=ACCESS_TOKEN ,access_token_var=ACCESS_TOKEN
,grant_type=sas_services ,grant_type=sas_services
,mdebug=0 ,mdebug=0
@@ -103,8 +105,10 @@ filename &fref filesrvc
folderPath="&path" folderPath="&path"
filename="&name" filename="&name"
cdisp="&contentdisp" cdisp="&contentdisp"
%if "&ctype" ne "0" %then %do;
ctype="&ctype"
%end;
lrecl=1048544; lrecl=1048544;
%if &intype=BINARY %then %do; %if &intype=BINARY %then %do;
%mp_binarycopy(inref=&inref, outref=&fref) %mp_binarycopy(inref=&inref, outref=&fref)
%end; %end;

View File

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

View File

@@ -236,14 +236,14 @@ data _null_;
file &adapter; file &adapter;
put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */"; put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */";
/* WEBOUT BEGIN */ /* WEBOUT BEGIN */
put ' ';
put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ';
put ' ,engine=DATASTEP '; put ' ,engine=DATASTEP ';
put ' ,missing=NULL '; put ' ,missing=NULL ';
put ' ,showmeta=N '; put ' ,showmeta=N ';
put ' ,maxobs=MAX '; put ' ,maxobs=MAX ';
put ')/*/STORE SOURCE*/; '; put ')/*/STORE SOURCE*/; ';
put '%local tempds colinfo fmtds i numcols stmt_obs; '; put '%local tempds colinfo fmtds i numcols numobs stmt_obs lastobs optval ';
put ' tmpds1 tmpds2 tmpds3 tmpds4; ';
put '%let numcols=0; '; put '%let numcols=0; ';
put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); ';
put ' '; put ' ';
@@ -281,7 +281,7 @@ data _null_;
put ' by varnum; '; put ' by varnum; ';
put ' run; '; put ' run; ';
put ' /* move meta to mac vars */ '; put ' /* move meta to mac vars */ ';
put ' data _null_; '; put ' data &colinfo; ';
put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); '; put ' if _n_=1 then call symputx(''numcols'',nobs,''l''); ';
put ' set &colinfo end=last nobs=nobs; '; put ' set &colinfo end=last nobs=nobs; ';
put ' name=upcase(name); '; put ' name=upcase(name); ';
@@ -292,7 +292,6 @@ data _null_;
put ' if format='''' then fmt=cats(''$'',length,''.''); '; put ' if format='''' then fmt=cats(''$'',length,''.''); ';
put ' else if formatl=0 then fmt=cats(format,''.''); '; put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else fmt=cats(format,formatl,''.''); '; put ' else fmt=cats(format,formatl,''.''); ';
put ' newlen=max(formatl,length); ';
put ' end; '; put ' end; ';
put ' else do; '; put ' else do; ';
put ' typelong=''num''; '; put ' typelong=''num''; ';
@@ -300,23 +299,26 @@ data _null_;
put ' else if formatl=0 then fmt=cats(format,''.''); '; put ' else if formatl=0 then fmt=cats(format,''.''); ';
put ' else if formatd=0 then fmt=cats(format,formatl,''.''); '; put ' else if formatd=0 then fmt=cats(format,formatl,''.''); ';
put ' else fmt=cats(format,formatl,''.'',formatd); '; put ' else fmt=cats(format,formatl,''.'',formatd); ';
put ' /* needs to be wide, for datetimes etc */ ';
put ' newlen=max(length,formatl,24); ';
put ' end; '; put ' end; ';
put ' /* 32 char unique name */ '; put ' /* 32 char unique name */ ';
put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); ';
put ' '; put ' ';
put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''name'',_n_),name,''l''); ';
put ' call symputx(cats(''newname'',_n_),newname,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); ';
put ' call symputx(cats(''len'',_n_),newlen,''l''); ';
put ' call symputx(cats(''length'',_n_),length,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); ';
put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); ';
put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); ';
put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); ';
put ' call symputx(cats(''label'',_n_),coalescec(label,name),''l''); '; 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+10)*1.5)),''l''); ';
put ' run; '; put ' run; ';
put ' '; put ' ';
put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' proc sql; ';
put ' select count(*) into: lastobs from &ds; ';
put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); ';
put ' '; put ' ';
put ' %if &engine=PROCJSON %then %do; '; put ' %if &engine=PROCJSON %then %do; ';
put ' %if &missing=STRING %then %do; '; put ' %if &missing=STRING %then %do; ';
@@ -353,26 +355,99 @@ data _null_;
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
put ' data _data_; '; put ' /** ';
put ' * Extract format definitions ';
put ' * First, by getting library locations from dictionary.formats ';
put ' * Then, by exporting the width using proc format ';
put ' * Cannot use maxw from sashelp.vformat as not always populated ';
put ' * Cannot use fmtinfo() as not supported in all flavours ';
put ' */ ';
put ' %let tmpds1=%substr(fmtsum%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds2=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
put ' %let tmpds3=%substr(cntl%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); ';
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 ' 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 ' LENGTH num ';
put ' ); ';
put ' %local catlist cat fmtlist i; ';
put ' select distinct fmtcat into: catlist separated by '' '' from &tmpds1; ';
put ' %do i=1 %to %sysfunc(countw(&catlist,%str( ))); ';
put ' %let cat=%scan(&catlist,&i,%str( )); ';
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 length); ';
put ' select &fmtlist; ';
put ' run; ';
put ' proc sql; ';
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.length as MAXW ';
put ' from &colinfo a ';
put ' left join &tmpds2 b ';
put ' on cats(a.format)=cats(upcase(b.fmtname)) ';
put ' order by a.varnum; ';
put ' data _null_; ';
put ' set &tmpds4; ';
put ' if not missing(maxw); ';
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)+10)*1.5)) ';
put ' ,''l'' ';
put ' ); ';
put ' run; ';
put ' ';
put ' /* configure varlenchk - as we are explicitly shortening the variables */ ';
put ' %let optval=%sysfunc(getoption(varlenchk)); ';
put ' options varlenchk=NOWARN; ';
put ' data _data_(compress=char); ';
put ' /* shorten the new vars */ ';
put ' length ';
put ' %do i=1 %to &numcols; ';
put ' &&name&i $&&fmtlen&i ';
put ' %end; ';
put ' ; ';
put ' /* rename on entry */ '; put ' /* rename on entry */ ';
put ' set &ds(rename=( '; put ' set &ds(rename=( ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' &&name&i=&&newname&i '; put ' &&name&i=&&newname&i ';
put ' %end; '; put ' %end; ';
put ' )); '; put ' )); ';
put ' &stmt_obs; ';
put ' ';
put ' drop ';
put ' %do i=1 %to &numcols; ';
put ' &&newname&i ';
put ' %end; ';
put ' ; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' length &&name&i $&&len&i; ';
put ' %if &&typelong&i=num %then %do; '; put ' %if &&typelong&i=num %then %do; ';
put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); ';
put ' %end; '; put ' %end; ';
put ' %else %do; '; put ' %else %do; ';
put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' &&name&i=put(&&newname&i,&&fmt&i); ';
put ' %end; '; put ' %end; ';
put ' drop &&newname&i; ';
put ' %end; '; put ' %end; ';
put ' if _error_ then call symputx(''syscc'',1012); '; put ' if _error_ then do; ';
put ' call symputx(''syscc'',1012); ';
put ' stop; ';
put ' end; ';
put ' run; '; put ' run; ';
put ' %let fmtds=&syslast; '; put ' %let fmtds=&syslast; ';
put ' options varlenchk=&optval; ';
put ' %end; '; put ' %end; ';
put ' '; put ' ';
put ' proc format; /* credit yabwon for special null removal */ '; put ' proc format; /* credit yabwon for special null removal */ ';
@@ -391,8 +466,8 @@ data _null_;
put ' attrib _all_ label=''''; '; put ' attrib _all_ label=''''; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; ';
put ' length &&name&i $32767; '; put ' length &&name&i $&&fmtlen&i...; ';
put ' format &&name&i $32767.; '; put ' format &&name&i $&&fmtlen&i...; ';
put ' %end; '; put ' %end; ';
put ' %end; '; put ' %end; ';
put ' %if &fmt=Y %then %do; '; put ' %if &fmt=Y %then %do; ';
@@ -405,7 +480,7 @@ data _null_;
put ' format _numeric_ bart.; '; put ' format _numeric_ bart.; ';
put ' %do i=1 %to &numcols; '; put ' %do i=1 %to &numcols; ';
put ' %if &&typelong&i=char or &fmt=Y %then %do; '; 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 ' &&name&i=''"''!!trim( ';
put ' prxchange(''s/"/\\"/'',-1, /* double quote */ '; put ' prxchange(''s/"/\\"/'',-1, /* double quote */ ';
put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ '; put ' prxchange(''s/\x0A/\n/'',-1, /* new line */ ';
@@ -418,8 +493,9 @@ data _null_;
put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ '; put ' prxchange(''s/\x02/\\u0002/'',-1, /* STX */ ';
put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ '; put ' prxchange(''s/\x10/\\u0010/'',-1, /* DLE */ ';
put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ '; put ' prxchange(''s/\x11/\\u0011/'',-1, /* DC1 */ ';
put ' prxchange(''s/\x1A/\\u001A/'',-1, /* SUB */ ';
put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) '; put ' prxchange(''s/\\/\\\\/'',-1,&&name&i) ';
put ' ))))))))))))!!''"''; '; put ' )))))))))))))!!''"''; ';
put ' end; '; put ' end; ';
put ' else &&name&i=quote(cats(&&name&i)); '; put ' else &&name&i=quote(cats(&&name&i)); ';
put ' %end; '; put ' %end; ';
@@ -514,8 +590,8 @@ data _null_;
put ' %quote(&user) '; put ' %quote(&user) ';
put ' '; put ' ';
put '%mend mf_getuser; '; put '%mend mf_getuser; ';
put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL '; put '%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL ';
put ' ,showmeta=N '; put ' ,showmeta=N,maxobs=MAX,workobs=0 ';
put '); '; put '); ';
put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name '; put '%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name ';
put ' sasjs_tables SYS_JES_JOB_URI; '; put ' sasjs_tables SYS_JES_JOB_URI; ';
@@ -617,13 +693,13 @@ data _null_;
put ' run; '; put ' run; ';
put '%end; '; put '%end; ';
put '%else %if &action=ARR or &action=OBJ %then %do; '; put '%else %if &action=ARR or &action=OBJ %then %do; ';
put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt '; put ' %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref ';
put ' ,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta '; put ' ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs ';
put ' ) '; put ' ) ';
put '%end; '; put '%end; ';
put '%else %if &action=CLOSE %then %do; '; put '%else %if &action=CLOSE %then %do; ';
put ' %if %str(&_debug) ge 131 %then %do; '; put ' %if %str(&workobs) > 0 %then %do; ';
put ' /* send back first 10 records of each work table for debugging */ '; put ' /* send back first XX records of each work table for debugging */ ';
put ' data;run;%let tempds=%scan(&syslast,2,.); '; put ' data;run;%let tempds=%scan(&syslast,2,.); ';
put ' ods output Members=&tempds; '; put ' ods output Members=&tempds; ';
put ' proc datasets library=WORK memtype=data; '; put ' proc datasets library=WORK memtype=data; ';
@@ -646,7 +722,9 @@ data _null_;
put ' put " ""&wt"" : {"; '; put ' put " ""&wt"" : {"; ';
put ' put ''"nlobs":'' nlobs; '; put ' put ''"nlobs":'' nlobs; ';
put ' put '',"nvars":'' nvars; '; put ' put '',"nvars":'' nvars; ';
put ' %mp_jsonout(OBJ,&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 ' data _null_; file &fref mod;put "}"; ';
put ' %end; '; put ' %end; ';
put ' data _null_; file &fref mod;put "}";run; '; put ' data _null_; file &fref mod;put "}";run; ';
@@ -654,20 +732,28 @@ data _null_;
put ' '; put ' ';
put ' /* close off json */ '; put ' /* close off json */ ';
put ' data _null_;file &fref mod; '; put ' data _null_;file &fref mod; ';
put ' length SYSPROCESSNAME syserrortext syswarningtext autoexec $512; ';
put ' put ",""_DEBUG"" : ""&_debug"" "; ';
put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); '; put ' _PROGRAM=quote(trim(resolve(symget(''_PROGRAM'')))); ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; '; put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
put ' autoexec=quote(urlencode(trim(getoption(''autoexec'')))); ';
put ' put '',"AUTOEXEC" : '' autoexec; ';
put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; '; put ' put ",""MF_GETUSER"" : ""%mf_getuser()"" "; ';
put ' SYS_JES_JOB_URI=quote(trim(resolve(symget(''SYS_JES_JOB_URI'')))); '; put ' SYS_JES_JOB_URI=quote(trim(resolve(symget(''SYS_JES_JOB_URI'')))); ';
put ' put '',"SYS_JES_JOB_URI" : '' SYS_JES_JOB_URI ; '; put ' put '',"SYS_JES_JOB_URI" : '' SYS_JES_JOB_URI ; ';
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; '; put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
put ' put ",""_DEBUG"" : ""&_debug"" "; ';
put ' put '',"_PROGRAM" : '' _PROGRAM ; ';
put ' put ",""SYSCC"" : ""&syscc"" "; '; put ' put ",""SYSCC"" : ""&syscc"" "; ';
put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); '; put ' syserrortext=cats(''"'',tranwrd(symget(''syserrortext''),''"'',''\"''),''"''); ';
put ' put '',"SYSERRORTEXT" : '' syserrortext; '; put ' put '',"SYSERRORTEXT" : '' syserrortext; ';
put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; '; put ' put ",""SYSHOSTNAME"" : ""&syshostname"" "; ';
put ' put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" "; ';
put ' put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" "; ';
put ' SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME))); ';
put ' put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME; ';
put ' put ",""SYSJOBID"" : ""&sysjobid"" "; ';
put ' put ",""SYSSCPL"" : ""&sysscpl"" "; '; put ' put ",""SYSSCPL"" : ""&sysscpl"" "; ';
put ' put ",""SYSSITE"" : ""&syssite"" "; '; put ' put ",""SYSSITE"" : ""&syssite"" "; ';
put ' put ",""SYSUSERID"" : ""&sysuserid"" "; ';
put ' sysvlong=quote(trim(symget(''sysvlong''))); '; put ' sysvlong=quote(trim(symget(''sysvlong''))); ';
put ' put '',"SYSVLONG" : '' sysvlong; '; put ' put '',"SYSVLONG" : '' sysvlong; ';
put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); '; put ' syswarningtext=cats(''"'',tranwrd(symget(''syswarningtext''),''"'',''\"''),''"''); ';

View File

@@ -25,7 +25,7 @@
@param [in] _webout= fileref for returning the json @param [in] _webout= fileref for returning the json
@param [out] fref=(_mvwtemp) Temp fileref to which to write the output @param [out] fref=(_mvwtemp) Temp fileref to which to write the output
@param [out] dslabel= value to use instead of table name for sending to JSON @param [out] dslabel= value to use instead of table name for sending to JSON
@param [in] fmt=(Y) change to N to strip formats from output @param [in] fmt= (N) Setting Y converts all vars to their formatted values
@param [in] stream=(Y) Change to N if not streaming to _webout @param [in] stream=(Y) Change to N if not streaming to _webout
@param [in] missing= (NULL) Special numeric missing values can be sent as NULL @param [in] missing= (NULL) Special numeric missing values can be sent as NULL
(eg `null`) or as STRING values (eg `".a"` or `".b"`) (eg `null`) or as STRING values (eg `".a"` or `".b"`)
@@ -33,17 +33,26 @@
such as the column formats and types. The metadata is contained inside an 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, object with the same name as the table but prefixed with a dollar sign - ie,
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}` `,"$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> <h4> SAS Macros </h4>
@li mp_jsonout.sas @li mp_jsonout.sas
@li mf_getuser.sas @li mf_getuser.sas
<h4> Related Macros </h4>
@li ms_webout.sas
@li mm_webout.sas
@version Viya 3.3 @version Viya 3.3
@author Allan Bowe, source: https://github.com/sasjs/core @author Allan Bowe, source: https://github.com/sasjs/core
**/ **/
%macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=Y,stream=Y,missing=NULL %macro mv_webout(action,ds,fref=_mvwtemp,dslabel=,fmt=N,stream=Y,missing=NULL
,showmeta=N ,showmeta=N,maxobs=MAX,workobs=0
); );
%global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name %global _webin_file_count _webin_fileuri _debug _omittextlog _webin_name
sasjs_tables SYS_JES_JOB_URI; sasjs_tables SYS_JES_JOB_URI;
@@ -145,13 +154,13 @@
run; run;
%end; %end;
%else %if &action=ARR or &action=OBJ %then %do; %else %if &action=ARR or &action=OBJ %then %do;
%mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt %mp_jsonout(&action,&ds,dslabel=&dslabel,fmt=&fmt,jref=&fref
,jref=&fref,engine=DATASTEP,missing=&missing,showmeta=&showmeta ,engine=DATASTEP,missing=&missing,showmeta=&showmeta,maxobs=&maxobs
) )
%end; %end;
%else %if &action=CLOSE %then %do; %else %if &action=CLOSE %then %do;
%if %str(&_debug) ge 131 %then %do; %if %str(&workobs) > 0 %then %do;
/* send back first 10 records of each work table for debugging */ /* send back first XX records of each work table for debugging */
data;run;%let tempds=%scan(&syslast,2,.); data;run;%let tempds=%scan(&syslast,2,.);
ods output Members=&tempds; ods output Members=&tempds;
proc datasets library=WORK memtype=data; proc datasets library=WORK memtype=data;
@@ -174,7 +183,9 @@
put " ""&wt"" : {"; put " ""&wt"" : {";
put '"nlobs":' nlobs; put '"nlobs":' nlobs;
put ',"nvars":' nvars; 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 "}"; data _null_; file &fref mod;put "}";
%end; %end;
data _null_; file &fref mod;put "}";run; data _null_; file &fref mod;put "}";run;
@@ -182,20 +193,28 @@
/* close off json */ /* close off json */
data _null_;file &fref mod; data _null_;file &fref mod;
length SYSPROCESSNAME syserrortext syswarningtext autoexec $512;
put ",""_DEBUG"" : ""&_debug"" ";
_PROGRAM=quote(trim(resolve(symget('_PROGRAM')))); _PROGRAM=quote(trim(resolve(symget('_PROGRAM'))));
put ",""SYSUSERID"" : ""&sysuserid"" "; put ',"_PROGRAM" : ' _PROGRAM ;
autoexec=quote(urlencode(trim(getoption('autoexec'))));
put ',"AUTOEXEC" : ' autoexec;
put ",""MF_GETUSER"" : ""%mf_getuser()"" "; put ",""MF_GETUSER"" : ""%mf_getuser()"" ";
SYS_JES_JOB_URI=quote(trim(resolve(symget('SYS_JES_JOB_URI')))); SYS_JES_JOB_URI=quote(trim(resolve(symget('SYS_JES_JOB_URI'))));
put ',"SYS_JES_JOB_URI" : ' SYS_JES_JOB_URI ; put ',"SYS_JES_JOB_URI" : ' SYS_JES_JOB_URI ;
put ",""SYSJOBID"" : ""&sysjobid"" "; put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""_DEBUG"" : ""&_debug"" ";
put ',"_PROGRAM" : ' _PROGRAM ;
put ",""SYSCC"" : ""&syscc"" "; put ",""SYSCC"" : ""&syscc"" ";
syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"'); syserrortext=cats('"',tranwrd(symget('syserrortext'),'"','\"'),'"');
put ',"SYSERRORTEXT" : ' syserrortext; put ',"SYSERRORTEXT" : ' syserrortext;
put ",""SYSHOSTNAME"" : ""&syshostname"" "; put ",""SYSHOSTNAME"" : ""&syshostname"" ";
put ",""SYSPROCESSID"" : ""&SYSPROCESSID"" ";
put ",""SYSPROCESSMODE"" : ""&SYSPROCESSMODE"" ";
SYSPROCESSNAME=quote(urlencode(cats(SYSPROCESSNAME)));
put ",""SYSPROCESSNAME"" : " SYSPROCESSNAME;
put ",""SYSJOBID"" : ""&sysjobid"" ";
put ",""SYSSCPL"" : ""&sysscpl"" "; put ",""SYSSCPL"" : ""&sysscpl"" ";
put ",""SYSSITE"" : ""&syssite"" "; put ",""SYSSITE"" : ""&syssite"" ";
put ",""SYSUSERID"" : ""&sysuserid"" ";
sysvlong=quote(trim(symget('sysvlong'))); sysvlong=quote(trim(symget('sysvlong')));
put ',"SYSVLONG" : ' sysvlong; put ',"SYSVLONG" : ' sysvlong;
syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"'); syswarningtext=cats('"',tranwrd(symget('syswarningtext'),'"','\"'),'"');