/*-------------------------------------------------------------- * * NAME: migrate_macros.sas * TITLE: contains macros which produce validation output * for MIGRATE * SUPPORT: David Wiehle david.wiehle@sas.com * INPUT: see below * OUTPUT: migrate_macros.log * * SPEC.REQ: see below *--------------------------------------------------------------*/ *--------------------------------------------------------------* * This program was designed to be used in conjunction with a * data migration template program (migrate_template.sas). * Please refer to this data migration template program to * determine the order in which the macros defined below should * be run. In order for the macros contained in this program to * work, the following must be true: * * 1. The source library containing files to be migrated must be * assigned to libref "lib1". * 2. The target library must be assigned to libref "lib2". * 3. A separate library must be assigned to a libref named "ods". * This is the destination for data sets created with the ODS * output statements below. *--------------------------------------------------------------*; /* the validation code must be run in a V8 or later SAS session */ %macro checkver; %global too_old; %let too_old=N; %if &sysver lt 8 %then %do; %let too_old=Y; %put ERROR: The Validation Tools must be submitted in; %put a SAS session running at least V8.; %end; %mend checkver; *--------------------------------------------------------------* * mig_in_lib produces an ODS output data set which contains * the members and memtypes in a library. These data sets * must be created before any of the macros below can be * successfully run. * * To output the contents of the source library before the * MIGRATE: %mig_in_lib(lib=lib1) * * To output the contents of the target library after the * MIGRATE: %mig_in_lib(lib=lib2) * *--------------------------------------------------------------*; %macro mig_in_lib(lib); %local suffix; %let suffix=m; *--------------------------------------------------------------* * determine contents of library *--------------------------------------------------------------*; ods output members=ods.&lib.&suffix/*(drop=num filesize lastmodified)*/; ods listing close; proc contents data=&lib.._all_;run; ods listing; /* the myvariables macro variable is used by some */ /* of the memtype check macros. Ensure it is se */ /* if &lib is LIB1 or LIB2. */ %global myvariables; %local myrow; /* the name of the "variables" ODS output object is */ /* different in V8 and SAS 9. Assign a global macro */ /* variable to the correct value for the current */ /* release. */ data _null_; if &sysver lt 9 then do; call symput("myvariables", "VariablesAlpha"); call symput("myrow", "MEMNAME"); end; else if &sysver ge 9 then do; call symput("myvariables", "Variables"); call symput("myrow", "NAME"); end; run; *--------------------------------------------------------------* * use the members in the library to determine the * length of the longest member name. *--------------------------------------------------------------* ; %if &lib=lib1 %then %do; %global longest; ods output &myvariables=ods.longest(keep=variable len where=(upcase(variable)="&myrow")); ods listing close; proc contents data=ods.lib1m;run;quit; ods listing; proc sort data=ods.longest;by descending len;run; data _null_; set ods.longest; if _n_=1 then do; len4=len+4; call symput("longest", put(len4,8.)); end; run; %end; *--------------------------------------------------------------* * determine contents of library * option 1: SAS 9 *--------------------------------------------------------------*; %if &sysver ge 9 %then %do; ods output members=ods.&lib.&suffix; ods listing close; proc contents data=&lib.._all_ ;run; ods listing; %end; *--------------------------------------------------------------* * option 2: V8 * rename "Memname" to the variable name produced by * SAS 9 Proc Contents. *--------------------------------------------------------------*; %if &sysver lt 9 %then %do; ods output members=ods.&lib.&suffix(rename=(Memname=Name)); ods listing close; proc contents data=&lib.._all_ ;run; ods listing; %end; *-------------------------------------------------------------------------* * change "name" variable for Generation data sets to a format recognized * by the EXIST function *-------------------------------------------------------------------------*; data ods.&lib.&suffix; length name $&longest..; set ods.&lib.&suffix; if GenNum gt 0 then do; name=compress(name||"#"||put(gennum, Z3.)); end; run; proc sort data=ods.&lib.&suffix;by memtype name;run; /*clean up the ODS library */ proc datasets lib=ods nolist;delete longest;run;quit; %mend mig_in_lib; *--------------------------------------------------------------* * mig_source produces memtype flag variables and creates macro * variables which contain the names of sas files in the source * library. *--------------------------------------------------------------*; %macro mig_source; %global audit_here catalog_here data_here index_here itemstore_here mddb_here program_here view_here lib1taudit lib1tcatalog lib1tdata lib1tindex lib1titemstor lib1tmddb lib1tprogram lib1tview ; *-------------------------------------------------------------------------------* * Create memtype "flag" variables. Initialize them to null. *-------------------------------------------------------------------------------*; %let audit_here=; %let catalog_here=; %let data_here=; %let index_here=; %let itemstore_here=; %let mddb_here=; %let program_here=; %let view_here=; *-------------------------------------------------------------------------------* * Create memtype counter variables. Initialize them to zero. *-------------------------------------------------------------------------------*; data _null_; call symput("lib1taudit", put(0,8.)); call symput("lib1tcatalog", put(0,8.)); call symput("lib1tdata", put(0,8.)); call symput("lib1tindex", put(0,8.)); call symput("lib1titemstor", put(0,8.)); call symput("lib1tmddb", put(0,8.)); call symput("lib1tprogram", put(0,8.)); call symput("lib1tview", put(0,8.)); run; data _null_; set ods.lib1m end=eof; by memtype name; *-------------------------------------------------------------------------------* * initialize a "memtype counter variable" to zero. *-------------------------------------------------------------------------------*; retain n 0; if first.memtype then do; *-------------------------------------------------------------------------------* * Set the values of the memtype flag variables to "Y" if a member with that * memtype exists in the source library. *-------------------------------------------------------------------------------*; select (memtype); when ("AUDIT") do; call symput("audit_here","Y"); end; when ("CATALOG") do; call symput("catalog_here","Y"); end; when ("DATA") do; call symput("data_here","Y"); end; when ("INDEX") do; call symput("index_here","Y"); end; when ("ITEMSTOR") do; call symput("itemstore_here","Y");end; when ("MDDB") do; call symput("mddb_here","Y"); end; when ("PROGRAM") do; call symput("program_here","Y"); end; when ("VIEW") do; call symput("view_here","Y"); end; otherwise; end; *-------------------------------------------------------------------------------* * Set the memtype counter to one. *-------------------------------------------------------------------------------*; n=1; *-------------------------------------------------------------------------------* * combine the memtype and the counter variable into a macro variable. Do * the same with the libref, memtype and member name. *-------------------------------------------------------------------------------*; call symput(compress("lib1mt"||put(n,8.)), memtype); call symput(compress("lib1"||memtype||put(n,8.)),name); *-------------------------------------------------------------------------------* * if this also happens to be the last memtype, output the counter variable into * a macro variable containing the total number of this particular memtype. *-------------------------------------------------------------------------------*; if last.memtype then do; call symput(compress("lib1t"||memtype),put(n,8.)); end; end; else if not first.memtype then do; n+1; if not last.memtype then do; call symput(compress("lib1mt"||put(n,8.)), memtype); call symput(compress("lib1"||memtype||put(n,8.)),name); end; else if last.memtype then do; call symput(compress("lib1mt"||put(n,8.)), memtype); call symput(compress("lib1"||memtype||put(n,8.)),name); call symput(compress("lib1t"||memtype),put(n,8.)); end; end; run; %mend mig_source; /* The keyvars data set contains all global macros defined by the */ /* Validation tools. Its purpose is to serve as a "vehicle" to */ /* transport this information to the target environment when */ /* CEDA is invoked when accessing the source library from the */ /* target environment (i.e., 32-bit to 64-bit or cross-host */ /* migration scenarios). It is not necessary to make use of the */ /* keyvars data set when migrating in the simplest case (i.e., */ /* same-host/same-bit migration). Keyvars has a single */ /* observation, and a varying number of variables. (The number */ /* of variables is 10 plus the total number of audit, data catalog */ /* and view members in the source library). */ /* keyvars, step 1: define macros which can create data sets in */ /* the ODS library containing the names of source library members.*/ /* Each data set has an identical "ID" variable, which can be used*/ /* to merge the data sets together. */ %macro getaudit; data ods.temp_audit(drop=i); length id 8. lib1audit1-%sysfunc(compress(lib1audit%sysfunc(left(%sysfunc(trim(&lib1taudit)))))) $&longest..; id=1; array myaudit {&lib1taudit} lib1audit1-%sysfunc(compress(lib1audit%sysfunc(left(%sysfunc(trim(&lib1taudit)))))); do i=1 to &lib1taudit; myaudit{i}=symget('lib1audit'||left(trim(i))); end; run; %mend getaudit; %macro getcatalog; data ods.temp_catalog(drop=i); length id 8. lib1catalog1-%sysfunc(compress(lib1catalog%sysfunc(left(%sysfunc(trim(&lib1tcatalog)))))) $&longest..; id=1; array mycatalog {&lib1tcatalog} lib1catalog1-%sysfunc(compress(lib1catalog%sysfunc(left(%sysfunc(trim(&lib1tcatalog)))))); do i=1 to &lib1tcatalog; mycatalog{i}=symget('lib1catalog'||left(trim(i))); end; run; %mend getcatalog; %macro getdata; data ods.temp_data(drop=i); length id 8. lib1data1-%sysfunc(compress(lib1data%sysfunc(left(%sysfunc(trim(&lib1tdata)))))) $&longest..; id=1; array mydata {&lib1tdata} lib1data1-%sysfunc(compress(lib1data%sysfunc(left(%sysfunc(trim(&lib1tdata)))))); do i=1 to &lib1tdata; mydata{i}=symget('lib1data'||left(trim(i))); end; run; %mend getdata; %macro getview; data ods.temp_view(drop=i); length id 8. lib1view1-%sysfunc(compress(lib1view%sysfunc(left(%sysfunc(trim(&lib1tview)))))) $&longest..; id=1; array myview {&lib1tview} lib1view1-%sysfunc(compress(lib1view%sysfunc(left(%sysfunc(trim(&lib1tview)))))); do i=1 to &lib1tview; myview{i}=symget('lib1view'||left(trim(i))); end; run; %mend getview; /* keyvars, step 2: define a macro to run the "get" macros defined above */ /* if members of a particular memtype are present. */ %macro getemall; /* create local macro variables which build the merge statement */ /* used to combine any data sets created by this macro. */ %local part1 part2 part3 part4; %let part1=; %let part2=; %let part3=; %let part4=; /* create the merge statement as a global macro variable */ %global mymerge99; %if "&audit_here" eq "Y" %then %do; %getaudit; %let part1=ods.temp_audit; %end; %if "&catalog_here" eq "Y" %then %do; %getcatalog; %let part2=ods.temp_catalog; %end; %if "&data_here" eq "Y" %then %do; %getdata; %let part3=ods.temp_data; %end; %if "&view_here" eq "Y" %then %do; %getview; %let part4=ods.temp_view; %end; /* build a merge statement */ data _null_; call symput("mymerge99", symget('part1')||' '||symget('part2')||' '||symget('part3')||' '||symget('part4')); run; /* keyvars, step 3: merge any data set(s) created in step 2 together, */ /* create data step "memtype flag" variables which match their global */ /* macro variable counterparts. */ data ods.keyvars(drop=id); length audit_here catalog_here data_here index_here itemstore_here mddb_here program_here view_here $3. lib1taudit lib1tcatalog lib1tdata lib1tindex lib1titemstor lib1tmddb lib1tprogram lib1tview longest longestn longestr mysysjobid $10.; merge &mymerge99; by id; mysysjobid="&sysjobid"; audit_here=symget('audit_here'); catalog_here=symget('catalog_here'); data_here=symget('data_here'); index_here=symget('index_here'); itemstore_here=symget('itemstore_here'); mddb_here=symget('mddb_here'); program_here=symget('program_here'); view_here=symget('view_here'); lib1taudit=symget('lib1taudit'); lib1tcatalog=symget('lib1tcatalog'); lib1tdata=symget('lib1tdata'); lib1tindex=symget('lib1tindex'); lib1titemstor=symget('lib1titemstor'); lib1tmddb=symget('lib1tmddb'); lib1tprogram=symget('lib1tprogram'); lib1tview=symget('lib1tview'); longest=symget('longest'); /* the longestn and longestr macro variables */ /* are present only if indexes are present */ /* in the source library. */ if left(trim(lib1tindex)) ne '0' then do; longestn=symget('longestn'); longestr=symget('longestr'); end; else if left(trim(lib1tindex)) eq '0' then do; longestn=' '; longestr=' '; end; run; * delete any data sets created by the "%get" macros above; proc datasets lib=ods nolist; delete temp_: ; run;quit; %mend getemall; /* read in the ODS data set containing the global macro variables */ /* created from the source library, and re-create them as global */ /* macro variables in the current session. */ %macro getmvars; /* step 1: compare the value of &sysjobid to the sysjobid value */ /* stored in the KEYVARS data set. If the values match, the */ /* global macro variables necessary for validation were defined */ /* in the current session, and are still available. There is */ /* no need to re-create the global macro variables. */ %local currid; data _null_; set ods.keyvars(keep=mysysjobid); call symput("currid", mysysjobid); **call symput("currid", put(mysysjobid, 8.)); run; %if &currid=&sysjobid %then %do; %goto exit; %end; /* step 2: determine the total number of character */ /* variables in the keyvars data set, output that value */ /* to a global macro variable, "totmvars". */ %global totmvars; ods output attributes=ods.temp_atr(keep=cvalue2 label2 where=(label2='Variables')); ods listing close; proc contents data=ods.keyvars;run; ods listing; /* there is one numeric variable in keyvars */ data _null_; set ods.temp_atr; call symput("totmvars", (input(cvalue2, 8.)-1)); run; /* step 3: read the keyvars data set. Create an array of all */ /* the character variables in keyvars whose dimension value is */ /* &totmvars. Use the array to create global macro variables */ /* with the same name and value as all variables in keyvars. */ data _null_; set ods.keyvars; array myvars{*} _char_; do i=1 to dim(myvars); varnayme=vname(myvars{i}); valyoo=myvars{i}; call symputx(varnayme, valyoo, 'g'); end; run; ** delete the temporary data sets created in the ODS library; proc datasets lib=ods nolist;delete temp_atr;run;quit; %exit: %mend getmvars; *--------------------------------------------------------------* * mig_check_libs outputs a side-by-side comparison of the * contents of source library before migrate against the * contents of the target library after migrate. *--------------------------------------------------------------*; %macro mig_check_libs; *--------------------------------------------------------------* * merge contents of source library before MIGRATE and contents * * of target library after MIGRATE. * *--------------------------------------------------------------*; data ods.final; length result $30. name $&longest.; merge ods.lib1m(in=in1) ods.lib2m(in=in2); by memtype name; if in1 and in2 then result="OK"; else if in1 and not in2 then result="not MIGRATED"; else if not in1 and in2 then result="not in source library"; run; title2 "contents of target library after MIGRATE (relative to source lib)"; proc print data=ods.final; var name memtype result; run; %mend mig_check_libs; %macro odsdata(lib); %local ishere myvar; %do i=1 %to &lib1tdata; %if %sysfunc(upcase(&lib))=LIB1 %then %do; %let ishere=1; %let myvar=source; %end; %if %sysfunc(upcase(&lib))=LIB2 %then %do; %let ishere=%sysfunc(exist(lib2.&&lib1data&i)); %let myvar=target; %end; %if &ishere eq 1 %then %do; /* create data sets to be used in "smart compare" */ /* of data set attributes */ ods output attributes=ods.atr1(keep=label1 cvalue1 where=(label1 not in (" ", "Data Set Name"/*, "Created", "Last Modified"*/))) attributes=ods.atr2(keep=label2 cvalue2 where=(label2 ne " ")); ods listing close; %if %sysfunc(index(&&lib1data&i, #)) gt 0 %then %do; proc contents data=&lib..%str("&&lib1data&i")n;run; %end; %else %do; proc contents data=&lib..&&lib1data&i;run; %end; ods listing; data ods.atr1(rename=(label1=attribute cvalue1=&myvar)); set ods.atr1; run; data ods.atr2(rename=(label2=attribute cvalue2=&myvar)); set ods.atr2; run; data ods.&lib._data_atr&i; length attribute $50.; set ods.atr1 ods.atr2; run; proc sort data=ods.&lib._data_atr&i;by attribute;run; %end; %end; %mend odsdata; *----------------------------------------------------------------* * CHECKDATA outputs the following to the lst: * 1. side-by-side comparison of data set attributes * 2. proc compare of data set contents * * The default behavior of CHECKDATA is to output only those data * set attributes and engine/host data which are different in the * source library and the target library. To output a * comparison of all data set attributes, submit the following: * %checkdata(showall=yes); *----------------------------------------------------------------*; %macro checkdata(showall); %local ishere; %do i=1 %to &lib1tdata; %let ishere=%sysfunc(exist(lib2.&&lib1data&i)); %if &ishere eq 1 %then %do; /* 1. "smart compare" of header data */ data ods.comp_data_atr&i; merge ods.lib1_data_atr&i(in=in1) ods.lib2_data_atr&i(in=in2); by attribute; if in1 or in2; run; title2 "%sysfunc(left(%sysfunc(trim(&&lib1data&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tdata)) data sets in source library"; %if &showall=yes %then %do; proc print data=ods.comp_data_atr&i; title4 "Comparison of PROC CONTENTS header information"; title5 "Number 1 of 2 reports for this data set (from checkdata macro)"; var attribute source target; run; %end; %else %if &showall ne yes %then %do; data ods.comp_data_atr&i; set ods.comp_data_atr&i; if source ne target; run; proc print data=ods.comp_data_atr&i; title4 "Differences in PROC CONTENTS header information"; title5 "Note: all other header information was the same"; title6 "Number 1 of 2 reports for this data set (from checkdata macro)"; var attribute source target; run; %end; /* 2. proc compare of data set data */ title4 "PROC COMPARE of data"; title5 "Number 2 of 2 reports for this data set (from checkdata macro)"; title6; %if %sysfunc(index(&&lib1data&i, #)) gt 0 %then %do; proc compare data=lib1.%str("&&lib1data&i")n comp=lib2.%str("&&lib1data&i")n briefsummary listall; run; %end; %else %do; proc compare data=lib1.&&lib1data&i comp=lib2.&&lib1data&i briefsummary listall; run; %end; *clean up data sets created in the ODS library by this macro; /* note: for now, delete only the comp data set */ proc datasets lib=ods nolist; delete /*atr1 atr2 lib1_data_atr&i lib2_data_atr&i */ comp_data_atr&i ; quit; %end; %else %if &ishere ne 1 %then %do; data ods.nothere; note="No validation will be performed"; run; title2 "&&lib1data&i"; title3 "Number &i of %sysfunc(compress(&&lib1tdata)) data sets in source library"; title4 "Member is present in LIB1 but was not MIGRATED to LIB2"; proc print data=ods.nothere noobs;run; %end; %end; title2; title3; title4; title5; title6; %mend checkdata; %macro odscatalog(lib); %local ishere myvar formats_here; %do i=1 %to &lib1tcatalog; %if %sysfunc(upcase(&lib))=LIB1 %then %do; %let ishere=1; %let myvar=source; %end; %if %sysfunc(upcase(&lib))=LIB2 %then %do; %let ishere=%sysfunc(exist(lib2.&&lib1catalog&i, CATALOG)); %let myvar=target; %end; %if &ishere eq 1 %then %do; /* step 1: determine if any formats exist in the catalog */ ods output catalog_random=ods.temp_cat(keep=type); ods listing close; proc catalog catalog=&lib..&&lib1catalog&i; contents;run;quit; ods listing; proc sort data=ods.temp_cat out=ods.temp_cat nodupkey; by type; run; data _null_; set ods.temp_cat end=eof; retain count 0; if upcase(type)="FORMAT" then count+1; if eof then do; if count ne 0 then do; call symput("formats_here", "Y"); end; end; run; /* step 2: If formats do exist, create a "control out" */ /* data set. */ %if &formats_here=Y %then %do; proc format lib=&lib..&&lib1catalog&i cntlout=ods.&lib._catalog&i._formats;run; %end; /* step 3: output the catalog metadata to a data set */ ods output catalog_random=ods.&lib._catalog&i(drop=crdate moddate); ods listing close; proc catalog catalog=&lib..&&lib1catalog&i; contents; run; quit; ods listing; %end; ***end ishere loop; %end; ***end outer do i=1 to total catalog loop; %mend odscatalog; *----------------------------------------------------------------* * CHECKCATALOG outputs the following to the lst: * 1. COMPARE of catalog metadata in source library against * catalog metadata in target library. * 2. If the catalog contains format entries, a COMPARE of the * Proc Format cntlout= data set created from the source and * target library versions of a data set. *----------------------------------------------------------------*; /* compcatm (compare catalog metadata) is called by the checkcatalog */ /* macro if a proc compare of the metadata of the source and target */ /* library versions of a catalog shows any significant differences. */ /* It produces a report of any catalog entries which either were not */ /* migrated or whose type or description change after migration. */ %macro compcatm; data ods.temp_cat1(rename=(type=s_type desc=s_desc) drop=num); set ods.lib1_catalog&i; run; proc sort data=ods.temp_cat1;by objname;run; data ods.temp_cat2(rename=(type=t_type desc=t_desc) drop=num); set ods.lib2_catalog&i; run; proc sort data=ods.temp_cat2;by objname;run; data ods.temp_cat; length result $25.; merge ods.temp_cat1(in=in1) ods.temp_cat2(in=in2); by objname; if in1 or in2; if in1 and not in2 then result="Entry not migrated"; if in1 and in2 then result="Migrated"; if not in1 and in2 then result="Added after migration"; run; data ods.temp_cat; set ods.temp_cat; if result ne "Migrated"; if (upcase(s_type) ne upcase(t_type)) or (upcase(s_desc) ne upcase(t_desc)); run; title2 "%sysfunc(left(%sysfunc(trim(&&lib1catalog&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "Catalog entries which differ between source and target catalogs"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; proc print data=ods.temp_cat; var objname result s_type t_type s_desc t_desc; run; /* clean up temporary data sets created by this macro */ proc datasets lib=ods nolist;delete temp:;run;quit; %mend compcatm; /* compcatf (compare catalog formats) is called by the checkcatalog */ /* macro if a proc compare of the formats in the source and target */ /* library versions of a catalog shows any significant differences. */ /* It produces a report of any format entries which were not migrated*/ %macro compcatf; data ods.temp_cat1(keep=fmtname start label in_source); length in_source 8. fmtname $32.; set ods.lib1_catalog&i._formats; in_source=1; run; proc sort data=ods.temp_cat1;by fmtname start label;run; data ods.temp_cat2(keep=fmtname start label in_target); length in_target 8.; set ods.lib2_catalog&i._formats; in_target=1; run; proc sort data=ods.temp_cat2;by fmtname start label;run; data ods.temp_cat; length result $25.; merge ods.temp_cat1(in=in1) ods.temp_cat2(in=in2); by fmtname start label; if in1 or in2; if in_source=1 and in_target=1 then result="Migrated"; if in_source=1 and in_target=. then result="Entry not migrated"; if in_source=. and in_target=1 then result="Added after migration"; run; data ods.temp_cat; set ods.temp_cat; if result ne "Migrated"; run; title2 "%sysfunc(left(%sysfunc(trim(&&lib1catalog&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "Format entries which differ between source and target catalogs"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; proc print data=ods.temp_cat; var fmtname result start label in_source in_target; run; /* clean up temporary data sets created by this macro */ proc datasets lib=ods nolist;delete temp:;run;quit; %mend compcatf; %macro checkcatalog; %do i=1 %to &lib1tcatalog; %let ishere=%sysfunc(exist(lib2.&&lib1catalog&i, CATALOG)); /* if target library catalog exists*/ %if &ishere=1 %then %do; %let mytotal=1; %let mynum=1; %let ishere_f_source=%sysfunc(exist(ods.lib1_catalog&i._formats)); /* if source library catalog had formats */ %if &ishere_f_source=1 %then %do; %let mytotal=2; %let mynum=1; %let ishere_f_target=%sysfunc(exist(ods.lib2_catalog&i._formats)); /* if target library catalog also has formats */ %if &ishere_f_target=1 %then %do; proc sort data=ods.lib1_catalog&i._formats; by fmtname start label;run; proc sort data=ods.lib2_catalog&i._formats; by fmtname start label;run; proc compare base=ods.lib1_catalog&i._formats comp=ods.lib2_catalog&i._formats noprint; run; %if &sysinfo lt 64 %then %do; title2 "%sysfunc(left(%sysfunc(trim(&&lib1catalog&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "PROC COMPARE of FORMAT entries in catalog in source and target libraries"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; data ods.nothere3; length result $75.; result="All formats in target catalog match those in source catalog"; run; proc print data=ods.nothere3 noobs;run; %end; %else %if &sysinfo ge 64 %then %do; **smart compare logic for formats; %compcatf; %end; %let mynum=2; proc compare base=ods.lib1_catalog&i comp=ods.lib2_catalog&i noprint; run; %if &sysinfo lt 64 %then %do; title2 "%sysfunc(left(%sysfunc(trim(&&lib1catalog&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "PROC COMPARE of METADATA of the source and target versions of this catalog"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; data ods.nothere4; length result $75.; result="All metadata (names, types, descriptions) same as in source catalog"; run; proc print data=ods.nothere4 noobs;run; %end; %else %if &sysinfo ge 64 %then %do; **smart compare logic for metadata; %compcatm; %end; %end; /*end source yes formats, target yes formats */ /* target library catalog did not have formats, but source did */ %else %do; data ods.nothere2; note="No format entry validation will be performed"; run; title2 "&&lib1catalog&i"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "Formats present in LIB1 catalog but were not MIGRATED to LIB2"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; proc print data=ods.nothere2 noobs;run; %let mynum=2; proc compare base=ods.lib1_catalog&i comp=ods.lib2_catalog&i noprint; run; %if &sysinfo lt 64 %then %do; title2 "%sysfunc(left(%sysfunc(trim(&&lib1catalog&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "PROC COMPARE of METADATA of the source and target versions of this catalog"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; data ods.nothere4; length result $75.; result="All metadata (names, types, descriptions) same as in source catalog"; run; proc print data=ods.nothere4 noobs;run; %end; %else %if &sysinfo ge 64 %then %do; **smart compare logic for metadata; %compcatm; %end; %end; /* end source yes formats, target no formats*/ %end; /*end source library had formats */ /* source library did not have formats */ %else %if &ishere_f_source ne 1 %then %do; %let mytotal=1; %let mynum=1; proc compare base=ods.lib1_catalog&i comp=ods.lib2_catalog&i noprint; run; %if &sysinfo lt 64 %then %do; title2 "%sysfunc(left(%sysfunc(trim(&&lib1catalog&i))))"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "PROC COMPARE of METADATA of the source and target versions of this catalog"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; data ods.nothere4; length result $75.; result="All metadata (names, types, descriptions) same as in source catalog"; run; proc print data=ods.nothere4 noobs;run; %end; %else %if &sysinfo ge 64 %then %do; ***smart compare for metadata; %compcatm; %end; %end; /* end source library did not have formats */ %end; /* end target library version of catalog was migrated */ /* target library version of catalog was not migrated */ %if &ishere ne 1 %then %do; %let mytotal=1; %let mynum=1; data ods.nothere; note="No validation will be performed"; run; title2 "&&lib1catalog&i"; title3 "Number &i of %sysfunc(compress(&&lib1tcatalog)) catalogs in source library"; title4 "Member is present in LIB1 but was not MIGRATED to LIB2"; title5 "Number &mynum of &mytotal reports for this catalog (from checkcatalog macro)"; proc print data=ods.nothere noobs;run; %end; /* end catalog was not migrated */ /* clean up temporary data sets created by the macro */ proc datasets lib=ods nolist;delete nothere: ;run;quit; %end; /*end do i to total catalog */ title2; title3; title4; title5; %mend checkcatalog; %macro odsaudit(lib); %local ishere myvar myvar2 libnum; %do i=1 %to &lib1taudit; %if %sysfunc(upcase(&lib))=LIB1 %then %do; %let ishere=1; %let myvar=source; %let myvar2=s; %let libnum=1; %end; %if %sysfunc(upcase(&lib))=LIB2 %then %do; %let ishere=%sysfunc(exist(lib2.&&lib1audit&i)); %let myvar=target; %let myvar2=t; %let libnum=2; %end; %if &ishere eq 1 %then %do; ods output attributes=ods.atr1(keep=label1 cvalue1 where=(label1 not in (" ", "Data Set Name", "Created", "Last Modified"))) attributes=ods.atr2(keep=label2 cvalue2 where=(label2 ne " ")) &myvariables=ods.audit&libnum.var&i(drop=member); ods listing close; %if %sysfunc(index(&&lib1audit&i, #)) gt 0 %then %do; proc contents data=&lib..%str("&&lib1audit&i")n(type=audit);run; %end; %else %do; proc contents data=&lib..&&lib1audit&i(type=audit);run; %end; ods listing; data ods.atr1(rename=(label1=attribute cvalue1=&myvar)); set ods.atr1; run; data ods.atr2(rename=(label2=attribute cvalue2=&myvar)); set ods.atr2; run; data ods.&lib._audit_atr&i; length attribute $50.; set ods.atr1 ods.atr2; run; proc sort data=ods.&lib._audit_atr&i;by attribute;run; proc sort data=ods.audit&libnum.var&i;by num variable;run; data ods.audit&libnum.var&i(rename=(type=&myvar2._type len=&myvar2._len pos=&myvar2._pos format=&myvar2._format)); set ods.audit&libnum.var&i; run; %end; %end; %mend odsaudit; %macro checkaudit(showall); %do i=1 %to &lib1taudit; %let ishere=%sysfunc(exist(lib2.&&lib1audit&i)); %if &ishere=1 %then %do; /* combine the source and target library attribute data sets */ data ods.comp_audit_atr&i; merge ods.lib1_audit_atr&i(in=in1) ods.lib2_audit_atr&i(in=in2); by attribute; if in1 or in2; run; title2 "%sysfunc(left(%sysfunc(trim(&&lib1audit&i))))"; title3 "Number &i of %sysfunc(left(%sysfunc(trim(&&lib1taudit)))) audit files in source library"; %if &showall=yes %then %do; proc print data=ods.comp_audit_atr&i; title4 "Comparison of PROC CONTENTS header information"; title5 "Number 1 of 5 reports for this file (from checkaudit macro)"; var attribute source target; run; %end; %else %if &showall ne yes %then %do; data ods.comp_audit_atr&i; set ods.comp_audit_atr&i; if source ne target; run; proc print data=ods.comp_audit_atr&i; title4 "Comparison of PROC CONTENTS header information"; title5 "Note: all other header information was the same"; title6 "Number 1 of 4 reports for this file (from checkaudit macro)"; var attribute source target; run; %end; data ods.audit3var&i; merge ods.audit1var&i(in=in1) ods.audit2var&i(in=in2); by num variable; if in1 or in2; run; %if &showall=yes %then %do; title4 "Comparison of PROC CONTENTS variable information"; title5 "s=source library, t=target library"; title6 "Number 2 of 4 reports for this file (from checkaudit macro)"; proc print data=ods.audit3var&i noobs; var num variable s_type t_type s_len t_len s_pos t_pos s_format t_format; run; title6; title4 "Contents of audit file from source library"; title5 "Number 3 of 4 reports for this file (from checkaudit macro)"; proc print data=lib1.%str("&&lib1audit&i")n(type=audit);run; title4 "Contents of audit file from target library"; title5 "Number 4 of 4 reports for this file (from checkaudit macro)"; proc print data=lib2.%str("&&lib1audit&i")n(type=audit);run; %end; /* end showall=yes */ %else %if &showall ne yes %then %do; data ods.audit3var&i; length result $50.; retain count 0; set ods.audit3var&i end=eof; if s_type=t_type and s_len=t_len and s_pos=t_pos and s_format=t_format then do; result="OK"; end; else do; result="PROBLEM"; count+1; end; if eof then do; if count=0 then do; result="type, len, pos and format agreed for all variables"; keep result; output; end; end; run; title4 "Comparison of PROC CONTENTS variable information"; title5 "Number 2 of 4 reports for this file (from checkaudit macro)"; proc print data=ods.audit3var&i noobs; run; title6; *-------------------------------------------------------------------------------* * separate the new audit records created by MIGRATE from the migrated audit file *-------------------------------------------------------------------------------*; data ods.testaud&i; rid=_n_; set lib2.%str("&&lib1audit&i")n(type=audit); run; proc sort data=ods.testaud&i;by rid _atmessage_;run; *--------------------------------------------------------------------------------* * MIGRATE always adds at least one record to the end of an audit file where * _atmessage_= "MIGRATE". If an audit trail was suspended prior to migration, * MIGRATE adds one more record, where _atmessage_="SUSPEND". Use this information * to identify the cutoff point between "old" and "new" audit file records. *--------------------------------------------------------------------------------*; data _null_; set ods.testaud&i end=eof; by rid _atmessage_; lag_at=lag(_atmessage_); if eof then do; if lag_at= "MIGRATE" then call symput("cutoff", rid-2);*the current record must be "suspend", so 2 records were added; else if lag_at ne "MIGRATE" then call symput("cutoff", rid-1);*the current record must be "migrate", so 1 record was added; end; run; data ods.audit_short&i ods.audit_new&i; set lib2.%str("&&lib1audit&i")n(type=audit); if _n_ le &cutoff then output ods.audit_short&i; if _n_ gt &cutoff then output ods.audit_new&i; run; *-------------------------------------------------------------------------------* * create a "copy" of the original audit file to avoid a "type" difference * when comparing its data to the "audit_short" data set created above. *-------------------------------------------------------------------------------*; data ods.audit_source&i; set lib1.%str("&&lib1audit&i")n(type=audit); run; *-------------------------------------------------------------------------------* * compare the data in audit file in the source library to the data in the * "shortened" audit file in the target library *-------------------------------------------------------------------------------*; proc compare base=ods.audit_source&i comp=ods.audit_short&i noprint; run; %if &sysinfo=0 %then %do; data ods.audcompo&i; length result $100.; result="OK: pre-MIGRATE records in migrated audit file match original"; run; %end; %else %if &sysinfo ne 0 %then %do; data ods.audcompo&i; length result $100.; result="PROBLEM: pre-MIGRATE records in migrated audit file do NOT match original"; run; %end; title4 "Results of comparison of pre-MIGRATE records in audit file in source and target libraries"; title5 "Number 3 of 4 reports for this file (from checkaudit macro)"; proc print data=ods.audcompo&i noobs;run; *-------------------------------------------------------------------------------* * Create a macro variable which contains the last "date modified" record * from last observation in source library audit file. *-------------------------------------------------------------------------------*; data _null_; %global last_date; set lib1.%str("&&lib1audit&i")n(type=audit) end=eof; if eof then do; call symput("last_date", put(_atdatetime_, datetime20.)); end; run; *-------------------------------------------------------------------------------* * Check the records added to the migrated audit file. *-------------------------------------------------------------------------------*; data ods.audcompn&i(keep=result); length result $100.; set ods.audit_new&i end=eof; retain count 0; if _n_=1 then do; if _atopcode_ ne "AM" then count+1; if _atmessage_ ne "MIGRATE" then count+1; if x ne . then count+1; if _atobsno_ ne . then count+1; if _atreturncode_ ne . then count+1; if _atdatetime_ lt input("&last_date", datetime20.) then count+1; if _atuserid_ =" " then count+1; end; else if _n_=2 then do; if _atopcode_ ne "AS" then count+1; if _atmessage_ ne "SUSPEND" then count+1; if x ne . then count+1; if _atobsno_ ne . then count+1; if _atreturncode_ ne . then count+1; if _atdatetime_ lt input("&last_date", datetime20.) then count+1; if _atuserid_ =" " then count+1; end; if eof then do; if count=0 then result="OK: post-MIGRATE records in migrated audit file contain expected values"; else if count ne 0 then result="PROBLEM: post-MIGRATE records in migrated audit file contain unexpected values"; output; end; run; title4 "Status of post-MIGRATE records in target library audit file"; title5 "Number 4 of 4 reports for this file (from checkaudit macro)"; proc print data=ods.audcompn&i noobs;run; %end; /*end showall ne yes */ %end; /*end audit file migrated to target library */ %else %if &ishere ne 1 %then %do; data ods.nothere; note="No validation will be performed"; run; title2 "&&lib1audit&i"; title3 "Number &i of %sysfunc(compress(&&lib1taudit)) audit files in source library"; title4 "Member is present in LIB1 but was not MIGRATED to LIB2"; proc print data=ods.nothere noobs;run; %end; /* end audit file not migrated to target library */ %end; /* end do i= 1 to total */ title2; title3; title4; title5; title6; %mend checkaudit; /* for this macro to produce expected results for %checkindex, the lib= */ /* argument must be entered in upper case. (i.e., %checkindex(LIB1); )*/ /* for this macro to produce expected results for %checkindex, the lib= */ /* argument must be entered in upper case. (i.e., %checkindex(LIB1); )*/ %macro odsindex(lib); %local ishere; %global ics_here; *-------------------------------------------------------------------------------* * Only run this macro if indexes are present in &lib *-------------------------------------------------------------------------------*; %if &index_here=Y %then %do; %if %sysfunc(upcase(&lib))=LIB1 %then %do; %let ishere=1; %end; /* Since it's possible indexes did not migrate, and */ /* &index_here was created in the source library, check the*/ /* members in the target library to confirm the presence */ /* or absence of indexes. */ %if %sysfunc(upcase(&lib))=LIB2 %then %do; ods output members=ods.temp_mem(keep=memtype); ods listing close; proc contents data=lib2._all_;run; ods listing; proc sort data=ods.temp_mem nodupkey;by memtype;run; data _null_; set ods.temp_mem end=eof; retain count 0; if upcase(memtype)="INDEX" then count+1; if eof then do; call symput("ishere", put(count, 8.)); end; run; %end; %if &ishere=1 %then %do; /* an attempt to output the out2= data set to a two-level member name */ /* silently fails in V8. (only the first obs is output to the out2= */ /* data set) This problem was corrected in SAS 9.0 (SAS defect number */ /* S0121691) To ensure all obs are output to the index data set, send*/ /* the out2= results to a work data set, then create the ODS lib data */ /* set with a set statement. */ proc contents data=&lib.._all_ out2=temp&lib.7283 noprint;run; data ods.&lib.indy; set temp&lib.7283; run; *--------------------------------------------------------------------------------* * Use the "type" variable to determine whether or not any Integrity Constraints * exist in the library. Create a memtype "flag" variable for integrity * constraints. *--------------------------------------------------------------------------------*; data _null_; set ods.&lib.indy end=eof; retain count 0; if type ne "Index" then do; count+1; end; if eof then do; if count eq 0 then do; call symput("ics_here", " "); end; else if count ne 0 then do; call symput("ics_here", "Y"); end; end; run; *-------------------------------------------------------------------------------* * Indexes *-------------------------------------------------------------------------------*; /* Prior to SAS 9, the member name was not output in the OUT2= data set. */ /* Add the member name to any index. */ /* if the current libref is LIB1, output the length of the longest index */ /* name and index recreate statement to global macro variables. */ %if %sysfunc(upcase(&lib))=LIB1 %then %do; %global longestn longestr; proc contents data=temp&lib.7283 noprint out=ods.temp888;run; data ods.temp888; retain lengthn lengthr 0; set ods.temp888(where=(upcase(name) in ("NAME", "RECREATE"))); if upcase(name)="NAME" then do; call symput("longestn", put(length, 8.)); end; if upcase(name)="RECREATE" then do; call symput("longestr", put(length, 8.)); end; run; %end; /* create local macro variables: out2 is an "index here" flag for each */ /* data set, myset is a string to which any out2= data set name is */ /* appended. The resulting myset string is used in a set statement to */ /* set all the out2= data sets together. */ %local out2 myset; %let out2=; %let myset=; %do i=1 %to &lib1tdata; ods output attributes=ods.tempidx(keep=label2 cvalue2 where=(label2='Indexes')); ods listing close; %if %sysfunc(index(&&lib1data&i, #)) gt 0 %then %do; proc contents data=&lib..%str("&&lib1data&i")n;run; %end; %else %do; proc contents data=&lib..&&lib1data&i;run; %end; ods listing; data _null_; set ods.tempidx; if cvalue2 ne '0' then do; call symput("out2", "Y"); end; run; %if &out2=Y %then %do; %if %sysfunc(index(&&lib1data&i, #)) gt 0 %then %do; proc contents data=&lib..%str("&&lib1data&i")n out2=tempdat&i noprint ;run; %end; %else %do; proc contents data=&lib..&&lib1data&i out2=tempdat&i noprint;run; %end; data tempdat&i(keep=membr name type recreate inactive); length membr $&longest..; set tempdat&i; membr="&&lib1data&i"; run; %let myset=&myset tempdat&i; %end; /*end out2=y loop */ %let out2=; %end; /*end do i to &total data loop */ data ods.temp&lib._idx; length name $&longestn.. type $12. recreate $&longestr.. inactive $3.; set &myset; run; %if &sysver ge 9 %then %do; /* V8 and SAS 9 use different criteria for displaying "member" information */ /* for generation data sets ("member" is captured in the "membr" column */ /* this point in the program). For example, gennum=1 for a data set: */ /* V8: IC_GENS#001 */ /* SAS 9: IC_GENS (gennum=1) */ /* The code below "normalizes" the membr column so that it will match the */ /* same column generated by V8. */ data ods.&lib._idx; set ods.temp&lib._idx; if index(membr, "(gennum=") gt 0 then do; mytot=length(membr); mypart1=index(membr, "(gennum="); mystart=index(membr, "="); mylen=mypart1-2; mylen2=(mytot-mystart)-1; mypart2=input((substr(membr, mystart+1, mylen2)), 8.); part1=substr(membr, 1, mylen); /* only "normalize" the membr column if it contains a generation */ /* data set name. */ if mypart1 gt 0 then do; membr=compress(part1||"#"||put(mypart2, Z3.)); end; drop my: part1; end; run; %end; %if &sysver lt 9 %then %do; /* V8 Proc Contents outputs an "ICBuilt" variable that does not appear in SAS 9 */ /* contents output. If the current release is prior to SAS 9, and ICs are */ /* present, drop this variable to avoid an insignificant difference when */ /* compared to the target library Index data set. */ %local mydrop; %if "&ics_here"="Y" %then %do; data _null_; call symput("mydrop", "(drop=ICBuilt)"); run; %end; %if "&ics_here" ne "Y" %then %do; data _null_; call symput("mydrop", " "); run; %end; %end; %else %if &sysver ge 9 %then %do; %local mydrop; %let mydrop=; %end; data ods.&lib._idx&mydrop; set ods.temp&lib._idx; run; /* add normalization code here*/ /* Until this point, the length of the "name" and "recreate" */ /* variables have used the Proc Contents length, which is */ /* assigned in such a way that it always exceeds the length */ /* of the longest text value in the variable. "Trim" the */ /* lengths to avoid unnecessary Proc Print WARNINGs in the */ /* checkindex macro. */ data _null_; set ods.&lib._idx end=eof; retain longestn 0; if length(name) gt longestn then do; longestn=length(name); end; if eof then do; call symput("mylongestn", longestn); end; run; data _null_; set ods.&lib._idx end=eof; retain longestr 0; if length(recreate) gt longestr then do; longestr=length(recreate); end; if eof then do; call symput("mylongestr", longestr); end; run; data ods.&lib._idx(rename=(myrec=recreate myname=name)); length myrec $&mylongestr.. myname $&mylongestn.. ; set ods.&lib._idx; myrec=recreate; myname=name; drop recreate name; run; %local myprefix; %if &lib=LIB1 %then %let myprefix=source; %if &lib=LIB2 %then %let myprefix=target; /* existing code */ data ods.&lib._idx(keep=membr name type recreate inactive rename=(type=&myprefix._type recreate=&myprefix._recreate inactive=&myprefix._inactive ) ); set ods.&lib._idx; run; proc sort data=ods.&lib._idx;by membr name;run; *---------------------------------------------------------------* * The remaining integrity constraint information is expected * to be the same in each library. *---------------------------------------------------------------*; %end; /*end ishere */ /* delete any temporary data sets created by the macro */ proc datasets lib=ods nolist;delete temp: ;run;quit; %end; /*end index flag=yes */ %mend odsindex; /* proc sort data=ods.&lib._idx;by membr name type;run; */ %macro compindex; %local mymissing; %let mymissing=; data ods.idx_comp; length migrated not_migrated diff_type diff_recreate diff_inactive 8.; merge ods.lib1_idx(in=in1) ods.lib2_idx(in=in2); by membr name; if in1 or in2; if in1 and in2 then migrated=1; if in1 and not in2 then not_migrated=1; if source_type ne target_type then diff_type=1; if source_recreate ne target_recreate then diff_recreate=1; if source_inactive ne target_inactive then diff_inactive=1; run; /* part 1: what was migrated to target library? */ data _null_; set ods.idx_comp end=eof; retain count 0; if not_migrated=1 then count+1; if eof then do; if count=0 then do; call symput("need_nothere", "Y"); end; end; run; %if &need_nothere=Y %then %do; title2 "Comparison of Indexes in source and target libraries"; title3 "Report 1 of 4"; proc print data=ods.nothere;run; %end; %if &need_nothere= %then %do; ods output onewayfreqs=ods.tempcount(keep=cumfrequency); ods listing close; proc freq data=ods.idx_comp; where not_migrated=1; table membr; run; ods listing; data _null_; set ods.tempcount end=eof; if eof then do; call symput("mymissing", cumfrequency); end; run; data ods.tempprint1; set ods.idx_comp; where not_migrated=1; run; title2 "%sysfunc(left(%sysfunc(trim(&mymissing)))) Indexes which were not migrated to target library"; title3 "Report 1 of 4"; proc print data=ods.tempprint1; var membr name source_type source_recreate source_inactive; run; %let mymissing=; %end; %let need_nothere=; /* part 2: did any types change? */ data _null_; set ods.idx_comp end=eof; retain count 0; if not_migrated=. and diff_type=1 then count+1; if eof then do; if count eq 0 then do; call symput("need_nothere", "Y"); end; end; run; %if &need_nothere=Y %then %do; title2 "Indexes: Comparison of types in source and target libraries"; title3 "Report 2 of 4"; proc print data=ods.nothere;run; %end; %if &need_nothere= %then %do; data ods.tempprint2; set ods.idx_comp; where not_migrated=. and diff_type=1; run; title2 "Indexes: Differences in type in source and target libraries"; title3 "Report 2 of 4"; proc print data=ods.tempprint2; var membr name source_type target_type; run; %end; /* part 3: did any inactive statuses change? */ data _null_; set ods.idx_comp end=eof; retain count 0; if not_migrated=. and diff_inactive=1 then count+1; if eof then do; if count eq 0 then do; call symput("need_nothere", "Y"); end; end; run; %if &need_nothere=Y %then %do; title2 "Indexes: Comparison of inactive flag in source and target libraries"; title3 "Report 3 of 4"; proc print data=ods.nothere;run; %end; %if &need_nothere= %then %do; data ods.tempprint3; set ods.idx_comp; where not_migrated=. and diff_inactive=1; run; title2 "Indexes: Differences in inactive flag in source and target libraries"; title3 "Report 3 of 4"; proc print data=ods.tempprint3; var membr name source_inactive target_inactive; run; %end; %let need_nothere=; %let need_nothere=; /* part 4: did any recreate statements change? */ data _null_; set ods.idx_comp end=eof; retain count 0; if not_migrated=. and diff_recreate=1 then count+1; if eof then do; if count eq 0 then do; call symput("need_nothere", "Y"); end; end; run; %if &need_nothere=Y %then %do; title2 "Indexes: Comparison of recreate statement in source and target libraries"; title3 "Report 4 of 4"; proc print data=ods.nothere;run; %end; %if &need_nothere= %then %do; data ods.tempprint4; set ods.idx_comp; where not_migrated=. and diff_recreate=1; run; title2 "Indexes: Differences in recreate statement in source and target libraries"; title3 "Report 4 of 4"; proc print data=ods.tempprint4; var membr name source_recreate target_recreate; run; %end; %let need_nothere=; title2; title3; %mend compindex; %macro checkindex(showall); %local need_nothere; %let need_nothere=; /* create a dummy data set to ensure reports are printed */ /* if no differences found. */ data ods.nothere; length result $75.; result="No differences found"; run; %if &index_here=Y %then %do; %if &showall ne yes %then %do; %if &showall ne yes %then %do; %compindex; %end; %end; /* end Index showall ne yes loop */ %else %if &showall eq yes %then %do; title2 "Index data from source library"; proc print data=ods.lib1_idx; var membr name source_type source_recreate source_inactive; run; title2 "Index data from target library"; proc print data=ods.lib2_idx; var membr name source_type source_recreate source_inactive; run; %end; /* end Index showall eq yes loop */ %end; /* end index here loop */ /* clean up temporary data sets created by the macro */ proc datasets lib=ods nolist;delete nothere: temp:;run;quit; %mend checkindex; %macro odsview(lib); %local ishere mylib view_type; %do i=1 %to &lib1tview; %if %sysfunc(upcase(&lib))=LIB1 %then %do; %let ishere=1; %let mylib=source; %end; %else %if %sysfunc(upcase(&lib))=LIB2 %then %do; %let ishere=%sysfunc(exist(lib2.&&lib1view&i, view)); %let mylib=target; %end; %if &ishere=1 %then %do; *----------------------------------------------------------------* * Determine whether or not the view is a Data Step view. Only * create validation output for non-Data Step views. *----------------------------------------------------------------*; ods output attributes=ods.temp_v_atr(keep=label1 cvalue1 where=(label1 in ('Engine'))); ods listing close; proc contents data=lib1.&&lib1view&i;run; ods listing; data _null_; set ods.temp_v_atr; call symput("view_type", cvalue1); run; /* clean up */ proc datasets lib=ods nolist;delete temp_v_atr;run;quit; %if &view_type ne SASDSV %then %do; data ods.&mylib._&&lib1view&i; set &lib..&&lib1view&i; run; %end; %end; /*end ishere=1 */ %end; /* end do i=1 to total */ %mend odsview; *----------------------------------------------------------------* * At present, CHECKVIEW is intended to be run only on libraries * which contain data step views and SQL views. Use on libraries * which contain SAS/ACCESS views at your own risk. * * CHECKVIEW can produce validation output for only SQL views. * * CHECKVIEW compares a data set created from a view in the source * library to a data set created from the same view that's been * migrated to the target library. Please note these data sets * are in the ODS library, and are created using the ODS library's * engine. * * CHECKVIEW can also print a flexible number of observations * from each of the data sets. * To print the first 10 observations, submit the following: * * %checkview(printview=yes) * * You can also specify the number of observations by use of the * "obs" argument: * * %checkview(printview=yes, obs=50) * %checkview(printview=yes, obs=2) *----------------------------------------------------------------*; %macro checkview(printview, obs); %global view_type; %do i=1 %to &lib1tview; %let ishere=%sysfunc(exist(lib2.&&lib1view&i, view)); %if &ishere=1 %then %do; %let isherev1=%sysfunc(exist(ods.source_&&lib1view&i)); %if &isherev1=1 %then %do; %let isherev2=%sysfunc(exist(ods.target_&&lib1view&i)); %if &isherev2=1 %then %do; title2 "&&lib1view&i"; title3 "Number &i of %sysfunc(compress(&&lib1tview)) views in source library"; title4 "COMPARE of data sets created from source and target library versions of view"; proc compare base=ods.source_&&lib1view&i comp=ods.target_&&lib1view&i briefsummary listall; run; %if &printview=yes %then %do; %if &obs= %then %let obs=10; proc print data=ods.source_&&lib1view&i(obs=&obs); title3 "first &obs observations from data set created from source library version of %sysfunc(left(%sysfunc(trim(&&lib1view&i))))"; run; proc print data=ods.target_&&lib1view&i(obs=&obs); title3 "first &obs observations from data set created from target library version of %sysfunc(left(%sysfunc(trim(&&lib1view&i))))"; run; %end; %end; %if &isherev2 ne 1 %then %do; title2 "&&lib1view&i"; title3 "Number &i of %sysfunc(compress(&&lib1tview)) views in source library"; title4 "Member is present in LIB1 but was not MIGRATED to LIB2"; proc print data=ods.nothere noobs;run; %end; %end; %else %if &isherev1 ne 1 %then %do; data ods.nothere; length result $35.; result="No validation will be performed"; run; title2 "&&lib1view&i"; title3 "Number &i of %sysfunc(compress(&&lib1tview)) views in source library"; title4 "This view is a data step view, and cannot be validated by the checkview macro"; proc print data=ods.nothere noobs;run; %end; %end; %end; %mend checkview; %macro before; %checkver; %if "&too_old"="Y" %then %do; %goto exit; %end; %mig_in_lib(lib=lib1); %mig_source; %odsaudit(lib=lib1); %odscatalog(lib=lib1); %odsdata(lib=lib1); %odsindex(lib=LIB1); /*must be upper case */ %odsview(lib=lib1); %getemall; %put *-----------------------------------------------------------------------*; %put * If the LIB1, LIB2 and ODS libraries have been defined correctly, and ; %put * migrate_macros.sas has been complied in this SAS session, this ; %put * macro does the following: ; %put * ; %put * 1. Creates global macro (flag) variables for each memtype. ; %put * 2. Creates global macro variables containing the name of each member ; %put * in the source library before the MIGRATE. ; %put * 3. Creates a data set in the ODS library containing the information ; %put * described above. ; %put * 4. Creates data sets in the ODS library containing validation ; %put * information collected on members in the source library. ; %put *-----------------------------------------------------------------------*; %exit: %mend before; *----------------------------------------------------------------* * macro after includes all the macros that must be run after * the MIGRATE to produce default validation output. *----------------------------------------------------------------*; %macro after; %checkver; %if "&too_old"="Y" %then %do; %goto exit; %end; %getmvars; %mig_in_lib(lib=lib2); %mig_check_libs; %odsaudit(lib=lib2); %odscatalog(lib=lib2); %odsdata(lib=lib2); %odsindex(lib=LIB2); /*must be upper case */ %odsview(lib=lib2); %put *-----------------------------------------------------------------------*; %put * This macro does the following: ; %put * ; %put * 1. If the current SAS session is different than the session in which ; %put * the before macro was run, the global macro variables necessary for ; %put * validation are re-created. ; %put * 2. Creates a data set in the ODS library containing information about ; *put * the target library after migration. ; %put * 3. Outputs a side-by-side comparison of the contents of the source lib ; %put * before the MIGRATE with the contents of the target lib after the ; %put * MIGRATE in the SAS Output window. ; %put * 4. Outputs a side-by-side comparison of the contents of the source lib ; %put * before the MIGRATE with the contents of the source lib after the ; %put * MIGRATE in the SAS Output window. ; %put * 5. Creates data sets in the ODS library containing validation ; %put * information collected on members in the target library. ; %put *-----------------------------------------------------------------------*; %exit: %mend after; %macro checkem; %checkaudit; %checkcatalog; %checkdata; %checkindex; %checkview; %mend checkem; /*-------------------------------------------------------------- * * SYSTEMS: 9.1 or later * PRODUCT: MIGRATE * * CREATED: 25 November 2002. * UPDATE: * 21 November 2006 dawieh added proc sort to checkcatalog * macro to avoid insignificant proc compare * difference in format validation. *--------------------------------------------------------------*/