102%macro mp_derivedvars(libds=,
106 outvars=work.derived_varlist,
113%local physlist phys_sp derived_err left nready nplaced
114 cyc_start cyc_cur cyc_next cyc_path cyc_seen cyc_i
115 ds_cfg ds_byname ds_bad ds_baddep ds_dup ds_dep ds_ref ds_stale
116 ds_undecl ds_self ds_edge ds_todo ds_ready ds_placed ds_srt ds_errpart;
120%let ds_cfg=%mf_getuniquename(prefix=dvcfg);
121%let ds_byname=%mf_getuniquename(prefix=dvbyn);
122%let ds_bad=%mf_getuniquename(prefix=dvbad);
123%let ds_baddep=%mf_getuniquename(prefix=dvbdp);
124%let ds_dup=%mf_getuniquename(prefix=dvdup);
125%let ds_dep=%mf_getuniquename(prefix=dvdep);
126%let ds_ref=%mf_getuniquename(prefix=dvref);
127%let ds_stale=%mf_getuniquename(prefix=dvstl);
128%let ds_undecl=%mf_getuniquename(prefix=dvund);
129%let ds_self=%mf_getuniquename(prefix=dvsfl);
130%let ds_edge=%mf_getuniquename(prefix=dvedg);
131%let ds_todo=%mf_getuniquename(prefix=dvtd);
132%let ds_ready=%mf_getuniquename(prefix=dvrdy);
133%let ds_placed=%mf_getuniquename(prefix=dvpld);
134%let ds_srt=%mf_getuniquename(prefix=dvsrt);
135%let ds_errpart=%mf_getuniquename(prefix=dverr);
137%mp_abort(iftrue= (%length(&libds)=0 or %length(&control)=0)
139 ,msg=%str(Missing: libds control)
141%mp_abort(iftrue= (&outref = 0)
143 ,msg=%str(Please provide an outref
for the generated statements)
145%mp_abort(iftrue= (%mf_existds(libds=&libds) ne 1)
147 ,msg=%str(Target table not found: &libds)
152filename &outref temp lrecl=32767;
158data &outvars(keep=name);
166data &errds(keep=name reason);
167 length name $32 reason $200;
173%
if %mf_existds(libds=&control) ne 1 %then %
do;
174 %put &sysmacroname: &control not found - no derived variables;
183 %
if %superq(where) ne 0 %then %
do;
186 where also upcase(libref)=
"%upcase(%scan(&libds,1,.))"
187 and upcase(dsn)=
"%upcase(%scan(&libds,2,.))";
188 length name $32 logic $4000 fmt $32 deps $4000 lbl $256;
189 name=upcase(strip(var_nm));
190 logic=strip(var_logic);
191 type=upcase(strip(var_type));
193 fmt=strip(upcase(var_fmt));
194 deps=upcase(strip(var_deps));
197 %
if %mf_existvar(&control,VAR_LABEL) gt 0 %then %
do;
198 lbl=translate(strip(var_label),
' ',
'0D0A'x);
205%
if %mf_nobs(work.&ds_cfg.)=0 %then %
return;
207proc sort data=work.&ds_cfg. out=work.&ds_byname.;
213%let physlist=%upcase(%mf_getvarlist(&libds,dlm=%str(,),quote=SINGLE));
214%let phys_sp=%upcase(%mf_getvarlist(&libds));
221%mp_derivedvarcheck(inds=work.&ds_byname.
227data work.&ds_dup.(keep=name reason);
228 set work.&ds_byname.;
231 if first.name and not last.name;
232 reason=
'VAR_NM is defined more than once';
235proc append base=work.&ds_bad. data=work.&ds_dup.;
242 set work.&ds_byname.;
244 do i=1 to countw(deps,
' ');
245 dep_nm=scan(deps,i,
' ');
246 if not missing(dep_nm) then output;
256 set work.&ds_byname.;
257 length l $4000 tok $32;
258 l=prxchange(
"s/'([^']|'')*'//",-1,logic);
259 l=prxchange(
's/"([^"]|"")*"//',-1,l);
260 l=prxchange(
's/[^A-Za-z0-9_]/ /',-1,upcase(l));
261 do i=1 to countw(l,
' ');
263 if not missing(tok) then output;
271create table work.&ds_baddep. as
272 select d.name, d.dep_nm
274 where d.dep_nm not in (select name from work.&ds_byname.)
277 %
if %length(&physlist)>0 %then %
do;
278 and d.dep_nm not in (%unquote(&physlist))
283create table work.&ds_stale. as
284 select d.name, d.dep_nm
287 select 1 from work.&ds_ref. r
288 where r.name=d.name and r.tok=d.dep_nm
293create table work.&ds_undecl. as
294 select distinct r.name, r.tok as dep_nm
296 where r.tok in (select name from work.&ds_byname.)
299 select 1 from work.&ds_dep. d
300 where d.name=r.name and d.dep_nm=r.tok
304create table work.&ds_self. as
305 select name, dep_nm from work.&ds_dep.
314%
if "%upcase(&abort)"=
"NO" %then %
do;
315 data work.&ds_errpart.;
316 set work.&ds_baddep.;
318 reason=cats(
'VAR_DEPS lists ',dep_nm
319 ,
', which is not a column of the table or a derived variable');
322 proc append base=&errds data=work.&ds_errpart. force;
325 data work.&ds_errpart.;
328 reason=
'VAR_DEPS may not list itself';
331 proc append base=&errds data=work.&ds_errpart. force;
334 data work.&ds_errpart.;
335 set work.&ds_undecl.;
337 reason=cats(
'VAR_LOGIC references ',dep_nm,
', which is not in VAR_DEPS');
340 proc append base=&errds data=work.&ds_errpart. force;
343 data work.&ds_errpart.;
346 reason=cats(
'VAR_DEPS lists ',dep_nm,
', which VAR_LOGIC does not'
350 proc append base=&errds data=work.&ds_errpart. force;
353 data work.&ds_errpart.(keep=name reason);
356 proc append base=&errds data=work.&ds_errpart. force;
360%
if "%upcase(&abort)"=
"NO" and %mf_nobs(&errds)>0 %then %
return;
363 set work.&ds_baddep.(obs=1);
364 call symputx(
'derived_err'
365 ,cats(name,
': VAR_DEPS lists ',dep_nm
366 ,
', which is not a column of the table or a derived variable'));
369%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_baddep.))
371 ,msg=%superq(derived_err)
375 set work.&ds_self.(obs=1);
376 call symputx(
'derived_err',cats(name,
': VAR_DEPS may not list itself'));
379%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_self.))
381 ,msg=%superq(derived_err)
385 set work.&ds_undecl.(obs=1);
386 call symputx(
'derived_err'
387 ,cats(name,
': VAR_LOGIC references ',dep_nm
388 ,
', which is not in VAR_DEPS'));
391%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_undecl.))
393 ,msg=%superq(derived_err)
397 set work.&ds_stale.(obs=1);
398 call symputx(
'derived_err'
399 ,cats(name,
': VAR_DEPS lists ',dep_nm
400 ,
', which VAR_LOGIC does not reference'));
403%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_stale.))
405 ,msg=%superq(derived_err)
410 putlog
'ERR' 'OR: derived variable config: ' name= reason=;
413 set work.&ds_bad.(obs=1);
414 call symputx(
'derived_err',cats(name,
': ',reason));
416%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_bad.))
418 ,msg=%str(Invalid derived variable configuration - &derived_err)
428create table work.&ds_edge. as
429 select d.name as child, d.dep_nm as parent
431 where d.dep_nm in (select name from work.&ds_byname.);
434data work.&ds_todo.(keep=name);
435 set work.&ds_byname.;
438data work.&ds_placed.(keep=name seq);
445%let left=%mf_nobs(work.&ds_todo.);
451 create table work.&ds_ready. as
453 from work.&ds_todo. t
455 select 1 from work.&ds_edge. e
456 inner join work.&ds_todo. t2 on e.parent=t2.name
462 %let nready=%mf_nobs(work.&ds_ready.);
463 %if &nready=0 %then %do;
467 set work.&ds_todo.(obs=1);
468 call symputx('cyc_start',name);
470 %let cyc_path=&cyc_start;
471 %let cyc_cur=&cyc_start;
472 %let cyc_seen=%str(.&cyc_start..);
473 %do cyc_i=1 %to &left;
476 select e.parent into: cyc_next
477 from work.&ds_edge. e
478 where e.child=
"&cyc_cur"
479 and e.parent in (select name from work.&ds_todo.);
481 %if %length(&cyc_next)=0 %then %let cyc_i=&left;
483 %let cyc_path=&cyc_path. -> &cyc_next;
484 %if %index(&cyc_seen,%str(.&cyc_next..))>0 %then %let cyc_i=&left;
486 %let cyc_seen=&cyc_seen.&cyc_next..;
487 %let cyc_cur=&cyc_next;
497 %if &cyc_found=0 %then %do;
500 data work.&ds_ready.;
504 proc append base=work.&ds_placed. data=work.&ds_ready. force;
506 %let nplaced=%eval(&nplaced+&nready);
509 delete from work.&ds_todo.
510 where name in (select name from work.&ds_ready.);
513 %let left=%mf_nobs(work.&ds_todo.);
517%if
"%upcase(&abort)"=
"NO" and &cyc_found=1 %then %do;
519 length name $32 reason $200;
521 reason='VAR_DEPS contains a cycle';
527%mp_abort(iftrue= (&cyc_found=1)
529 ,msg=%str(VAR_DEPS contains a cycle: &cyc_path)
532proc sort data=work.&ds_placed.;
537 merge work.&ds_placed.(in=in1) work.&ds_byname.(in=in2);
542proc sort data=work.&ds_srt.;
553 length stmt $4600 label $512 fmt2 $49;
559 if not missing(lbl) then label=lbl;
560 else label=cats(name,
'=',translate(logic,
' ','0D0A'x));
561 label=tranwrd(label,
"'",
"''");
562 if length(label)>256 then do;
563 label=substr(label,1,256);
564 if mod(count(label,
"'"),2) ne 0 then label=substr(label,1,255);
566 if not missing(fmt) and index(fmt,
'.')=0 then fmt=cats(fmt,
'.');
568 if missing(fmt) then fmt2=cats(
'$',len,
'.');
570 stmt=catx(
' ','attrib',name,cats('length=$',len)
571 ,cats(
"label='",label,
"'"),cats('format=',fmt2,
';'));
574 if missing(fmt) then fmt2=cats(len,
'.');
576 stmt=catx(
' ','attrib',name,cats('length=',len)
577 ,cats(
"label='",label,
"'"),cats('format=',fmt2,
';'));
580 stmt=cats(name,' = ',logic,
';');
584%mp_abort(iftrue= (&syscc ne 0)
586 ,msg=%str(Unable to generate derived variables for &libds)
589data &outvars(keep=name);
593%if %length(&outview)>0 and &outview ne 0 %then %do;
594 data &outview / view=&outview;
598 %mp_abort(iftrue= (&syscc ne 0)
600 ,msg=%str(Unable to build derived view &outview for &libds)