diff --git a/all.sas b/all.sas index b51cf41..9b8c9e3 100644 --- a/all.sas +++ b/all.sas @@ -7954,8 +7954,8 @@ run; %mend mp_guesspk;/** @file @brief Returns a unique hash for a dataset - @details Ignores metadata attributes, used only to hash values. Compared - datasets must be in the same order. + @details Ignores metadata attributes, used only to hash values. If used to + compare datasets, they must have their columns and rows in the same order. %mp_hashdataset(sashelp.class,outds=myhash) @@ -7970,7 +7970,10 @@ run; @li mf_getattrn.sas @li mf_getuniquename.sas @li mf_getvarlist.sas - @li mf_getvartype.sas + @li mp_md5.sas + +

Related Files

+ @li mp_hashdataset.test.sas @param [in] libds dataset to hash @param [in] salt= Provide a salt (could be, for instance, the dataset name) @@ -7988,51 +7991,52 @@ run; %macro mp_hashdataset( libds, - outds=, + outds=work._data_, salt=, iftrue=%str(1=1) )/*/STORE SOURCE*/; - %if not(%eval(%unquote(&iftrue))) %then %return; +%local keyvar /* roll up the md5 */ + prevkeyvar /* retain prev record md5 */ + lastvar /* last var in input ds */ + cvars nvars; - %if %mf_getattrn(&libds,NLOBS)=0 %then %do; - %put %str(WARN)ING: Dataset &libds is empty, or is not a dataset; - %end; - %else %if %mf_getattrn(&libds,NLOBS)<0 %then %do; - %put %str(ERR)OR: Dataset &libds is not a dataset; - %end; - %else %do; - %local keyvar /* roll up the md5 */ - prevkeyvar /* retain prev record md5 */ - lastvar /* last var in input ds */ - varlist var i; - /* avoid naming conflict for hash key vars */ - %let keyvar=%mf_getuniquename(); - %let prevkeyvar=%mf_getuniquename(); - %let lastvar=%mf_getuniquename(); - %let varlist=%mf_getvarlist(&libds); - data &outds(rename=(&keyvar=hashkey) keep=&keyvar); - length &prevkeyvar &keyvar $32; - retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)"; - set &libds end=&lastvar; - /* hash should include previous row */ - &keyvar=put(md5(&prevkeyvar - /* loop every column, hashing every individual value */ - %do i=1 %to %sysfunc(countw(&varlist)); - %let var=%scan(&varlist,&i,%str( )); - %if %mf_getvartype(&libds,&var)=C %then %do; - !!put(md5(trim(&var)),$hex32.) - %end; - %else %do; - !!put(md5(trim(put(&var*1,binary64.))),$hex32.) - %end; - %end; - ),$hex32.); - &prevkeyvar=&keyvar; - if &lastvar then output; - run; - %end; -%mend mp_hashdataset;/** +%if not(%eval(%unquote(&iftrue))) %then %return; + +/* avoid naming conflict for hash key vars */ +%let keyvar=%mf_getuniquename(); +%let prevkeyvar=%mf_getuniquename(); +%let lastvar=%mf_getuniquename(); + +%if %mf_getattrn(&libds,NLOBS)=0 %then %do; + data &outds; + length hashkey $32; + retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)"; + output; + stop; + run; + %put &sysmacroname: Dataset &libds is empty, or is not a dataset; + %put &sysmacroname: hashkey of &outds is based on salt (&salt) only; +%end; +%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do; + %put %str(ERR)OR: Dataset &libds is not a dataset; +%end; +%else %do; + data &outds(rename=(&keyvar=hashkey) keep=&keyvar); + length &prevkeyvar &keyvar $32; + retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)"; + set &libds end=&lastvar; + /* hash should include previous row */ + &keyvar=%mp_md5( + cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar, + nvars=%mf_getvarlist(&libds,typefilter=N) + ); + &prevkeyvar=&keyvar; + if &lastvar then output; + run; +%end; +%mend mp_hashdataset; +/** @file @brief Performs a wrapped \%include @details This macro wrapper is necessary if you need your included code to @@ -19224,6 +19228,7 @@ run; data _null_; set &tempds; if not (upcase(name) =:"DATA"); /* ignore temp datasets */ + if not (upcase(name)=:"_DATA_"); i+1; call symputx(cats('wt',i),name,'l'); call symputx('wtcnt',i,'l'); diff --git a/base/mp_hashdataset.sas b/base/mp_hashdataset.sas index 4904a93..5adbaa9 100644 --- a/base/mp_hashdataset.sas +++ b/base/mp_hashdataset.sas @@ -1,8 +1,8 @@ /** @file @brief Returns a unique hash for a dataset - @details Ignores metadata attributes, used only to hash values. Compared - datasets must be in the same order. + @details Ignores metadata attributes, used only to hash values. If used to + compare datasets, they must have their columns and rows in the same order. %mp_hashdataset(sashelp.class,outds=myhash) @@ -17,7 +17,10 @@ @li mf_getattrn.sas @li mf_getuniquename.sas @li mf_getvarlist.sas - @li mf_getvartype.sas + @li mp_md5.sas + +

Related Files

+ @li mp_hashdataset.test.sas @param [in] libds dataset to hash @param [in] salt= Provide a salt (could be, for instance, the dataset name) @@ -35,48 +38,48 @@ %macro mp_hashdataset( libds, - outds=, + outds=work._data_, salt=, iftrue=%str(1=1) )/*/STORE SOURCE*/; - %if not(%eval(%unquote(&iftrue))) %then %return; +%local keyvar /* roll up the md5 */ + prevkeyvar /* retain prev record md5 */ + lastvar /* last var in input ds */ + cvars nvars; - %if %mf_getattrn(&libds,NLOBS)=0 %then %do; - %put %str(WARN)ING: Dataset &libds is empty, or is not a dataset; - %end; - %else %if %mf_getattrn(&libds,NLOBS)<0 %then %do; - %put %str(ERR)OR: Dataset &libds is not a dataset; - %end; - %else %do; - %local keyvar /* roll up the md5 */ - prevkeyvar /* retain prev record md5 */ - lastvar /* last var in input ds */ - varlist var i; - /* avoid naming conflict for hash key vars */ - %let keyvar=%mf_getuniquename(); - %let prevkeyvar=%mf_getuniquename(); - %let lastvar=%mf_getuniquename(); - %let varlist=%mf_getvarlist(&libds); - data &outds(rename=(&keyvar=hashkey) keep=&keyvar); - length &prevkeyvar &keyvar $32; - retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)"; - set &libds end=&lastvar; - /* hash should include previous row */ - &keyvar=put(md5(&prevkeyvar - /* loop every column, hashing every individual value */ - %do i=1 %to %sysfunc(countw(&varlist)); - %let var=%scan(&varlist,&i,%str( )); - %if %mf_getvartype(&libds,&var)=C %then %do; - !!put(md5(trim(&var)),$hex32.) - %end; - %else %do; - !!put(md5(trim(put(&var*1,binary64.))),$hex32.) - %end; - %end; - ),$hex32.); - &prevkeyvar=&keyvar; - if &lastvar then output; - run; - %end; -%mend mp_hashdataset; \ No newline at end of file +%if not(%eval(%unquote(&iftrue))) %then %return; + +/* avoid naming conflict for hash key vars */ +%let keyvar=%mf_getuniquename(); +%let prevkeyvar=%mf_getuniquename(); +%let lastvar=%mf_getuniquename(); + +%if %mf_getattrn(&libds,NLOBS)=0 %then %do; + data &outds; + length hashkey $32; + retain hashkey "%sysfunc(md5(%str(&salt)),$hex32.)"; + output; + stop; + run; + %put &sysmacroname: Dataset &libds is empty, or is not a dataset; + %put &sysmacroname: hashkey of &outds is based on salt (&salt) only; +%end; +%else %if %mf_getattrn(&libds,NLOBS)<0 %then %do; + %put %str(ERR)OR: Dataset &libds is not a dataset; +%end; +%else %do; + data &outds(rename=(&keyvar=hashkey) keep=&keyvar); + length &prevkeyvar &keyvar $32; + retain &prevkeyvar "%sysfunc(md5(%str(&salt)),$hex32.)"; + set &libds end=&lastvar; + /* hash should include previous row */ + &keyvar=%mp_md5( + cvars=%mf_getvarlist(&libds,typefilter=C) &prevkeyvar, + nvars=%mf_getvarlist(&libds,typefilter=N) + ); + &prevkeyvar=&keyvar; + if &lastvar then output; + run; +%end; +%mend mp_hashdataset; diff --git a/sasjs/sasjsconfig.json b/sasjs/sasjsconfig.json index a88f06c..564ed75 100644 --- a/sasjs/sasjsconfig.json +++ b/sasjs/sasjsconfig.json @@ -71,7 +71,7 @@ }, { "name": "server", - "serverUrl": "https://sas.analytium.co.uk:5000", + "serverUrl": " ", "serverType": "SASJS", "httpsAgentOptions": { "allowInsecureRequests": false diff --git a/server/ms_webout.sas b/server/ms_webout.sas index a566116..951c85a 100644 --- a/server/ms_webout.sas +++ b/server/ms_webout.sas @@ -114,6 +114,7 @@ data _null_; set &tempds; if not (upcase(name) =:"DATA"); /* ignore temp datasets */ + if not (upcase(name)=:"_DATA_"); i+1; call symputx(cats('wt',i),name,'l'); call symputx('wtcnt',i,'l'); diff --git a/tests/crossplatform/mp_hashdataset.test.sas b/tests/crossplatform/mp_hashdataset.test.sas new file mode 100644 index 0000000..76c3dfb --- /dev/null +++ b/tests/crossplatform/mp_hashdataset.test.sas @@ -0,0 +1,59 @@ +/** + @file + @brief Testing mp_hashdataset.sas macro + +

SAS Macros

+ @li mp_hashdataset.sas + @li mp_assert.sas + +**/ + +/* test 1 - regular DS */ +data work.test; + set sashelp.vextfl; + missval=.; + misscval=''; +run; + +%mp_assertscope(SNAPSHOT) +%mp_hashdataset(test) +%mp_assertscope(COMPARE) + +%mp_assert( + iftrue=(&syscc=0), + desc=Regular test works, + outds=work.test_results +) + +%mp_hashdataset(test,outds=work.test2) + +%mp_assert( + iftrue=(&syscc=0), + desc=hash with output runs without errors, + outds=work.test_results +) + +%mp_assert( + iftrue=(%mf_nobs(work.test2)=1), + desc=output has 1 row, + outds=work.test_results +) + + +data work.test3a; + set work.test; + stop; +run; +%mp_hashdataset(test3a,outds=work.test3b) + +%mp_assert( + iftrue=(&syscc=0), + desc=hash with zero-row input runs without errors, + outds=work.test_results +) + +%mp_assert( + iftrue=(%mf_nobs(work.test3b)=1), + desc=test 3 output has 1 row, + outds=work.test_results +)