mirror of
https://github.com/sasjs/core.git
synced 2025-12-17 17:04:34 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
713f7544cd | ||
|
|
de4e96ab01 | ||
|
|
3e7b15c7db | ||
|
|
eb2ccfbbca | ||
|
|
70e508e583 | ||
|
|
8b0acf2eae | ||
|
|
d254870439 | ||
|
|
303225cb85 | ||
|
|
90de167643 | ||
|
|
8ee997de8e | ||
|
|
e27f6ac716 | ||
|
|
ec4de95fcf | ||
|
|
df0fa95519 | ||
|
|
2fe7fba79b | ||
|
|
e40234ee29 | ||
|
|
a287cc27a7 | ||
|
|
921186eb74 | ||
|
|
6fd215ceff | ||
|
|
0297509aa0 | ||
|
|
c5a8bc745d | ||
|
|
36aa466561 | ||
|
|
009485e5b9 | ||
|
|
eb01c8772d | ||
|
|
e3fb69928c | ||
|
|
65afa14466 | ||
|
|
0176b19616 | ||
|
|
9f3dfd9a59 | ||
|
|
513ea354ab | ||
|
|
7b686e11c9 | ||
|
|
3997000266 |
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: monthly
|
||||
open-pull-requests-limit: 3
|
||||
allow:
|
||||
- dependency-type: "production"
|
||||
35
README.md
35
README.md
@@ -31,19 +31,28 @@ Documentation: https://core.sasjs.io
|
||||
|
||||
## Components
|
||||
|
||||
### BASE library (SAS9/Viya)
|
||||
### BASE library (All Platforms)
|
||||
|
||||
- OS independent
|
||||
- Not metadata aware
|
||||
- Works on all SAS Platforms
|
||||
- No X command
|
||||
- Prefixes: _mf_, _mp_
|
||||
|
||||
#### FCMP library (SAS9/Viya)
|
||||
### DDL library (All Platforms)
|
||||
|
||||
- OS independent
|
||||
- Works on all SAS Platforms
|
||||
- No X command
|
||||
- Prefixes: _mddl_(lib)_ -> where lib can be "SAS" (in relation to a SAS component) or "DC" (in relation to a Data Controller component)
|
||||
|
||||
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 library (All Platforms)
|
||||
|
||||
- Function and macro names are identical, except for special cases
|
||||
- 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.
|
||||
|
||||
### META library (SAS9 only)
|
||||
|
||||
@@ -81,7 +90,7 @@ Macros used for interfacing with SAS Viya.
|
||||
|
||||
Wait - this is a macro library - what is LUA doing here? Well, it is a little known fact that you CAN run LUA within a SAS Macro. It has to be written to a text file with a `.lua` extension, from where you can `%include` it. So, without using the `proc lua` wrapper.
|
||||
|
||||
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert your LUA into a data step with put statements, and create the macro wrapper with a `ml_` prefix. You can then use your module in any program by running:
|
||||
To contribute, simply write your freeform LUA in the LUA folder. Then run the `build.py`, which will convert all files with a ".lua" extension into a macro wrapper with an `ml_` prefix (embedding the necessary data step put statements). You can then use your module in any program by running:
|
||||
|
||||
```sas
|
||||
/* compile the lua module */
|
||||
@@ -95,8 +104,7 @@ endsubmit;
|
||||
run;
|
||||
```
|
||||
|
||||
- X command enabled
|
||||
- Prefixes: _mmw_,_mmu_,_mmx_
|
||||
- Prefixes: _ml_
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -126,14 +134,15 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
|
||||
- one macro per file
|
||||
- prefixes:
|
||||
- _mcf_ for macro compiled functions (proc fcmp)
|
||||
- _mddl_ for macros containing DDL (Data Definition Language)
|
||||
- _mf_ for macro functions (can be used in open code).
|
||||
- _ml_ for macros that are used to compile LUA modules
|
||||
- _mm_ for metadata macros (interface with the metadata server).
|
||||
- _mmx_ for macros that use metadata and are XCMD enabled
|
||||
- _mmx_ for macros that use metadata and are XCMD enabled (working on both windows and unix)
|
||||
- _mp_ for macro procedures (which generate sas code)
|
||||
- _ms_ for macro procedures that will only work with [@sasjs/server](https://github.com/sasjs/server)
|
||||
- _mv_ for macro procedures that will only work in Viya
|
||||
- _mx_ for macros that are XCMD enabled
|
||||
- _mx_ for macros that are XCMD enabled (working on both windows and unix)
|
||||
- follow verb-noun convention
|
||||
- unix style line endings (lf)
|
||||
- individual lines should be no more than 80 characters long
|
||||
@@ -193,11 +202,13 @@ When contributing to this library, it is therefore important to ensure that all
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major (breaking) release becomes necessary:
|
||||
We are currently on major release v4. Breaking changes should be marked with the [deprecated](https://www.doxygen.nl/manual/commands.html#cmddeprecated) doxygen tag. The following changes are planned when the next major/breaking release (v5) becomes necessary:
|
||||
|
||||
* mp_testservice.sas to be renamed as mp_execute.sas (as it doesn't actually test anything)
|
||||
* `insert_cmplib` option of mcf_xxx macros will be deprecated (the option is now checked automatically with value inserted only if needed)
|
||||
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience
|
||||
* mcf_xxx macros to have `wrap=` option defaulted to YES for convenience. Set this option explicitly to avoid issues.
|
||||
* mp_getddl.sas to be renamed to mp_ds2ddl.sas (consistent with other ds2xxx macros). A wrapper macro is already in place, and you are able to use this immediately. The default for SHOWLOG will also be YES instead of NO.
|
||||
* mp_coretable.sas will be replaced by the standalone macros in the `ddl` folder (which are already available)
|
||||
|
||||
## Star Gazing
|
||||
|
||||
|
||||
525
all.sas
525
all.sas
@@ -352,6 +352,7 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
||||
@li /data
|
||||
@li /jobs
|
||||
@li /services
|
||||
@li /tests
|
||||
@li /tests/jobs
|
||||
@li /tests/services
|
||||
@li /tests/macros
|
||||
@@ -383,9 +384,13 @@ https://github.com/yabwon/SAS_PACKAGES/blob/main/packages/baseplus.md#functionex
|
||||
|
||||
/**
|
||||
* First check we are not in the tests/macros folder (which has no subfolders)
|
||||
* or specifically in the testsetup or testteardown services
|
||||
*/
|
||||
%if %index(&pgm,/tests/macros/) %then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1);
|
||||
%if %index(&pgm,/tests/macros/)
|
||||
or %index(&pgm,/tests/testsetup)
|
||||
or %index(&pgm,/tests/testteardown)
|
||||
%then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests)-1);
|
||||
&root
|
||||
%return;
|
||||
%end;
|
||||
@@ -1802,7 +1807,7 @@ Usage:
|
||||
|
||||
%mend mf_uid;/**
|
||||
@file
|
||||
@brief Checks if a set of macro variables exist / contain values.
|
||||
@brief Checks if a set of macro variables exist AND contain values.
|
||||
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
|
||||
Usage:
|
||||
|
||||
@@ -1816,10 +1821,11 @@ Usage:
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_abort.sas
|
||||
|
||||
@param verifyvars space separated list of macro variable names
|
||||
@param makeupcase= set to YES to convert all variable VALUES to
|
||||
@param [in] verifyvars Space separated list of macro variable names
|
||||
@param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
|
||||
uppercase.
|
||||
@param mAbort= Abort Type. Default is SOFT (writes err to log).
|
||||
@param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
|
||||
message to the log.
|
||||
Set to any other value to call mf_abort (which can be configured to abort in
|
||||
various fashions according to context).
|
||||
|
||||
@@ -1860,9 +1866,15 @@ Usage:
|
||||
|
||||
%goto exit_success;
|
||||
%exit_err:
|
||||
%if &mAbort=SOFT %then %put %str(ERR)OR: &abortmsg;
|
||||
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg);
|
||||
%put %str(ERR)OR: &abortmsg;
|
||||
%mf_abort(iftrue=(&mabort ne SOFT),
|
||||
mac=mf_verifymacvars,
|
||||
msg=%str(&abortmsg)
|
||||
)
|
||||
0
|
||||
%return;
|
||||
%exit_success:
|
||||
1
|
||||
|
||||
%mend mf_verifymacvars;
|
||||
/**
|
||||
@@ -3521,24 +3533,32 @@ run;
|
||||
make use of permanent tables. To avoid duplication in definitions, this
|
||||
macro provides a central location for managing the corresponding DDL.
|
||||
|
||||
Note - this macro is likely to be deprecated in future in favour of a
|
||||
dedicated "datamodel" folder (prefix mddl)
|
||||
|
||||
Any corresponding data would go in a seperate repo, to avoid this one
|
||||
ballooning in size!
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_coretable(LOCKTABLE,libds=work.locktable)
|
||||
|
||||
@param [in] table_ref The type of table to create. Example values:
|
||||
@li DIFFTABLE - Used to store changes to tables. Used by mp_storediffs.sas
|
||||
and mp_stackdiffs.sas
|
||||
@li FILTER_DETAIL - For storing detailed filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
@li FILTER_SUMMARY - For storing summary filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
@li LOCKANYTABLE - For "locking" tables prior to multipass loads. Used by
|
||||
mp_lockanytable.sas
|
||||
@li MAXKEYTABLE - For storing the maximum retained key information. Used
|
||||
by mp_retainedkey.sas
|
||||
@li DIFFTABLE
|
||||
@li FILTER_DETAIL
|
||||
@li FILTER_SUMMARY
|
||||
@li LOCKANYTABLE
|
||||
@li MAXKEYTABLE
|
||||
@param [in] libds= (0) The library.dataset reference used to create the table.
|
||||
If not provided, then the DDL is simply printed to the log.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_difftable.sas
|
||||
@li mddl_dc_filterdetail.sas
|
||||
@li mddl_dc_filtersummary.sas
|
||||
@li mddl_dc_locktable.sas
|
||||
@li mddl_dc_maxkeytable.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_filterstore.sas
|
||||
@li mp_lockanytable.sas
|
||||
@@ -3557,76 +3577,21 @@ run;
|
||||
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
|
||||
proc sql;
|
||||
%if &table_ref=DIFFTABLE %then %do;
|
||||
create table &outds(
|
||||
load_ref char(36) label='unique load reference',
|
||||
processed_dttm num format=E8601DT26.6 label='Processed at timestamp',
|
||||
libref char(8) label='Library Reference (8 chars)',
|
||||
dsn char(32) label='Dataset Name (32 chars)',
|
||||
key_hash char(32) label=
|
||||
'MD5 Hash of primary key values (pipe seperated)',
|
||||
move_type char(1) label='Either (A)ppended, (D)eleted or (M)odified',
|
||||
is_pk num label='Is Primary Key Field? (1/0)',
|
||||
is_diff num label=
|
||||
'Did value change? (1/0/-1). Always -1 for appends and deletes.',
|
||||
tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
|
||||
tgtvar_nm char(32) label='Target variable name (32 chars)',
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
);
|
||||
%mddl_dc_difftable(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=LOCKTABLE %then %do;
|
||||
create table &outds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_ref char(200),
|
||||
lock_pid char(10),
|
||||
lock_start_dttm num format=E8601DT26.6,
|
||||
lock_end_dttm num format=E8601DT26.6,
|
||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds));
|
||||
%mddl_dc_locktable(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=FILTER_SUMMARY %then %do;
|
||||
create table &outds(
|
||||
filter_rk num not null,
|
||||
filter_hash char(32) not null,
|
||||
filter_table char(41) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_rk));
|
||||
%mddl_dc_filtersummary(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=FILTER_DETAIL %then %do;
|
||||
create table &outds(
|
||||
filter_hash char(32) not null,
|
||||
filter_line num not null,
|
||||
group_logic char(3) not null,
|
||||
subgroup_logic char(3) not null,
|
||||
subgroup_id num not null,
|
||||
variable_nm varchar(32) not null,
|
||||
operator_nm varchar(12) not null,
|
||||
raw_value varchar(4000) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_hash,filter_line));
|
||||
%mddl_dc_filterdetail(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=MAXKEYTABLE %then %do;
|
||||
create table &outds(
|
||||
keytable varchar(41) label='Base table in libref.dataset format',
|
||||
keycolumn char(32) format=$32.
|
||||
label='The Retained key field containing the key values.',
|
||||
max_key num label=
|
||||
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||
processed_dttm num format=E8601DT26.6
|
||||
label='Datetime this value was last updated',
|
||||
constraint pk_mpe_maxkeyvalues
|
||||
primary key(keytable));
|
||||
%mddl_dc_maxkeytable(libds=&outds)
|
||||
%end;
|
||||
|
||||
|
||||
%if &libds=0 %then %do;
|
||||
describe table &syslast;
|
||||
drop table &syslast;
|
||||
@@ -4715,7 +4680,7 @@ quit;
|
||||
numbers).
|
||||
@param [out] outfile= The output filename - should be quoted.
|
||||
@param [out] outref= (0) The output fileref (takes precedence if provided)
|
||||
@param [in] outencoding= (0) The output encoding to use (unquoted)
|
||||
@param [in] outencoding= (0) The (quoted) output encoding to use, eg `"UTF-8"`
|
||||
@param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
|
||||
always be CRLF. Valid values:
|
||||
@li CRLF
|
||||
@@ -4751,7 +4716,7 @@ quit;
|
||||
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
|
||||
|
||||
%if &outencoding=0 %then %let outencoding=;
|
||||
%else %let outencoding=encoding="&outencoding";
|
||||
%else %let outencoding=encoding=&outencoding;
|
||||
|
||||
%if &outref=0 %then %let outloc=&outfile;
|
||||
%else %let outloc=&outref;
|
||||
@@ -4876,6 +4841,35 @@ data _null_;
|
||||
run;
|
||||
|
||||
%mend mp_ds2csv;/**
|
||||
@file
|
||||
@brief A wrapper for mp_getddl.sas
|
||||
@details In the next release, this will be the main version.
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_getddl.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_ds2ddl(libds,fref=getddl,flavour=SAS,showlog=YES,schema=
|
||||
,applydttm=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local libref;
|
||||
%let libds=%upcase(&libds);
|
||||
%let libref=%scan(&libds,1,.);
|
||||
%if &libref=&libds %then %let libds=WORK.&libds;
|
||||
|
||||
%mp_getddl(%scan(&libds,1,.)
|
||||
,%scan(&libds,2,.)
|
||||
,fref=&fref
|
||||
,flavour=SAS
|
||||
,showlog=&showlog
|
||||
,schema=&schema
|
||||
,applydttm=&applydttm
|
||||
)
|
||||
|
||||
%mend mp_ds2ddl;/**
|
||||
@file
|
||||
@brief Converts every value in a dataset to formatted value
|
||||
@details Converts every value to it's formatted value. All variables will
|
||||
@@ -5382,7 +5376,8 @@ options varlenchk=&optval;
|
||||
SYSCC to 1008 if bad records are found, and call mp_abort.sas for a
|
||||
graceful service exit (configurable).
|
||||
|
||||
Used for dynamic filtering in [Data Controller for SAS®](https://datacontroller.io).
|
||||
Used for dynamic filtering in [Data Controller for SAS®](
|
||||
https://datacontroller.io).
|
||||
|
||||
Usage:
|
||||
|
||||
@@ -5501,7 +5496,7 @@ data &outds;
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
@@ -5689,8 +5684,13 @@ filename &outref temp;
|
||||
https://sasapps.io)). This macro is also used in [Data Controller for SAS](
|
||||
https://datacontroller.io).
|
||||
|
||||
A more recent feature of this macro is the ability to support filter queries
|
||||
on Format Catalogs. This is achieved by adding a `-FC` suffix to the `libds`
|
||||
parameter - where the "ds" in this case is the catalog name.
|
||||
|
||||
@param [in] libds= The target dataset to be filtered (lib should be assigned)
|
||||
|
||||
@param [in] libds= The target dataset to be filtered (lib should be assigned).
|
||||
If filtering a format catalog, add the following suffix: `-FC`.
|
||||
@param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
|
||||
be validated. Has the following format:
|
||||
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||
@@ -5726,6 +5726,7 @@ filename &outref temp;
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_sas_cntlout.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvalue.sas
|
||||
@li mf_islibds.sas
|
||||
@@ -5759,7 +5760,10 @@ filename &outref temp;
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
|
||||
%local ds1 ds2 ds3 ds4 filter_hash;
|
||||
%local ds0 ds1 ds2 ds3 ds4 filter_hash orig_libds;
|
||||
%let libds=%upcase(&libds);
|
||||
%let orig_libds=&libds;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=mp_filterstore
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
@@ -5777,12 +5781,27 @@ filename &outref temp;
|
||||
,msg=%str(Invalid lock_table value: &lock_table)
|
||||
)
|
||||
|
||||
/* validate query */
|
||||
/**
|
||||
* validate query
|
||||
* use format catalog export, if a format
|
||||
*/
|
||||
%if "%substr(&libds,%length(&libds)-2,3)"="-FC" %then %do;
|
||||
%let libds=%scan(&libds,1,-); /* chop off -FC extension */
|
||||
%let ds0=%mf_getuniquename(prefix=fmtds_);
|
||||
%let libds=&ds0;
|
||||
/*
|
||||
There is no need to export the entire format catalog here - the validations
|
||||
are done against the data model, not the data values. So we can simply
|
||||
hardcode the structure based on the cntlout dataset.
|
||||
*/
|
||||
%mddl_sas_cntlout(libds=&ds0)
|
||||
|
||||
%end;
|
||||
%mp_filtercheck(&queryds,targetds=&libds,abort=YES)
|
||||
|
||||
/* hash the result */
|
||||
%let ds1=%mf_getuniquename(prefix=hashds);
|
||||
%mp_hashdataset(&queryds,outds=&ds1,salt=&libds)
|
||||
%mp_hashdataset(&queryds,outds=&ds1,salt=&orig_libds)
|
||||
%let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
@@ -5813,7 +5832,7 @@ run;
|
||||
%let ds3=%mf_getuniquename(prefix=filtersum);
|
||||
data work.&ds3;
|
||||
if 0 then set &filter_summary;
|
||||
filter_table=symget('libds');
|
||||
filter_table="&orig_libds";
|
||||
filter_hash="&filter_hash";
|
||||
PROCESSED_DTTM=%sysfunc(datetime());
|
||||
output;
|
||||
@@ -6949,6 +6968,7 @@ https://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#
|
||||
|`WHICHPATH `|`**OTHER** `|`**OTHER** `|`big fat problem if not path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|`O `|` `|` `|` `|` `|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_sas_cntlout.sas
|
||||
@li mf_dedup.sas
|
||||
@li mf_getfmtlist.sas
|
||||
@li mf_getfmtname.sas
|
||||
@@ -7003,30 +7023,7 @@ create table &outsummary as
|
||||
|
||||
%if "&outdetail" ne "0" %then %do;
|
||||
/* ensure base table always exists */
|
||||
proc sql;
|
||||
create table &outdetail(
|
||||
FMTNAME char(32) label='Format name'
|
||||
,START char(16) label='Starting value for format'
|
||||
,END char(16) label='Ending value for format'
|
||||
,LABEL char(256) label='Format value label'
|
||||
,MIN num length=3 label='Minimum length'
|
||||
,MAX num length=3 label='Maximum length'
|
||||
,DEFAULT num length=3 label='Default length'
|
||||
,LENGTH num length=3 label='Format length'
|
||||
,FUZZ num label='Fuzz value'
|
||||
,PREFIX char(2) label='Prefix characters'
|
||||
,MULT num label='Multiplier'
|
||||
,FILL char(1) label='Fill character'
|
||||
,NOEDIT num length=3 label='Is picture string noedit?'
|
||||
,TYPE char(1) label='Type of format'
|
||||
,SEXCL char(1) label='Start exclusion'
|
||||
,EEXCL char(1) label='End exclusion'
|
||||
,HLO char(13) label='Additional information'
|
||||
,DECSEP char(1) label='Decimal separator'
|
||||
,DIG3SEP char(1) label='Three-digit separator'
|
||||
,DATATYPE char(8) label='Date/time/datetime?'
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
%mddl_sas_cntlout(libds=&outdetail)
|
||||
/* grab the location of each format */
|
||||
%let fmtcnt=0;
|
||||
data _null_;
|
||||
@@ -7043,6 +7040,10 @@ create table &outsummary as
|
||||
proc format library=&&fmtloc&i CNTLOUT=&tempds;
|
||||
select &&fmtname&i;
|
||||
run;
|
||||
data &tempds;
|
||||
length label $256;
|
||||
set &tempds;
|
||||
run;
|
||||
proc append base=&outdetail data=&tempds;
|
||||
run;
|
||||
%end;
|
||||
@@ -10800,17 +10801,19 @@ create table &outds as
|
||||
|
||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||
|
||||
@param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL
|
||||
@param [in] inloc= /path/to/file.ext to be sent
|
||||
@param [in] inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||
@param [in] iftrue= (1=1) Provide a condition under which to execute.
|
||||
@param [out] outname= the name of the file, as downloaded by the browser
|
||||
@param [out] outref= (_webout) The destination where the file should be
|
||||
streamed.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mp_binarycopy.sas
|
||||
|
||||
@param contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
|
||||
@param inloc= /path/to/file.ext to be sent
|
||||
@param inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||
@param outname= the name of the file, as downloaded by the browser
|
||||
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
|
||||
**/
|
||||
|
||||
@@ -10818,12 +10821,16 @@ create table &outds as
|
||||
contenttype=TEXT
|
||||
,inloc=
|
||||
,inref=0
|
||||
,iftrue=%str(1=1)
|
||||
,outname=
|
||||
,outref=_webout
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%let contentype=%upcase(&contenttype);
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
%let contentype=%upcase(&contenttype);
|
||||
%let outref=%upcase(&outref);
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
|
||||
/**
|
||||
* check engine type to avoid the below err message:
|
||||
@@ -10832,20 +10839,20 @@ create table &outds as
|
||||
%local streamweb;
|
||||
%let streamweb=0;
|
||||
data _null_;
|
||||
set sashelp.vextfl(where=(upcase(fileref)="_WEBOUT"));
|
||||
set sashelp.vextfl(where=(upcase(fileref)="&outref"));
|
||||
if xengine='STREAM' then call symputx('streamweb',1,'l');
|
||||
run;
|
||||
|
||||
%if &contentype=ZIP %then %do;
|
||||
%if &contentype=CSV %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
|
||||
contenttype='application/zip'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/csv'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
@@ -10858,11 +10865,52 @@ run;
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
contenttype='application/vnd.ms-excel'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="image/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/text'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="font/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=XLSX %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
@@ -10872,54 +10920,34 @@ run;
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
contenttype=
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%else %if &contentype=ZIP %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/text'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
|
||||
contenttype='application/zip'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=CSV %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/csv'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if &inref ne 0 %then %do;
|
||||
%mp_binarycopy(inref=&inref,outref=_webout)
|
||||
%mp_binarycopy(inref=&inref,outref=&outref)
|
||||
%end;
|
||||
%else %do;
|
||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||
%mp_binarycopy(inloc="&inloc",outref=&outref)
|
||||
%end;
|
||||
|
||||
%mend mp_streamfile;
|
||||
@@ -11878,6 +11906,170 @@ ods package publish archive properties
|
||||
ods package close;
|
||||
|
||||
%mend mp_zip;/**
|
||||
@file
|
||||
@brief Difftable DDL
|
||||
@details Used to store changes to tables. Used by mp_storediffs.sas
|
||||
and mp_stackdiffs.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_difftable(libds=WORK.DIFFTABLE);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
load_ref char(36) label='unique load reference',
|
||||
processed_dttm num format=E8601DT26.6 label='Processed at timestamp',
|
||||
libref char(8) label='Library Reference (8 chars)',
|
||||
dsn char(32) label='Dataset Name (32 chars)',
|
||||
key_hash char(32) label=
|
||||
'MD5 Hash of primary key values (pipe seperated)',
|
||||
move_type char(1) label='Either (A)ppended, (D)eleted or (M)odified',
|
||||
is_pk num label='Is Primary Key Field? (1/0)',
|
||||
is_diff num label=
|
||||
'Did value change? (1/0/-1). Always -1 for appends and deletes.',
|
||||
tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
|
||||
tgtvar_nm char(32) label='Target variable name (32 chars)',
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
);
|
||||
|
||||
%mend mddl_dc_difftable;/**
|
||||
@file
|
||||
@brief Filtertable DDL
|
||||
@details For storing detailed filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
filter_hash char(32) not null,
|
||||
filter_line num not null,
|
||||
group_logic char(3) not null,
|
||||
subgroup_logic char(3) not null,
|
||||
subgroup_id num not null,
|
||||
variable_nm varchar(32) not null,
|
||||
operator_nm varchar(12) not null,
|
||||
raw_value varchar(4000) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_hash,filter_line)
|
||||
);
|
||||
|
||||
%mend mddl_dc_filterdetail;/**
|
||||
@file
|
||||
@brief Filtersummary DDL
|
||||
@details For storing summary filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
filter_rk num not null,
|
||||
filter_hash char(32) not null,
|
||||
filter_table char(41) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_rk)
|
||||
);
|
||||
|
||||
%mend mddl_dc_filtersummary;/**
|
||||
@file
|
||||
@brief Locktable DDL
|
||||
@details For "locking" tables prior to multipass loads. Used by
|
||||
mp_lockanytable.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_ref char(200),
|
||||
lock_pid char(10),
|
||||
lock_start_dttm num format=E8601DT26.6,
|
||||
lock_end_dttm num format=E8601DT26.6,
|
||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds)
|
||||
);
|
||||
|
||||
%mend mddl_dc_locktable;/**
|
||||
@file
|
||||
@brief Maxkeytable DDL
|
||||
@details For storing the maximum retained key information. Used
|
||||
by mp_retainedkey.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_maxkeytable(libds=WORK.MAXKEYTABLE);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
keytable varchar(41) label='Base table in libref.dataset format',
|
||||
keycolumn char(32) format=$32.
|
||||
label='The Retained key field containing the key values.',
|
||||
max_key num label=
|
||||
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||
processed_dttm num format=E8601DT26.6
|
||||
label='Datetime this value was last updated',
|
||||
constraint pk_mpe_maxkeyvalues
|
||||
primary key(keytable));
|
||||
|
||||
%mend mddl_dc_maxkeytable;/**
|
||||
@file
|
||||
@brief The CNTLOUT table generated by proc format
|
||||
@details This table will actually change format depending on the data values,
|
||||
therefore the max possible lengths are described here to enable consistency
|
||||
when dealing with format data.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
FMTNAME char(32) label='Format name'
|
||||
,START char(16) label='Starting value for format'
|
||||
,END char(16) label='Ending value for format'
|
||||
,LABEL char(256) label='Format value label'
|
||||
,MIN num length=3 label='Minimum length'
|
||||
,MAX num length=3 label='Maximum length'
|
||||
,DEFAULT num length=3 label='Default length'
|
||||
,LENGTH num length=3 label='Format length'
|
||||
,FUZZ num label='Fuzz value'
|
||||
,PREFIX char(2) label='Prefix characters'
|
||||
,MULT num label='Multiplier'
|
||||
,FILL char(1) label='Fill character'
|
||||
,NOEDIT num length=3 label='Is picture string noedit?'
|
||||
,TYPE char(1) label='Type of format'
|
||||
,SEXCL char(1) label='Start exclusion'
|
||||
,EEXCL char(1) label='End exclusion'
|
||||
,HLO char(13) label='Additional information'
|
||||
,DECSEP char(1) label='Decimal separator'
|
||||
,DIG3SEP char(1) label='Three-digit separator'
|
||||
,DATATYPE char(8) label='Date/time/datetime?'
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
|
||||
%mend mddl_sas_cntlout;/**
|
||||
@file mm_adduser2group.sas
|
||||
@brief Adds a user to a group
|
||||
@details Adds a user to a metadata group. The macro first checks whether the
|
||||
@@ -12576,7 +12768,10 @@ run;
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(tree name)
|
||||
%mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: tree name)
|
||||
)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
@@ -12800,7 +12995,10 @@ run;
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(tree name)
|
||||
%mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: tree name)
|
||||
)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
@@ -13169,12 +13367,14 @@ run;
|
||||
filename &frefin temp;
|
||||
filename &frefout temp;
|
||||
|
||||
%mp_abort(iftrue= (
|
||||
&engine=BASE & %mf_verifymacvars(libname libref engine servercontext tree)=0
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: libname libref engine servercontext tree)
|
||||
)
|
||||
|
||||
%if &engine=BASE %then %do;
|
||||
|
||||
%mf_verifymacvars(libname libref engine servercontext tree)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the ServerContext exists
|
||||
*/
|
||||
@@ -13465,7 +13665,12 @@ filename &frefout temp;
|
||||
%&mD.put Executing mm_CreateSTP.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(stpname filename directory tree)
|
||||
%mp_abort(
|
||||
iftrue=(%mf_verifymacvars(stpname filename directory tree)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: stpname filename directory tree)
|
||||
)
|
||||
|
||||
%mp_dropmembers(%scan(&outds,2,.))
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
@li /data
|
||||
@li /jobs
|
||||
@li /services
|
||||
@li /tests
|
||||
@li /tests/jobs
|
||||
@li /tests/services
|
||||
@li /tests/macros
|
||||
@@ -46,9 +47,13 @@
|
||||
|
||||
/**
|
||||
* First check we are not in the tests/macros folder (which has no subfolders)
|
||||
* or specifically in the testsetup or testteardown services
|
||||
*/
|
||||
%if %index(&pgm,/tests/macros/) %then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests/macros)-1);
|
||||
%if %index(&pgm,/tests/macros/)
|
||||
or %index(&pgm,/tests/testsetup)
|
||||
or %index(&pgm,/tests/testteardown)
|
||||
%then %do;
|
||||
%let root=%substr(&pgm,1,%index(&pgm,/tests)-1);
|
||||
&root
|
||||
%return;
|
||||
%end;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
@file
|
||||
@brief Checks if a set of macro variables exist / contain values.
|
||||
@brief Checks if a set of macro variables exist AND contain values.
|
||||
@details Writes ERROR to log if abortType is SOFT, else will call %mf_abort.
|
||||
Usage:
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_abort.sas
|
||||
|
||||
@param verifyvars space separated list of macro variable names
|
||||
@param makeupcase= set to YES to convert all variable VALUES to
|
||||
@param [in] verifyvars Space separated list of macro variable names
|
||||
@param [in] makeupcase= (NO) Set to YES to convert all variable VALUES to
|
||||
uppercase.
|
||||
@param mAbort= Abort Type. Default is SOFT (writes err to log).
|
||||
@param [in] mAbort= (SOFT) Abort Type. When SOFT, simply writes an err
|
||||
message to the log.
|
||||
Set to any other value to call mf_abort (which can be configured to abort in
|
||||
various fashions according to context).
|
||||
|
||||
@@ -58,8 +59,14 @@
|
||||
|
||||
%goto exit_success;
|
||||
%exit_err:
|
||||
%if &mAbort=SOFT %then %put %str(ERR)OR: &abortmsg;
|
||||
%else %mf_abort(mac=mf_verifymacvars,type=&mabort,msg=&abortmsg);
|
||||
%put %str(ERR)OR: &abortmsg;
|
||||
%mf_abort(iftrue=(&mabort ne SOFT),
|
||||
mac=mf_verifymacvars,
|
||||
msg=%str(&abortmsg)
|
||||
)
|
||||
0
|
||||
%return;
|
||||
%exit_success:
|
||||
1
|
||||
|
||||
%mend mf_verifymacvars;
|
||||
|
||||
@@ -5,24 +5,32 @@
|
||||
make use of permanent tables. To avoid duplication in definitions, this
|
||||
macro provides a central location for managing the corresponding DDL.
|
||||
|
||||
Note - this macro is likely to be deprecated in future in favour of a
|
||||
dedicated "datamodel" folder (prefix mddl)
|
||||
|
||||
Any corresponding data would go in a seperate repo, to avoid this one
|
||||
ballooning in size!
|
||||
|
||||
Example usage:
|
||||
|
||||
%mp_coretable(LOCKTABLE,libds=work.locktable)
|
||||
|
||||
@param [in] table_ref The type of table to create. Example values:
|
||||
@li DIFFTABLE - Used to store changes to tables. Used by mp_storediffs.sas
|
||||
and mp_stackdiffs.sas
|
||||
@li FILTER_DETAIL - For storing detailed filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
@li FILTER_SUMMARY - For storing summary filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
@li LOCKANYTABLE - For "locking" tables prior to multipass loads. Used by
|
||||
mp_lockanytable.sas
|
||||
@li MAXKEYTABLE - For storing the maximum retained key information. Used
|
||||
by mp_retainedkey.sas
|
||||
@li DIFFTABLE
|
||||
@li FILTER_DETAIL
|
||||
@li FILTER_SUMMARY
|
||||
@li LOCKANYTABLE
|
||||
@li MAXKEYTABLE
|
||||
@param [in] libds= (0) The library.dataset reference used to create the table.
|
||||
If not provided, then the DDL is simply printed to the log.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_difftable.sas
|
||||
@li mddl_dc_filterdetail.sas
|
||||
@li mddl_dc_filtersummary.sas
|
||||
@li mddl_dc_locktable.sas
|
||||
@li mddl_dc_maxkeytable.sas
|
||||
|
||||
<h4> Related Macros </h4>
|
||||
@li mp_filterstore.sas
|
||||
@li mp_lockanytable.sas
|
||||
@@ -41,76 +49,21 @@
|
||||
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
|
||||
proc sql;
|
||||
%if &table_ref=DIFFTABLE %then %do;
|
||||
create table &outds(
|
||||
load_ref char(36) label='unique load reference',
|
||||
processed_dttm num format=E8601DT26.6 label='Processed at timestamp',
|
||||
libref char(8) label='Library Reference (8 chars)',
|
||||
dsn char(32) label='Dataset Name (32 chars)',
|
||||
key_hash char(32) label=
|
||||
'MD5 Hash of primary key values (pipe seperated)',
|
||||
move_type char(1) label='Either (A)ppended, (D)eleted or (M)odified',
|
||||
is_pk num label='Is Primary Key Field? (1/0)',
|
||||
is_diff num label=
|
||||
'Did value change? (1/0/-1). Always -1 for appends and deletes.',
|
||||
tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
|
||||
tgtvar_nm char(32) label='Target variable name (32 chars)',
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
);
|
||||
%mddl_dc_difftable(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=LOCKTABLE %then %do;
|
||||
create table &outds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_ref char(200),
|
||||
lock_pid char(10),
|
||||
lock_start_dttm num format=E8601DT26.6,
|
||||
lock_end_dttm num format=E8601DT26.6,
|
||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds));
|
||||
%mddl_dc_locktable(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=FILTER_SUMMARY %then %do;
|
||||
create table &outds(
|
||||
filter_rk num not null,
|
||||
filter_hash char(32) not null,
|
||||
filter_table char(41) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_rk));
|
||||
%mddl_dc_filtersummary(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=FILTER_DETAIL %then %do;
|
||||
create table &outds(
|
||||
filter_hash char(32) not null,
|
||||
filter_line num not null,
|
||||
group_logic char(3) not null,
|
||||
subgroup_logic char(3) not null,
|
||||
subgroup_id num not null,
|
||||
variable_nm varchar(32) not null,
|
||||
operator_nm varchar(12) not null,
|
||||
raw_value varchar(4000) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_hash,filter_line));
|
||||
%mddl_dc_filterdetail(libds=&outds)
|
||||
%end;
|
||||
%else %if &table_ref=MAXKEYTABLE %then %do;
|
||||
create table &outds(
|
||||
keytable varchar(41) label='Base table in libref.dataset format',
|
||||
keycolumn char(32) format=$32.
|
||||
label='The Retained key field containing the key values.',
|
||||
max_key num label=
|
||||
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||
processed_dttm num format=E8601DT26.6
|
||||
label='Datetime this value was last updated',
|
||||
constraint pk_mpe_maxkeyvalues
|
||||
primary key(keytable));
|
||||
%mddl_dc_maxkeytable(libds=&outds)
|
||||
%end;
|
||||
|
||||
|
||||
%if &libds=0 %then %do;
|
||||
describe table &syslast;
|
||||
drop table &syslast;
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
numbers).
|
||||
@param [out] outfile= The output filename - should be quoted.
|
||||
@param [out] outref= (0) The output fileref (takes precedence if provided)
|
||||
@param [in] outencoding= (0) The output encoding to use (unquoted)
|
||||
@param [in] outencoding= (0) The (quoted) output encoding to use, eg `"UTF-8"`
|
||||
@param [in] termstr= (CRLF) The line seperator to use. For SASJS, will
|
||||
always be CRLF. Valid values:
|
||||
@li CRLF
|
||||
@@ -85,7 +85,7 @@
|
||||
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
|
||||
|
||||
%if &outencoding=0 %then %let outencoding=;
|
||||
%else %let outencoding=encoding="&outencoding";
|
||||
%else %let outencoding=encoding=&outencoding;
|
||||
|
||||
%if &outref=0 %then %let outloc=&outfile;
|
||||
%else %let outloc=&outref;
|
||||
|
||||
30
base/mp_ds2ddl.sas
Normal file
30
base/mp_ds2ddl.sas
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
@file
|
||||
@brief A wrapper for mp_getddl.sas
|
||||
@details In the next release, this will be the main version.
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_getddl.sas
|
||||
|
||||
**/
|
||||
|
||||
%macro mp_ds2ddl(libds,fref=getddl,flavour=SAS,showlog=YES,schema=
|
||||
,applydttm=NO
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%local libref;
|
||||
%let libds=%upcase(&libds);
|
||||
%let libref=%scan(&libds,1,.);
|
||||
%if &libref=&libds %then %let libds=WORK.&libds;
|
||||
|
||||
%mp_getddl(%scan(&libds,1,.)
|
||||
,%scan(&libds,2,.)
|
||||
,fref=&fref
|
||||
,flavour=SAS
|
||||
,showlog=&showlog
|
||||
,schema=&schema
|
||||
,applydttm=&applydttm
|
||||
)
|
||||
|
||||
%mend mp_ds2ddl;
|
||||
@@ -6,7 +6,8 @@
|
||||
SYSCC to 1008 if bad records are found, and call mp_abort.sas for a
|
||||
graceful service exit (configurable).
|
||||
|
||||
Used for dynamic filtering in [Data Controller for SAS®](https://datacontroller.io).
|
||||
Used for dynamic filtering in [Data Controller for SAS®](
|
||||
https://datacontroller.io).
|
||||
|
||||
Usage:
|
||||
|
||||
@@ -125,7 +126,7 @@ data &outds;
|
||||
output;
|
||||
end;
|
||||
if OPERATOR_NM not in
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS')
|
||||
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
|
||||
then do;
|
||||
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
|
||||
putlog REASON_CD= OPERATOR_NM=;
|
||||
|
||||
@@ -8,8 +8,13 @@
|
||||
https://sasapps.io)). This macro is also used in [Data Controller for SAS](
|
||||
https://datacontroller.io).
|
||||
|
||||
A more recent feature of this macro is the ability to support filter queries
|
||||
on Format Catalogs. This is achieved by adding a `-FC` suffix to the `libds`
|
||||
parameter - where the "ds" in this case is the catalog name.
|
||||
|
||||
@param [in] libds= The target dataset to be filtered (lib should be assigned)
|
||||
|
||||
@param [in] libds= The target dataset to be filtered (lib should be assigned).
|
||||
If filtering a format catalog, add the following suffix: `-FC`.
|
||||
@param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
|
||||
be validated. Has the following format:
|
||||
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
|
||||
@@ -45,6 +50,7 @@
|
||||
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_sas_cntlout.sas
|
||||
@li mf_getuniquename.sas
|
||||
@li mf_getvalue.sas
|
||||
@li mf_islibds.sas
|
||||
@@ -78,7 +84,10 @@
|
||||
%put &sysmacroname entry vars:;
|
||||
%put _local_;
|
||||
|
||||
%local ds1 ds2 ds3 ds4 filter_hash;
|
||||
%local ds0 ds1 ds2 ds3 ds4 filter_hash orig_libds;
|
||||
%let libds=%upcase(&libds);
|
||||
%let orig_libds=&libds;
|
||||
|
||||
%mp_abort(iftrue= (&syscc ne 0)
|
||||
,mac=mp_filterstore
|
||||
,msg=%str(syscc=&syscc on macro entry)
|
||||
@@ -96,12 +105,27 @@
|
||||
,msg=%str(Invalid lock_table value: &lock_table)
|
||||
)
|
||||
|
||||
/* validate query */
|
||||
/**
|
||||
* validate query
|
||||
* use format catalog export, if a format
|
||||
*/
|
||||
%if "%substr(&libds,%length(&libds)-2,3)"="-FC" %then %do;
|
||||
%let libds=%scan(&libds,1,-); /* chop off -FC extension */
|
||||
%let ds0=%mf_getuniquename(prefix=fmtds_);
|
||||
%let libds=&ds0;
|
||||
/*
|
||||
There is no need to export the entire format catalog here - the validations
|
||||
are done against the data model, not the data values. So we can simply
|
||||
hardcode the structure based on the cntlout dataset.
|
||||
*/
|
||||
%mddl_sas_cntlout(libds=&ds0)
|
||||
|
||||
%end;
|
||||
%mp_filtercheck(&queryds,targetds=&libds,abort=YES)
|
||||
|
||||
/* hash the result */
|
||||
%let ds1=%mf_getuniquename(prefix=hashds);
|
||||
%mp_hashdataset(&queryds,outds=&ds1,salt=&libds)
|
||||
%mp_hashdataset(&queryds,outds=&ds1,salt=&orig_libds)
|
||||
%let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
|
||||
%if &mdebug=1 %then %do;
|
||||
data _null_;
|
||||
@@ -132,7 +156,7 @@ run;
|
||||
%let ds3=%mf_getuniquename(prefix=filtersum);
|
||||
data work.&ds3;
|
||||
if 0 then set &filter_summary;
|
||||
filter_table=symget('libds');
|
||||
filter_table="&orig_libds";
|
||||
filter_hash="&filter_hash";
|
||||
PROCESSED_DTTM=%sysfunc(datetime());
|
||||
output;
|
||||
|
||||
@@ -40,6 +40,7 @@ https://support.sas.com/documentation/cdl/en/proc/61895/HTML/default/viewer.htm#
|
||||
|`WHICHPATH `|`**OTHER** `|`**OTHER** `|`big fat problem if not path1 `|`1 `|`40 `|`28 `|`28 `|`1E-12 `|` `|`0 `|` `|`0 `|`N `|`N `|`N `|`O `|` `|` `|` `|` `|
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_sas_cntlout.sas
|
||||
@li mf_dedup.sas
|
||||
@li mf_getfmtlist.sas
|
||||
@li mf_getfmtname.sas
|
||||
@@ -94,30 +95,7 @@ create table &outsummary as
|
||||
|
||||
%if "&outdetail" ne "0" %then %do;
|
||||
/* ensure base table always exists */
|
||||
proc sql;
|
||||
create table &outdetail(
|
||||
FMTNAME char(32) label='Format name'
|
||||
,START char(16) label='Starting value for format'
|
||||
,END char(16) label='Ending value for format'
|
||||
,LABEL char(256) label='Format value label'
|
||||
,MIN num length=3 label='Minimum length'
|
||||
,MAX num length=3 label='Maximum length'
|
||||
,DEFAULT num length=3 label='Default length'
|
||||
,LENGTH num length=3 label='Format length'
|
||||
,FUZZ num label='Fuzz value'
|
||||
,PREFIX char(2) label='Prefix characters'
|
||||
,MULT num label='Multiplier'
|
||||
,FILL char(1) label='Fill character'
|
||||
,NOEDIT num length=3 label='Is picture string noedit?'
|
||||
,TYPE char(1) label='Type of format'
|
||||
,SEXCL char(1) label='Start exclusion'
|
||||
,EEXCL char(1) label='End exclusion'
|
||||
,HLO char(13) label='Additional information'
|
||||
,DECSEP char(1) label='Decimal separator'
|
||||
,DIG3SEP char(1) label='Three-digit separator'
|
||||
,DATATYPE char(8) label='Date/time/datetime?'
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
%mddl_sas_cntlout(libds=&outdetail)
|
||||
/* grab the location of each format */
|
||||
%let fmtcnt=0;
|
||||
data _null_;
|
||||
@@ -134,6 +112,10 @@ create table &outsummary as
|
||||
proc format library=&&fmtloc&i CNTLOUT=&tempds;
|
||||
select &&fmtname&i;
|
||||
run;
|
||||
data &tempds;
|
||||
length label $256;
|
||||
set &tempds;
|
||||
run;
|
||||
proc append base=&outdetail data=&tempds;
|
||||
run;
|
||||
%end;
|
||||
|
||||
@@ -12,17 +12,19 @@
|
||||
|
||||
%mp_streamfile(contenttype=csv,inloc=/some/where.txt,outname=myfile.txt)
|
||||
|
||||
@param [in] contenttype= (TEXTS) Either TEXT, ZIP, CSV, EXCEL
|
||||
@param [in] inloc= /path/to/file.ext to be sent
|
||||
@param [in] inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||
@param [in] iftrue= (1=1) Provide a condition under which to execute.
|
||||
@param [out] outname= the name of the file, as downloaded by the browser
|
||||
@param [out] outref= (_webout) The destination where the file should be
|
||||
streamed.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_getplatform.sas
|
||||
@li mp_binarycopy.sas
|
||||
|
||||
@param contenttype= Either TEXT, ZIP, CSV, EXCEL (default TEXT)
|
||||
@param inloc= /path/to/file.ext to be sent
|
||||
@param inref= fileref of file to be sent (if provided, overrides `inloc`)
|
||||
@param outname= the name of the file, as downloaded by the browser
|
||||
|
||||
@author Allan Bowe
|
||||
@source https://github.com/sasjs/core
|
||||
|
||||
**/
|
||||
|
||||
@@ -30,12 +32,16 @@
|
||||
contenttype=TEXT
|
||||
,inloc=
|
||||
,inref=0
|
||||
,iftrue=%str(1=1)
|
||||
,outname=
|
||||
,outref=_webout
|
||||
)/*/STORE SOURCE*/;
|
||||
|
||||
%let contentype=%upcase(&contenttype);
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
%if not(%eval(%unquote(&iftrue))) %then %return;
|
||||
|
||||
%let contentype=%upcase(&contenttype);
|
||||
%let outref=%upcase(&outref);
|
||||
%local platform; %let platform=%mf_getplatform();
|
||||
|
||||
/**
|
||||
* check engine type to avoid the below err message:
|
||||
@@ -44,20 +50,20 @@
|
||||
%local streamweb;
|
||||
%let streamweb=0;
|
||||
data _null_;
|
||||
set sashelp.vextfl(where=(upcase(fileref)="_WEBOUT"));
|
||||
set sashelp.vextfl(where=(upcase(fileref)="&outref"));
|
||||
if xengine='STREAM' then call symputx('streamweb',1,'l');
|
||||
run;
|
||||
|
||||
%if &contentype=ZIP %then %do;
|
||||
%if &contentype=CSV %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
|
||||
contenttype='application/zip'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/csv'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
@@ -70,11 +76,52 @@ run;
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
contenttype='application/vnd.ms-excel'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="image/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/text'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
|
||||
contenttype="font/%lowcase(&contenttype)";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=XLSX %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
@@ -84,54 +131,34 @@ run;
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.xls'
|
||||
contenttype=
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=TEXT %then %do;
|
||||
%else %if &contentype=ZIP %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/text');
|
||||
rc=stpsrv_header('Content-type','application/zip');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/text'
|
||||
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.zip'
|
||||
contenttype='application/zip'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=CSV %then %do;
|
||||
%if &platform=SASMETA and &streamweb=1 %then %do;
|
||||
data _null_;
|
||||
rc=stpsrv_header('Content-type','application/csv');
|
||||
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
|
||||
run;
|
||||
%end;
|
||||
%else %if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name='_webout.txt'
|
||||
contenttype='application/csv'
|
||||
contentdisp="attachment; filename=&outname";
|
||||
%end;
|
||||
%end;
|
||||
%else %if &contentype=HTML %then %do;
|
||||
%if &platform=SASVIYA %then %do;
|
||||
filename _webout filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
|
||||
contenttype="text/html";
|
||||
%end;
|
||||
%end;
|
||||
%else %do;
|
||||
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
|
||||
%return;
|
||||
%end;
|
||||
|
||||
%if &inref ne 0 %then %do;
|
||||
%mp_binarycopy(inref=&inref,outref=_webout)
|
||||
%mp_binarycopy(inref=&inref,outref=&outref)
|
||||
%end;
|
||||
%else %do;
|
||||
%mp_binarycopy(inloc="&inloc",outref=_webout)
|
||||
%mp_binarycopy(inloc="&inloc",outref=&outref)
|
||||
%end;
|
||||
|
||||
%mend mp_streamfile;
|
||||
|
||||
2
build.py
2
build.py
@@ -84,7 +84,7 @@ options noquotelenmax;
|
||||
"""
|
||||
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
|
||||
f.write(header)
|
||||
folders=['base','meta','metax','server','viya','lua','fcmp']
|
||||
folders=['base','ddl','meta','metax','server','viya','lua','fcmp']
|
||||
for folder in folders:
|
||||
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
|
||||
filenames.sort()
|
||||
|
||||
34
ddl/mddl_dc_difftable.sas
Normal file
34
ddl/mddl_dc_difftable.sas
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
@file
|
||||
@brief Difftable DDL
|
||||
@details Used to store changes to tables. Used by mp_storediffs.sas
|
||||
and mp_stackdiffs.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_difftable(libds=WORK.DIFFTABLE);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
load_ref char(36) label='unique load reference',
|
||||
processed_dttm num format=E8601DT26.6 label='Processed at timestamp',
|
||||
libref char(8) label='Library Reference (8 chars)',
|
||||
dsn char(32) label='Dataset Name (32 chars)',
|
||||
key_hash char(32) label=
|
||||
'MD5 Hash of primary key values (pipe seperated)',
|
||||
move_type char(1) label='Either (A)ppended, (D)eleted or (M)odified',
|
||||
is_pk num label='Is Primary Key Field? (1/0)',
|
||||
is_diff num label=
|
||||
'Did value change? (1/0/-1). Always -1 for appends and deletes.',
|
||||
tgtvar_type char(1) label='Either (C)haracter or (N)umeric',
|
||||
tgtvar_nm char(32) label='Target variable name (32 chars)',
|
||||
oldval_num num format=best32. label='Old (numeric) value',
|
||||
newval_num num format=best32. label='New (numeric) value',
|
||||
oldval_char char(32765) label='Old (character) value',
|
||||
newval_char char(32765) label='New (character) value',
|
||||
constraint pk_mpe_audit
|
||||
primary key(load_ref,libref,dsn,key_hash,tgtvar_nm)
|
||||
);
|
||||
|
||||
%mend mddl_dc_difftable;
|
||||
27
ddl/mddl_dc_filterdetail.sas
Normal file
27
ddl/mddl_dc_filterdetail.sas
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
@file
|
||||
@brief Filtertable DDL
|
||||
@details For storing detailed filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_filterdetail(libds=WORK.FILTER_DETAIL);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
filter_hash char(32) not null,
|
||||
filter_line num not null,
|
||||
group_logic char(3) not null,
|
||||
subgroup_logic char(3) not null,
|
||||
subgroup_id num not null,
|
||||
variable_nm varchar(32) not null,
|
||||
operator_nm varchar(12) not null,
|
||||
raw_value varchar(4000) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_hash,filter_line)
|
||||
);
|
||||
|
||||
%mend mddl_dc_filterdetail;
|
||||
22
ddl/mddl_dc_filtersummary.sas
Normal file
22
ddl/mddl_dc_filtersummary.sas
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
@file
|
||||
@brief Filtersummary DDL
|
||||
@details For storing summary filter values. Used by
|
||||
mp_filterstore.sas.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_filtersummary(libds=WORK.FILTER_SUMMARY);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
filter_rk num not null,
|
||||
filter_hash char(32) not null,
|
||||
filter_table char(41) not null,
|
||||
processed_dttm num not null format=E8601DT26.6,
|
||||
constraint pk_mpe_filteranytable
|
||||
primary key(filter_rk)
|
||||
);
|
||||
|
||||
%mend mddl_dc_filtersummary;
|
||||
25
ddl/mddl_dc_locktable.sas
Normal file
25
ddl/mddl_dc_locktable.sas
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
@file
|
||||
@brief Locktable DDL
|
||||
@details For "locking" tables prior to multipass loads. Used by
|
||||
mp_lockanytable.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_locktable(libds=WORK.LOCKTABLE);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
lock_lib char(8),
|
||||
lock_ds char(32),
|
||||
lock_status_cd char(10) not null,
|
||||
lock_user_nm char(100) not null ,
|
||||
lock_ref char(200),
|
||||
lock_pid char(10),
|
||||
lock_start_dttm num format=E8601DT26.6,
|
||||
lock_end_dttm num format=E8601DT26.6,
|
||||
constraint pk_mp_lockanytable primary key(lock_lib,lock_ds)
|
||||
);
|
||||
|
||||
%mend mddl_dc_locktable;
|
||||
24
ddl/mddl_dc_maxkeytable.sas
Normal file
24
ddl/mddl_dc_maxkeytable.sas
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
@file
|
||||
@brief Maxkeytable DDL
|
||||
@details For storing the maximum retained key information. Used
|
||||
by mp_retainedkey.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_dc_maxkeytable(libds=WORK.MAXKEYTABLE);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
keytable varchar(41) label='Base table in libref.dataset format',
|
||||
keycolumn char(32) format=$32.
|
||||
label='The Retained key field containing the key values.',
|
||||
max_key num label=
|
||||
'Integer representing current max RK or SK value in the KEYTABLE',
|
||||
processed_dttm num format=E8601DT26.6
|
||||
label='Datetime this value was last updated',
|
||||
constraint pk_mpe_maxkeyvalues
|
||||
primary key(keytable));
|
||||
|
||||
%mend mddl_dc_maxkeytable;
|
||||
38
ddl/mddl_sas_cntlout.sas
Normal file
38
ddl/mddl_sas_cntlout.sas
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
@file
|
||||
@brief The CNTLOUT table generated by proc format
|
||||
@details This table will actually change format depending on the data values,
|
||||
therefore the max possible lengths are described here to enable consistency
|
||||
when dealing with format data.
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%macro mddl_sas_cntlout(libds=WORK.CNTLOUT);
|
||||
|
||||
proc sql;
|
||||
create table &libds(
|
||||
FMTNAME char(32) label='Format name'
|
||||
,START char(16) label='Starting value for format'
|
||||
,END char(16) label='Ending value for format'
|
||||
,LABEL char(256) label='Format value label'
|
||||
,MIN num length=3 label='Minimum length'
|
||||
,MAX num length=3 label='Maximum length'
|
||||
,DEFAULT num length=3 label='Default length'
|
||||
,LENGTH num length=3 label='Format length'
|
||||
,FUZZ num label='Fuzz value'
|
||||
,PREFIX char(2) label='Prefix characters'
|
||||
,MULT num label='Multiplier'
|
||||
,FILL char(1) label='Fill character'
|
||||
,NOEDIT num length=3 label='Is picture string noedit?'
|
||||
,TYPE char(1) label='Type of format'
|
||||
,SEXCL char(1) label='Start exclusion'
|
||||
,EEXCL char(1) label='End exclusion'
|
||||
,HLO char(13) label='Additional information'
|
||||
,DECSEP char(1) label='Decimal separator'
|
||||
,DIG3SEP char(1) label='Three-digit separator'
|
||||
,DATATYPE char(8) label='Date/time/datetime?'
|
||||
,LANGUAGE char(8) label='Language for date strings'
|
||||
);
|
||||
|
||||
%mend mddl_sas_cntlout;
|
||||
@@ -59,7 +59,10 @@
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(tree name)
|
||||
%mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: tree name)
|
||||
)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
|
||||
@@ -47,7 +47,10 @@
|
||||
%&mD.put Executing &sysmacroname..sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(tree name)
|
||||
%mp_abort(iftrue= (%mf_verifymacvars(tree name)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: tree name)
|
||||
)
|
||||
|
||||
/**
|
||||
* check tree exists
|
||||
|
||||
@@ -133,12 +133,14 @@ run;
|
||||
filename &frefin temp;
|
||||
filename &frefout temp;
|
||||
|
||||
%mp_abort(iftrue= (
|
||||
&engine=BASE & %mf_verifymacvars(libname libref engine servercontext tree)=0
|
||||
)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: libname libref engine servercontext tree)
|
||||
)
|
||||
|
||||
%if &engine=BASE %then %do;
|
||||
|
||||
%mf_verifymacvars(libname libref engine servercontext tree)
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check that the ServerContext exists
|
||||
*/
|
||||
|
||||
@@ -108,7 +108,12 @@
|
||||
%&mD.put Executing mm_CreateSTP.sas;
|
||||
%&mD.put _local_;
|
||||
|
||||
%mf_verifymacvars(stpname filename directory tree)
|
||||
%mp_abort(
|
||||
iftrue=(%mf_verifymacvars(stpname filename directory tree)=0)
|
||||
,mac=&sysmacroname
|
||||
,msg=%str(Empty inputs: stpname filename directory tree)
|
||||
)
|
||||
|
||||
%mp_dropmembers(%scan(&outds,2,.))
|
||||
|
||||
/**
|
||||
|
||||
432
package-lock.json
generated
432
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@
|
||||
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sasjs/cli": "3.6.0"
|
||||
"@sasjs/cli": "3.6.0",
|
||||
"@sasjs/core": "4.4.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
"$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json",
|
||||
"macroFolders": [
|
||||
"base",
|
||||
"ddl",
|
||||
"fcmp",
|
||||
"meta",
|
||||
"metax",
|
||||
"server",
|
||||
"viya",
|
||||
"lua",
|
||||
"tests/crossplatform"
|
||||
"tests/crossplatform",
|
||||
"tests/ddl"
|
||||
],
|
||||
"docConfig": {
|
||||
"displayMacroCore": false,
|
||||
@@ -59,6 +61,7 @@
|
||||
"tests/sas9only"
|
||||
],
|
||||
"programFolders": [],
|
||||
"binaryFolders": [],
|
||||
"deployConfig": {
|
||||
"deployServicePack": true,
|
||||
"deployScripts": []
|
||||
|
||||
@@ -46,4 +46,20 @@
|
||||
),
|
||||
desc=Checking tests/macros appLoc matches (which has no subfolder),
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
"%mf_getapploc(/some/area/tests/testsetup)"="/some/area"
|
||||
),
|
||||
desc=Checking tests/testsetup operation,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(
|
||||
"%mf_getapploc(/some/area/tests/testteardown)"="/some/area"
|
||||
),
|
||||
desc=Checking tests/teardown operation,
|
||||
outds=work.test_results
|
||||
)
|
||||
22
tests/crossplatform/mf_verifymacvars.test.sas
Normal file
22
tests/crossplatform/mf_verifymacvars.test.sas
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mf_verifymacvars macro
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mf_verifymacvars.sas
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
|
||||
**/
|
||||
|
||||
%let var1=x;
|
||||
%let var2=y;
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_assert(
|
||||
iftrue=(%mf_verifymacvars(var1 var2)=1),
|
||||
desc=Checking macvars exist,
|
||||
outds=work.test_results
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
112
tests/crossplatform/mp_filterstore.test.2.sas
Normal file
112
tests/crossplatform/mp_filterstore.test.2.sas
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_filterstore macro with a format catalog
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_assert.sas
|
||||
@li mp_assertdsobs.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mp_coretable.sas
|
||||
@li mp_filterstore.sas
|
||||
|
||||
**/
|
||||
|
||||
libname permlib (work);
|
||||
|
||||
%mp_coretable(LOCKTABLE,libds=permlib.locktable)
|
||||
%mp_coretable(FILTER_SUMMARY,libds=permlib.filtsum)
|
||||
%mp_coretable(FILTER_DETAIL,libds=permlib.filtdet)
|
||||
%mp_coretable(MAXKEYTABLE,libds=permlib.maxkey)
|
||||
|
||||
/* valid filter */
|
||||
data work.inds;
|
||||
infile datalines4 dsd;
|
||||
input GROUP_LOGIC:$3. SUBGROUP_LOGIC:$3. SUBGROUP_ID:8. VARIABLE_NM:$32.
|
||||
OPERATOR_NM:$12. RAW_VALUE:$4000.;
|
||||
datalines4;
|
||||
AND,AND,1,Start,>,"'2'"
|
||||
AND,AND,1,Fmtname,=,"'MORDOR'"
|
||||
OR,OR,2,Label,IN,"('Dragon1','Dragon2')"
|
||||
OR,OR,2,End,=,"'6'"
|
||||
OR,OR,2,Start,GE,"'10'"
|
||||
;;;;
|
||||
run;
|
||||
|
||||
/* make some formats */
|
||||
PROC FORMAT library=permlib.testfmts;
|
||||
picture MyMSdt other='%0Y-%0m-%0dT%0H:%0M:%0S' (datatype=datetime);
|
||||
RUN;
|
||||
data work.fmts;
|
||||
length fmtname $32;
|
||||
do fmtname='SMAUG','MORDOR','GOLLUM';
|
||||
do start=1 to 10;
|
||||
label= cats('Dragon ',start);
|
||||
output;
|
||||
end;
|
||||
end;
|
||||
run;
|
||||
proc sort data=work.fmts nodupkey;
|
||||
by fmtname start;
|
||||
run;
|
||||
proc format cntlin=work.fmts library=permlib.testfmts;
|
||||
run;
|
||||
proc format library=permlib.testfmts;
|
||||
invalue indays (default=13) other=42;
|
||||
run;
|
||||
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
%mp_filterstore(libds=permlib.testfmts-fc,
|
||||
queryds=work.inds,
|
||||
filter_summary=permlib.filtsum,
|
||||
filter_detail=permlib.filtdet,
|
||||
lock_table=permlib.locktable,
|
||||
maxkeytable=permlib.maxkey,
|
||||
outresult=work.result,
|
||||
outquery=work.query,
|
||||
mdebug=1
|
||||
)
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(iftrue=(&syscc=0),
|
||||
desc=Ensure macro runs without errors,
|
||||
outds=work.test_results
|
||||
)
|
||||
/* ensure only one record created */
|
||||
%mp_assertdsobs(permlib.filtsum,
|
||||
desc=Initial query,
|
||||
test=ATMOST 1,
|
||||
outds=work.test_results
|
||||
)
|
||||
/* check RK is correct */
|
||||
proc sql noprint;
|
||||
select max(filter_rk) into: test1 from work.result;
|
||||
%mp_assert(iftrue=(&test1=1),
|
||||
desc=Ensure filter rk is correct,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
/* Test 2 - load same table again and ensure we get the same RK */
|
||||
%mp_filterstore(libds=permlib.testfmts-fc,
|
||||
queryds=work.inds,
|
||||
filter_summary=permlib.filtsum,
|
||||
filter_detail=permlib.filtdet,
|
||||
lock_table=permlib.locktable,
|
||||
maxkeytable=permlib.maxkey,
|
||||
outresult=work.result,
|
||||
outquery=work.query,
|
||||
mdebug=1
|
||||
)
|
||||
/* ensure only one record created */
|
||||
%mp_assertdsobs(permlib.filtsum,
|
||||
desc=Initial query - same obs,
|
||||
test=ATMOST 1,
|
||||
outds=work.test_results
|
||||
)
|
||||
/* check RK is correct */
|
||||
proc sql noprint;
|
||||
select max(filter_rk) into: test2 from work.result;
|
||||
%mp_assert(iftrue=(&test2=1),
|
||||
desc=Ensure filter rk is correct for second run,
|
||||
outds=work.test_results
|
||||
)
|
||||
28
tests/crossplatform/mp_streamfile.test.sas
Normal file
28
tests/crossplatform/mp_streamfile.test.sas
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
@file
|
||||
@brief Testing mp_streamfile.sas macro
|
||||
@details This is tricky to test as it streams to webout. For now just
|
||||
check the compilation, and for macro leakage.
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_assert.sas
|
||||
@li mp_assertscope.sas
|
||||
@li mp_streamfile.sas
|
||||
|
||||
**/
|
||||
|
||||
%mp_assertscope(SNAPSHOT)
|
||||
|
||||
%mp_streamfile(iftrue=(1=0)
|
||||
,contenttype=csv,inloc=/some/where.txt
|
||||
,outname=myfile.txt
|
||||
)
|
||||
|
||||
%mp_assertscope(COMPARE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking error condition,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
19
tests/ddl/mddl_dc_difftable.test.sas
Normal file
19
tests/ddl/mddl_dc_difftable.test.sas
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
@file
|
||||
@brief Difftable DDL test
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_difftable.sas
|
||||
@li mf_existds.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
|
||||
%mddl_dc_difftable(libds=WORK.DIFFTABLE)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(WORK.DIFFTABLE)=1),
|
||||
desc=Checking table was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
18
tests/ddl/mddl_dc_filterdetail.test.sas
Normal file
18
tests/ddl/mddl_dc_filterdetail.test.sas
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief Filtertable DDL test
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_filterdetail.sas
|
||||
@li mf_existds.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
%mddl_dc_filterdetail(libds=WORK.TEST)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(WORK.TEST)=1),
|
||||
desc=Checking table was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
18
tests/ddl/mddl_dc_filtersummary.test.sas
Normal file
18
tests/ddl/mddl_dc_filtersummary.test.sas
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief Filtersummary DDL test
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_filtersummary.sas
|
||||
@li mf_existds.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
%mddl_dc_filtersummary(libds=WORK.TEST)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(WORK.TEST)=1),
|
||||
desc=Checking table was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
18
tests/ddl/mddl_dc_locktable.test.sas
Normal file
18
tests/ddl/mddl_dc_locktable.test.sas
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief Locktable DDL test
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_locktable.sas
|
||||
@li mf_existds.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
%mddl_dc_locktable(libds=WORK.TEST)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(WORK.TEST)=1),
|
||||
desc=Checking table was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
18
tests/ddl/mddl_dc_maxkeytable.test.sas
Normal file
18
tests/ddl/mddl_dc_maxkeytable.test.sas
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief Maxkeytable DDL test
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_dc_maxkeytable.sas
|
||||
@li mf_existds.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
%mddl_dc_maxkeytable(libds=WORK.TEST)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(WORK.TEST)=1),
|
||||
desc=Checking table was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
18
tests/ddl/mddl_sas_cntlout.test.sas
Normal file
18
tests/ddl/mddl_sas_cntlout.test.sas
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
@file
|
||||
@brief mddl_sas_cntlout ddl test
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mddl_sas_cntlout.sas
|
||||
@li mf_existds.sas
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
%mddl_sas_cntlout(libds=WORK.TEST)
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(%mf_existds(WORK.TEST)=1),
|
||||
desc=Checking table was created,
|
||||
outds=work.test_results
|
||||
)
|
||||
@@ -2,8 +2,18 @@
|
||||
@file
|
||||
@brief term file for tests
|
||||
|
||||
<h4> SAS Macros </h4>
|
||||
@li mp_assert.sas
|
||||
|
||||
**/
|
||||
|
||||
%mp_assert(
|
||||
iftrue=(&syscc=0),
|
||||
desc=Checking final error condition,
|
||||
outds=work.test_results
|
||||
)
|
||||
|
||||
|
||||
%webout(OPEN)
|
||||
%webout(OBJ, TEST_RESULTS)
|
||||
%webout(CLOSE)
|
||||
@@ -76,3 +76,6 @@ run;
|
||||
iftrue=(&syscc ne 0),
|
||||
desc=Check that non zero return code is returned if called job fails
|
||||
)
|
||||
|
||||
/* set syscc to zero for final check in testterm */
|
||||
%let syscc=0;
|
||||
Reference in New Issue
Block a user