85%macro mp_derivedvars(libds=,
89 outvars=work.derived_varlist,
94%local physlist derived_err resv_list i w left nready nplaced
95 cyc_start cyc_cur cyc_next cyc_path cyc_seen cyc_i
96 ds_cfg ds_byname ds_bad ds_baddep ds_dup ds_dep ds_ref ds_stale
97 ds_undecl ds_self ds_edge ds_todo ds_ready ds_placed ds_srt;
101%let ds_cfg=%mf_getuniquename(prefix=dvcfg);
102%let ds_byname=%mf_getuniquename(prefix=dvbyn);
103%let ds_bad=%mf_getuniquename(prefix=dvbad);
104%let ds_baddep=%mf_getuniquename(prefix=dvbdp);
105%let ds_dup=%mf_getuniquename(prefix=dvdup);
106%let ds_dep=%mf_getuniquename(prefix=dvdep);
107%let ds_ref=%mf_getuniquename(prefix=dvref);
108%let ds_stale=%mf_getuniquename(prefix=dvstl);
109%let ds_undecl=%mf_getuniquename(prefix=dvund);
110%let ds_self=%mf_getuniquename(prefix=dvsfl);
111%let ds_edge=%mf_getuniquename(prefix=dvedg);
112%let ds_todo=%mf_getuniquename(prefix=dvtd);
113%let ds_ready=%mf_getuniquename(prefix=dvrdy);
114%let ds_placed=%mf_getuniquename(prefix=dvpld);
115%let ds_srt=%mf_getuniquename(prefix=dvsrt);
117%mp_abort(iftrue= (%length(&libds)=0 or %length(&control)=0)
119 ,msg=%str(Missing: libds control)
121%mp_abort(iftrue= (&outref = 0)
123 ,msg=%str(Please provide an outref for the generated statements)
125%mp_abort(iftrue= (%mf_existds(libds=&libds) ne 1)
127 ,msg=%str(Target table not found: &libds)
132filename &outref temp lrecl=32767;
138data &outvars(keep=name);
144%
if %mf_existds(libds=&control) ne 1 %then %
do;
145 %put &sysmacroname: &control not found - no derived variables;
154 %
if %superq(where) ne 0 %then %
do;
157 where also upcase(libref)=
"%upcase(%scan(&libds,1,.))"
158 and upcase(dsn)=
"%upcase(%scan(&libds,2,.))";
159 length name $32 logic $4000 fmt $32 deps $4000 lbl $256;
160 name=upcase(strip(var_nm));
161 logic=strip(var_logic);
162 type=upcase(strip(var_type));
164 fmt=strip(upcase(var_fmt));
165 deps=upcase(strip(var_deps));
168 %
if %mf_existvar(&control,VAR_LABEL) gt 0 %then %
do;
169 lbl=translate(strip(var_label),
' ',
'0D0A'x);
176%
if %mf_nobs(work.&ds_cfg.)=0 %then %
return;
178proc sort data=work.&ds_cfg. out=work.&ds_byname.;
182%let physlist=%upcase(%mf_getvarlist(&libds,dlm=%str(,),quote=SINGLE));
186%
if "&reserved" ne
"0" %then %
do i=1 %to %sysfunc(countw(&reserved,%str( )));
187 %let w=%scan(&reserved,&i,%str( ));
188 %let resv_list=&resv_list.%str(,)%str(%
")%upcase(&w)%str(%");
190%
if %length(&resv_list)>0 %then %let resv_list=%substr(&resv_list,2);
197 set work.&ds_byname.;
199 call missing(reason);
201 if name=
'' then reason=
'VAR_NM is required';
202 else if verify(strip(name),
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')>0
203 then reason=
'VAR_NM must be a valid SAS name';
204 else if verify(substr(name,1,1),
'0123456789')=0
205 then reason=
'VAR_NM must not start with a digit';
206 %
if %length(&physlist)>0 %then %
do;
207 else if name in (%unquote(&physlist))
208 then reason=
'VAR_NM collides with a column in the target table';
210 %
if %length(&resv_list)>0 %then %
do;
211 else if name in (%unquote(&resv_list))
212 then reason=
'VAR_NM is reserved';
214 else if type not in (
'C',
'N')
215 then reason='VAR_TYPE must be C or N';
216 else if missing(len) or len ne
int(len) or len<1
217 then reason='VAR_LEN must be a positive integer';
218 else if type='C' and len>32767
219 then reason='VAR_LEN exceeds 32767 for a character variable';
220 else if type='N' and len>8
221 then reason='VAR_LEN exceeds 8 for a numeric variable';
222 else if index(fmt,';')>0 or indexc(fmt,'&%')>0
224 then reason='VAR_FMT must be a format name, without ; & % or
225 else if verify(strip(fmt),'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$.')>0
226 then reason='VAR_FMT must be a format name';
227 else if missing(logic)
228 then reason='VAR_LOGIC is required';
229 else if index(logic,';')>0
230 then reason='VAR_LOGIC must be a single expression - remove the ;';
231 else if indexc(logic,'&%')>0
232 then reason='VAR_LOGIC must not contain macro triggers';
233 else if index(logic,'')>0
234 then reason='VAR_LOGIC must not contain comments';
236 if missing(reason)=0 then output;
240 set work.&ds_byname.;
243 if first.name and not last.name;
244 reason='VAR_NM is defined more than once';
247proc append base=work.&ds_bad. data=work.&ds_dup.;
254 set work.&ds_byname.;
256 do i=1 to countw(deps,' ');
257 dep_nm=scan(deps,i,' ');
258 if not missing(dep_nm) then output;
268 set work.&ds_byname.;
269 length l $4000 tok $32;
270 l=prxchange("s/'([^']|'')*'
271 l=prxchange('s/"([^"]|"")*"
272 l=prxchange('s/[^A-Za-z0-9_]/ /',-1,upcase(l));
273 do i=1 to countw(l,' ');
275 if not missing(tok) then output;
283create table work.&ds_baddep. as
284 select d.name, d.dep_nm
286 where d.dep_nm not in (select name from work.&ds_byname.)
289 %if %length(&physlist)>0 %then %do;
290 and d.dep_nm not in (%unquote(&physlist))
295create table work.&ds_stale. as
296 select d.name, d.dep_nm
299 select 1 from work.&ds_ref. r
300 where r.name=d.name and r.tok=d.dep_nm
305create table work.&ds_undecl. as
306 select distinct r.name, r.tok as dep_nm
308 where r.tok in (select name from work.&ds_byname.)
311 select 1 from work.&ds_dep. d
312 where d.name=r.name and d.dep_nm=r.tok
316create table work.&ds_self. as
317 select name, dep_nm from work.&ds_dep.
322 set work.&ds_baddep.(obs=1);
323 call symputx('derived_err'
324 ,cats(name,': VAR_DEPS lists ',dep_nm
325 ,', which is not a column of the table or a derived variable'));
328%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_baddep.))
330 ,msg=%superq(derived_err)
334 set work.&ds_self.(obs=1);
335 call symputx('derived_err',cats(name,': VAR_DEPS may not list itself'));
338%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_self.))
340 ,msg=%superq(derived_err)
344 set work.&ds_undecl.(obs=1);
345 call symputx('derived_err'
346 ,cats(name,': VAR_LOGIC references ',dep_nm
347 ,', which is not in VAR_DEPS'));
350%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_undecl.))
352 ,msg=%superq(derived_err)
356 set work.&ds_stale.(obs=1);
357 call symputx('derived_err'
358 ,cats(name,': VAR_DEPS lists ',dep_nm
359 ,', which VAR_LOGIC does not reference'));
362%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_stale.))
364 ,msg=%superq(derived_err)
369 putlog 'ERR' 'OR: derived variable config: ' name= reason=;
372 set work.&ds_bad.(obs=1);
373 call symputx('derived_err',cats(name,': ',reason));
375%mp_abort(iftrue= (0 < %mf_nobs(work.&ds_bad.))
377 ,msg=%str(Invalid derived variable configuration - &derived_err)
387create table work.&ds_edge. as
388 select d.name as child, d.dep_nm as parent
390 where d.dep_nm in (select name from work.&ds_byname.);
393data work.&ds_todo.(keep=name);
394 set work.&ds_byname.;
397data work.&ds_placed.(keep=name seq);
404%let left=%mf_nobs(work.&ds_todo.);
410 create table work.&ds_ready. as
412 from work.&ds_todo. t
414 select 1 from work.&ds_edge. e
415 inner join work.&ds_todo. t2 on e.parent=t2.name
421 %let nready=%mf_nobs(work.&ds_ready.);
422 %if &nready=0 %then %do;
426 set work.&ds_todo.(obs=1);
427 call symputx('cyc_start',name);
429 %let cyc_path=&cyc_start;
430 %let cyc_cur=&cyc_start;
431 %let cyc_seen=%str(.&cyc_start..);
432 %do cyc_i=1 %to &left;
435 select e.parent into: cyc_next
436 from work.&ds_edge. e
437 where e.child="&cyc_cur"
438 and e.parent in (select name from work.&ds_todo.);
440 %if %length(&cyc_next)=0 %then %let cyc_i=&left;
442 %let cyc_path=&cyc_path. -> &cyc_next;
443 %if %index(&cyc_seen,%str(.&cyc_next..))>0 %then %let cyc_i=&left;
445 %let cyc_seen=&cyc_seen.&cyc_next..;
446 %let cyc_cur=&cyc_next;
456 %if &cyc_found=0 %then %do;
459 data work.&ds_ready.;
463 proc append base=work.&ds_placed. data=work.&ds_ready. force;
465 %let nplaced=%eval(&nplaced+&nready);
468 delete from work.&ds_todo.
469 where name in (select name from work.&ds_ready.);
472 %let left=%mf_nobs(work.&ds_todo.);
476%mp_abort(iftrue= (&cyc_found=1)
478 ,msg=%str(VAR_DEPS contains a cycle: &cyc_path)
481proc sort data=work.&ds_placed.;
486 merge work.&ds_placed.(in=in1) work.&ds_byname.(in=in2);
491proc sort data=work.&ds_srt.;
502 length stmt $4600 label $512 fmt2 $49;
508 if not missing(lbl) then label=lbl;
509 else label=cats(name,'=',translate(logic,' ','0D0A'x));
510 label=tranwrd(label,"'","''");
511 if length(label)>256 then do;
512 label=substr(label,1,256);
513 if mod(count(label,"'"),2) ne 0 then label=substr(label,1,255);
515 if not missing(fmt) and index(fmt,'.')=0 then fmt=cats(fmt,'.');
517 if missing(fmt) then fmt2=cats('$',len,'.');
519 stmt=catx(' ','attrib',name,cats('length=$',len)
520 ,cats("label='",label,"'"),cats('format=',fmt2,';'));
523 if missing(fmt) then fmt2=cats(len,'.');
525 stmt=catx(' ','attrib',name,cats('length=',len)
526 ,cats("label='",label,"'"),cats('format=',fmt2,';'));
529 stmt=cats(name,' = ',logic,';');
533%mp_abort(iftrue= (&syscc ne 0)
535 ,msg=%str(Unable to generate derived variables for &libds)
538data &outvars(keep=name);
542%if %length(&outview)>0 and &outview ne 0 %then %do;
543 data &outview / view=&outview;
547 %mp_abort(iftrue= (&syscc ne 0)
549 ,msg=%str(Unable to build derived view &outview for &libds)