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

Compare commits

..

6 Commits

Author SHA1 Message Date
Allan Bowe
713f7544cd Merge pull request #167 from sasjs/issue166
feat: adding mddl_xx series of macros (and tests).  Closes #166
2022-02-07 15:42:01 +02:00
munja
de4e96ab01 fix: dependency under wrong header in mp_getformats 2022-02-07 14:41:32 +01:00
munja
3e7b15c7db feat: adding mddl_xx series of macros (and tests). Closes #166 2022-02-07 14:14:25 +01:00
Allan Bowe
eb2ccfbbca Merge pull request #165 from sasjs/fmtstore
feat: adding format catalog capability to mp_filterstore
2022-02-07 00:02:34 +02:00
munja
70e508e583 fix: failing test 2022-02-06 23:01:46 +01:00
munja
8b0acf2eae feat: adding format catalog capability to mp_filterstore 2022-02-06 22:12:00 +01:00
25 changed files with 767 additions and 219 deletions

View File

@@ -31,19 +31,28 @@ Documentation: https://core.sasjs.io
## Components ## Components
### BASE library (SAS9/Viya) ### BASE library (All Platforms)
- OS independent - OS independent
- Not metadata aware - Works on all SAS Platforms
- No X command - No X command
- Prefixes: _mf_, _mp_ - 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 - 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 The fcmp macros are used to generate fcmp functions, and can be used with or without the `proc fcmp` wrapper.
without the `proc fcmp` wrapper.
### META library (SAS9 only) ### META library (SAS9 only)
@@ -125,6 +134,7 @@ filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
- one macro per file - one macro per file
- prefixes: - prefixes:
- _mcf_ for macro compiled functions (proc fcmp) - _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). - _mf_ for macro functions (can be used in open code).
- _ml_ for macros that are used to compile LUA modules - _ml_ for macros that are used to compile LUA modules
- _mm_ for metadata macros (interface with the metadata server). - _mm_ for metadata macros (interface with the metadata server).
@@ -192,11 +202,13 @@ When contributing to this library, it is therefore important to ensure that all
## Breaking Changes ## 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) * 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) * `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 ## Star Gazing

355
all.sas
View File

@@ -3533,24 +3533,32 @@ run;
make use of permanent tables. To avoid duplication in definitions, this make use of permanent tables. To avoid duplication in definitions, this
macro provides a central location for managing the corresponding DDL. 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: Example usage:
%mp_coretable(LOCKTABLE,libds=work.locktable) %mp_coretable(LOCKTABLE,libds=work.locktable)
@param [in] table_ref The type of table to create. Example values: @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 @li DIFFTABLE
and mp_stackdiffs.sas @li FILTER_DETAIL
@li FILTER_DETAIL - For storing detailed filter values. Used by @li FILTER_SUMMARY
mp_filterstore.sas. @li LOCKANYTABLE
@li FILTER_SUMMARY - For storing summary filter values. Used by @li MAXKEYTABLE
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
@param [in] libds= (0) The library.dataset reference used to create the table. @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. 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> <h4> Related Macros </h4>
@li mp_filterstore.sas @li mp_filterstore.sas
@li mp_lockanytable.sas @li mp_lockanytable.sas
@@ -3569,76 +3577,21 @@ run;
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds)); %let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
proc sql; proc sql;
%if &table_ref=DIFFTABLE %then %do; %if &table_ref=DIFFTABLE %then %do;
create table &outds( %mddl_dc_difftable(libds=&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)
);
%end; %end;
%else %if &table_ref=LOCKTABLE %then %do; %else %if &table_ref=LOCKTABLE %then %do;
create table &outds( %mddl_dc_locktable(libds=&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));
%end; %end;
%else %if &table_ref=FILTER_SUMMARY %then %do; %else %if &table_ref=FILTER_SUMMARY %then %do;
create table &outds( %mddl_dc_filtersummary(libds=&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));
%end; %end;
%else %if &table_ref=FILTER_DETAIL %then %do; %else %if &table_ref=FILTER_DETAIL %then %do;
create table &outds( %mddl_dc_filterdetail(libds=&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));
%end; %end;
%else %if &table_ref=MAXKEYTABLE %then %do; %else %if &table_ref=MAXKEYTABLE %then %do;
create table &outds( %mddl_dc_maxkeytable(libds=&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));
%end; %end;
%if &libds=0 %then %do; %if &libds=0 %then %do;
describe table &syslast; describe table &syslast;
drop table &syslast; drop table &syslast;
@@ -4888,6 +4841,35 @@ data _null_;
run; run;
%mend mp_ds2csv;/** %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 @file
@brief Converts every value in a dataset to formatted value @brief Converts every value in a dataset to formatted value
@details Converts every value to it's formatted value. All variables will @details Converts every value to it's formatted value. All variables will
@@ -5394,7 +5376,8 @@ options varlenchk=&optval;
SYSCC to 1008 if bad records are found, and call mp_abort.sas for a SYSCC to 1008 if bad records are found, and call mp_abort.sas for a
graceful service exit (configurable). graceful service exit (configurable).
Used for dynamic filtering in [Data Controller for SAS&reg;](https://datacontroller.io). Used for dynamic filtering in [Data Controller for SAS&reg;](
https://datacontroller.io).
Usage: Usage:
@@ -5513,7 +5496,7 @@ data &outds;
output; output;
end; end;
if OPERATOR_NM not in if OPERATOR_NM not in
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS') ('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
then do; then do;
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM); REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
putlog REASON_CD= OPERATOR_NM=; putlog REASON_CD= OPERATOR_NM=;
@@ -5701,8 +5684,13 @@ filename &outref temp;
https://sasapps.io)). This macro is also used in [Data Controller for SAS]( https://sasapps.io)). This macro is also used in [Data Controller for SAS](
https://datacontroller.io). 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 @param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
be validated. Has the following format: be validated. Has the following format:
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767| |GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767|
@@ -5738,6 +5726,7 @@ filename &outref temp;
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvalue.sas @li mf_getvalue.sas
@li mf_islibds.sas @li mf_islibds.sas
@@ -5771,7 +5760,10 @@ filename &outref temp;
%put &sysmacroname entry vars:; %put &sysmacroname entry vars:;
%put _local_; %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) %mp_abort(iftrue= (&syscc ne 0)
,mac=mp_filterstore ,mac=mp_filterstore
,msg=%str(syscc=&syscc on macro entry) ,msg=%str(syscc=&syscc on macro entry)
@@ -5789,12 +5781,27 @@ filename &outref temp;
,msg=%str(Invalid lock_table value: &lock_table) ,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) %mp_filtercheck(&queryds,targetds=&libds,abort=YES)
/* hash the result */ /* hash the result */
%let ds1=%mf_getuniquename(prefix=hashds); %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)); %let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
%if &mdebug=1 %then %do; %if &mdebug=1 %then %do;
data _null_; data _null_;
@@ -5825,7 +5832,7 @@ run;
%let ds3=%mf_getuniquename(prefix=filtersum); %let ds3=%mf_getuniquename(prefix=filtersum);
data work.&ds3; data work.&ds3;
if 0 then set &filter_summary; if 0 then set &filter_summary;
filter_table=symget('libds'); filter_table="&orig_libds";
filter_hash="&filter_hash"; filter_hash="&filter_hash";
PROCESSED_DTTM=%sysfunc(datetime()); PROCESSED_DTTM=%sysfunc(datetime());
output; output;
@@ -6961,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 `|` `|` `|` `|` `| |`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> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_dedup.sas @li mf_dedup.sas
@li mf_getfmtlist.sas @li mf_getfmtlist.sas
@li mf_getfmtname.sas @li mf_getfmtname.sas
@@ -7015,30 +7023,7 @@ create table &outsummary as
%if "&outdetail" ne "0" %then %do; %if "&outdetail" ne "0" %then %do;
/* ensure base table always exists */ /* ensure base table always exists */
proc sql; %mddl_sas_cntlout(libds=&outdetail)
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'
);
/* grab the location of each format */ /* grab the location of each format */
%let fmtcnt=0; %let fmtcnt=0;
data _null_; data _null_;
@@ -7055,6 +7040,10 @@ create table &outsummary as
proc format library=&&fmtloc&i CNTLOUT=&tempds; proc format library=&&fmtloc&i CNTLOUT=&tempds;
select &&fmtname&i; select &&fmtname&i;
run; run;
data &tempds;
length label $256;
set &tempds;
run;
proc append base=&outdetail data=&tempds; proc append base=&outdetail data=&tempds;
run; run;
%end; %end;
@@ -11917,6 +11906,170 @@ ods package publish archive properties
ods package close; ods package close;
%mend mp_zip;/** %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 @file mm_adduser2group.sas
@brief Adds a user to a group @brief Adds a user to a group
@details Adds a user to a metadata group. The macro first checks whether the @details Adds a user to a metadata group. The macro first checks whether the

View File

@@ -5,24 +5,32 @@
make use of permanent tables. To avoid duplication in definitions, this make use of permanent tables. To avoid duplication in definitions, this
macro provides a central location for managing the corresponding DDL. 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: Example usage:
%mp_coretable(LOCKTABLE,libds=work.locktable) %mp_coretable(LOCKTABLE,libds=work.locktable)
@param [in] table_ref The type of table to create. Example values: @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 @li DIFFTABLE
and mp_stackdiffs.sas @li FILTER_DETAIL
@li FILTER_DETAIL - For storing detailed filter values. Used by @li FILTER_SUMMARY
mp_filterstore.sas. @li LOCKANYTABLE
@li FILTER_SUMMARY - For storing summary filter values. Used by @li MAXKEYTABLE
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
@param [in] libds= (0) The library.dataset reference used to create the table. @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. 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> <h4> Related Macros </h4>
@li mp_filterstore.sas @li mp_filterstore.sas
@li mp_lockanytable.sas @li mp_lockanytable.sas
@@ -41,76 +49,21 @@
%let outds=%sysfunc(ifc(&libds=0,_data_,&libds)); %let outds=%sysfunc(ifc(&libds=0,_data_,&libds));
proc sql; proc sql;
%if &table_ref=DIFFTABLE %then %do; %if &table_ref=DIFFTABLE %then %do;
create table &outds( %mddl_dc_difftable(libds=&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)
);
%end; %end;
%else %if &table_ref=LOCKTABLE %then %do; %else %if &table_ref=LOCKTABLE %then %do;
create table &outds( %mddl_dc_locktable(libds=&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));
%end; %end;
%else %if &table_ref=FILTER_SUMMARY %then %do; %else %if &table_ref=FILTER_SUMMARY %then %do;
create table &outds( %mddl_dc_filtersummary(libds=&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));
%end; %end;
%else %if &table_ref=FILTER_DETAIL %then %do; %else %if &table_ref=FILTER_DETAIL %then %do;
create table &outds( %mddl_dc_filterdetail(libds=&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));
%end; %end;
%else %if &table_ref=MAXKEYTABLE %then %do; %else %if &table_ref=MAXKEYTABLE %then %do;
create table &outds( %mddl_dc_maxkeytable(libds=&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));
%end; %end;
%if &libds=0 %then %do; %if &libds=0 %then %do;
describe table &syslast; describe table &syslast;
drop table &syslast; drop table &syslast;

30
base/mp_ds2ddl.sas Normal file
View 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;

View File

@@ -6,7 +6,8 @@
SYSCC to 1008 if bad records are found, and call mp_abort.sas for a SYSCC to 1008 if bad records are found, and call mp_abort.sas for a
graceful service exit (configurable). graceful service exit (configurable).
Used for dynamic filtering in [Data Controller for SAS&reg;](https://datacontroller.io). Used for dynamic filtering in [Data Controller for SAS&reg;](
https://datacontroller.io).
Usage: Usage:
@@ -125,7 +126,7 @@ data &outds;
output; output;
end; end;
if OPERATOR_NM not in if OPERATOR_NM not in
('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS') ('=','>','<','<=','>=','BETWEEN','IN','NOT IN','NE','CONTAINS','GE','LE')
then do; then do;
REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM); REASON_CD='Invalid OPERATOR_NM: '!!cats(OPERATOR_NM);
putlog REASON_CD= OPERATOR_NM=; putlog REASON_CD= OPERATOR_NM=;

View File

@@ -8,8 +8,13 @@
https://sasapps.io)). This macro is also used in [Data Controller for SAS]( https://sasapps.io)). This macro is also used in [Data Controller for SAS](
https://datacontroller.io). 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 @param [in] queryds= (WORK.FILTERQUERY) The temporary input query dataset to
be validated. Has the following format: be validated. Has the following format:
|GROUP_LOGIC:$3|SUBGROUP_LOGIC:$3|SUBGROUP_ID:8.|VARIABLE_NM:$32|OPERATOR_NM:$10|RAW_VALUE:$32767| |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> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvalue.sas @li mf_getvalue.sas
@li mf_islibds.sas @li mf_islibds.sas
@@ -78,7 +84,10 @@
%put &sysmacroname entry vars:; %put &sysmacroname entry vars:;
%put _local_; %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) %mp_abort(iftrue= (&syscc ne 0)
,mac=mp_filterstore ,mac=mp_filterstore
,msg=%str(syscc=&syscc on macro entry) ,msg=%str(syscc=&syscc on macro entry)
@@ -96,12 +105,27 @@
,msg=%str(Invalid lock_table value: &lock_table) ,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) %mp_filtercheck(&queryds,targetds=&libds,abort=YES)
/* hash the result */ /* hash the result */
%let ds1=%mf_getuniquename(prefix=hashds); %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)); %let filter_hash=%upcase(%mf_getvalue(&ds1,hashkey));
%if &mdebug=1 %then %do; %if &mdebug=1 %then %do;
data _null_; data _null_;
@@ -132,7 +156,7 @@ run;
%let ds3=%mf_getuniquename(prefix=filtersum); %let ds3=%mf_getuniquename(prefix=filtersum);
data work.&ds3; data work.&ds3;
if 0 then set &filter_summary; if 0 then set &filter_summary;
filter_table=symget('libds'); filter_table="&orig_libds";
filter_hash="&filter_hash"; filter_hash="&filter_hash";
PROCESSED_DTTM=%sysfunc(datetime()); PROCESSED_DTTM=%sysfunc(datetime());
output; output;

View File

@@ -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 `|` `|` `|` `|` `| |`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> <h4> SAS Macros </h4>
@li mddl_sas_cntlout.sas
@li mf_dedup.sas @li mf_dedup.sas
@li mf_getfmtlist.sas @li mf_getfmtlist.sas
@li mf_getfmtname.sas @li mf_getfmtname.sas
@@ -94,30 +95,7 @@ create table &outsummary as
%if "&outdetail" ne "0" %then %do; %if "&outdetail" ne "0" %then %do;
/* ensure base table always exists */ /* ensure base table always exists */
proc sql; %mddl_sas_cntlout(libds=&outdetail)
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'
);
/* grab the location of each format */ /* grab the location of each format */
%let fmtcnt=0; %let fmtcnt=0;
data _null_; data _null_;
@@ -134,6 +112,10 @@ create table &outsummary as
proc format library=&&fmtloc&i CNTLOUT=&tempds; proc format library=&&fmtloc&i CNTLOUT=&tempds;
select &&fmtname&i; select &&fmtname&i;
run; run;
data &tempds;
length label $256;
set &tempds;
run;
proc append base=&outdetail data=&tempds; proc append base=&outdetail data=&tempds;
run; run;
%end; %end;

View File

@@ -84,7 +84,7 @@ options noquotelenmax;
""" """
f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb f = open('all.sas', "w") # r / r+ / rb / rb+ / w / wb
f.write(header) f.write(header)
folders=['base','meta','metax','server','viya','lua','fcmp'] folders=['base','ddl','meta','metax','server','viya','lua','fcmp']
for folder in folders: for folder in folders:
filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")] filenames = [fn for fn in Path('./' + folder).iterdir() if fn.match("*.sas")]
filenames.sort() filenames.sort()

34
ddl/mddl_dc_difftable.sas Normal file
View 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;

View 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;

View 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
View 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;

View 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
View 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;

14
package-lock.json generated
View File

@@ -8,7 +8,7 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@sasjs/cli": "3.6.0", "@sasjs/cli": "3.6.0",
"@sasjs/core": "4.4.4" "@sasjs/core": "4.4.7"
} }
}, },
"node_modules/@sasjs/adapter": { "node_modules/@sasjs/adapter": {
@@ -110,9 +110,9 @@
} }
}, },
"node_modules/@sasjs/core": { "node_modules/@sasjs/core": {
"version": "4.4.4", "version": "4.4.7",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.4.4.tgz", "resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.4.7.tgz",
"integrity": "sha512-gN6d0fvhaofp7buemS5KIOo5Bz8lbqhsEQD7SuH5FZ02MQurmfu7A0Zg0lIEi0w2/ptI4M/sZdF4D2DRh1D5xA==", "integrity": "sha512-9+HmwspvWu/gH9ElnmRaGdjOCspOidBRUYUfxLzgOy1Ya1JMZ2RErMklCAMg7XI0Us5jecd2FXdo8zlQDhRkWQ==",
"dev": true "dev": true
}, },
"node_modules/@sasjs/lint": { "node_modules/@sasjs/lint": {
@@ -2830,9 +2830,9 @@
} }
}, },
"@sasjs/core": { "@sasjs/core": {
"version": "4.4.4", "version": "4.4.7",
"resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.4.4.tgz", "resolved": "https://registry.npmjs.org/@sasjs/core/-/core-4.4.7.tgz",
"integrity": "sha512-gN6d0fvhaofp7buemS5KIOo5Bz8lbqhsEQD7SuH5FZ02MQurmfu7A0Zg0lIEi0w2/ptI4M/sZdF4D2DRh1D5xA==", "integrity": "sha512-9+HmwspvWu/gH9ElnmRaGdjOCspOidBRUYUfxLzgOy1Ya1JMZ2RErMklCAMg7XI0Us5jecd2FXdo8zlQDhRkWQ==",
"dev": true "dev": true
}, },
"@sasjs/lint": { "@sasjs/lint": {

View File

@@ -34,6 +34,6 @@
}, },
"devDependencies": { "devDependencies": {
"@sasjs/cli": "3.6.0", "@sasjs/cli": "3.6.0",
"@sasjs/core": "4.4.4" "@sasjs/core": "4.4.7"
} }
} }

View File

@@ -2,13 +2,15 @@
"$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json", "$schema": "https://raw.githubusercontent.com/sasjs/utils/main/src/types/sasjsconfig-schema.json",
"macroFolders": [ "macroFolders": [
"base", "base",
"ddl",
"fcmp", "fcmp",
"meta", "meta",
"metax", "metax",
"server", "server",
"viya", "viya",
"lua", "lua",
"tests/crossplatform" "tests/crossplatform",
"tests/ddl"
], ],
"docConfig": { "docConfig": {
"displayMacroCore": false, "displayMacroCore": false,

View 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
)

View 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
)

View 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
)

View 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
)

View 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
)

View 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
)

View 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
)