diff --git a/all.sas b/all.sas index 6541be2..ad94655 100644 --- a/all.sas +++ b/all.sas @@ -8717,14 +8717,13 @@ options @source https://github.com/sasjs/core **/ - %macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ,engine=DATASTEP ,missing=NULL ,showmeta=N ,maxobs=MAX )/*/STORE SOURCE*/; -%local tempds colinfo fmtds i numcols stmt_obs; +%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; %let numcols=0; %if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); @@ -8783,10 +8782,14 @@ options end; /* 32 char unique name */ newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27); + maxlenv='maxlen'!!substr(cats(put(md5(name),$hex32.)),1,26); call symputx(cats('name',_n_),name,'l'); call symputx(cats('newname',_n_),newname,'l'); + call symputx(cats('maxlenv',_n_),maxlenv,'l'); call symputx(cats('length',_n_),length,'l'); + /* overwritten when fmt=Y */ + call symputx(cats('fmtlen',_n_),min(32767,ceil((length+3)*1.2)),'l'); call symputx(cats('fmt',_n_),fmt,'l'); call symputx(cats('type',_n_),type,'l'); call symputx(cats('typelong',_n_),typelong,'l'); @@ -8794,6 +8797,9 @@ options run; %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); + proc sql; + select count(*) into: lastobs from &ds; + %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); %if &engine=PROCJSON %then %do; %if &missing=STRING %then %do; @@ -8830,24 +8836,49 @@ options %end; %if &fmt=Y %then %do; - data _data_; + %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); + /* need to find max length of formatted values */ + data _data_(compress=char); /* rename on entry */ set &ds(rename=( %do i=1 %to &numcols; &&name&i=&&newname&i %end; )); + &stmt_obs; + /* formatted values can be up to length 32767 */ + length + %do i=1 %to &numcols; + &&name&i + %end; + $32767; + retain &tempvar + %do i=1 %to &numcols; + &&maxlenv&i + %end; + 0; + drop &tempvar + %do i=1 %to &numcols; + &&newname&i &&maxlenv&i + %end; + ; %do i=1 %to &numcols; - /* formatted values can be up to length 32767 */ - length &&name&i $32767; %if &&typelong&i=num %then %do; - &&name&i=left(put(&&newname&i,&&fmt&i)); + &&name&i=cats(put(&&newname&i,&&fmt&i)); %end; %else %do; &&name&i=put(&&newname&i,&&fmt&i); %end; - drop &&newname&i; + /* grab max length of each value as we move down the data step */ + &tempvar=length(&&name&i); + if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; %end; + if _n_=&lastobs then do; + /* add a 20% buffer in case of special chars that need to be escaped */ + %do i=1 %to &numcols; + call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),'l'); + %end; + end; if _error_ then call symputx('syscc',1012); run; %let fmtds=&syslast; @@ -8865,12 +8896,15 @@ options %end; other = [best.]; + /* configure varlenchk - as we are explicitly shortening the variables */ + %let optval=%sysfunc(getoption(varlenchk)); + options varlenchk=NOWARN; data &tempds; attrib _all_ label=''; %do i=1 %to &numcols; %if &&typelong&i=char or &fmt=Y %then %do; - length &&name&i $32767; - format &&name&i $32767.; + length &&name&i $&&fmtlen&i...; + format &&name&i $&&fmtlen&i...; %end; %end; %if &fmt=Y %then %do; @@ -8903,6 +8937,7 @@ options %end; %end; run; + options varlenchk=&optval; filename _sjs3 temp lrecl=131068 ; data _null_; @@ -15134,14 +15169,13 @@ data _null_; file sasjs lrecl=3000 ; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; /* WEBOUT BEGIN */ - put ' '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put ' ,engine=DATASTEP '; put ' ,missing=NULL '; put ' ,showmeta=N '; put ' ,maxobs=MAX '; put ')/*/STORE SOURCE*/; '; - put '%local tempds colinfo fmtds i numcols stmt_obs; '; + put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%let numcols=0; '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put ' '; @@ -15200,10 +15234,14 @@ data _null_; put ' end; '; put ' /* 32 char unique name */ '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); '; put ' '; put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' /* overwritten when fmt=Y */ '; + put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; @@ -15211,6 +15249,9 @@ data _null_; put ' run; '; put ' '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' proc sql; '; + put ' select count(*) into: lastobs from &ds; '; + put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); '; put ' '; put ' %if &engine=PROCJSON %then %do; '; put ' %if &missing=STRING %then %do; '; @@ -15247,24 +15288,49 @@ data _null_; put ' %end; '; put ' '; put ' %if &fmt=Y %then %do; '; - put ' data _data_; '; + put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' /* need to find max length of formatted values */ '; + put ' data _data_(compress=char); '; put ' /* rename on entry */ '; put ' set &ds(rename=( '; put ' %do i=1 %to &numcols; '; put ' &&name&i=&&newname&i '; put ' %end; '; put ' )); '; + put ' &stmt_obs; '; + put ' /* formatted values can be up to length 32767 */ '; + put ' length '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i '; + put ' %end; '; + put ' $32767; '; + put ' retain &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&maxlenv&i '; + put ' %end; '; + put ' 0; '; + put ' drop &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&newname&i &&maxlenv&i '; + put ' %end; '; + put ' ; '; put ' %do i=1 %to &numcols; '; - put ' /* formatted values can be up to length 32767 */ '; - put ' length &&name&i $32767; '; put ' %if &&typelong&i=num %then %do; '; - put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); '; put ' %end; '; put ' %else %do; '; put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' %end; '; - put ' drop &&newname&i; '; + put ' /* grab max length of each value as we move down the data step */ '; + put ' &tempvar=length(&&name&i); '; + put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; '; put ' %end; '; + put ' if _n_=&lastobs then do; '; + put ' /* add a 20% buffer in case of special chars that need to be escaped */ '; + put ' %do i=1 %to &numcols; '; + put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); '; + put ' %end; '; + put ' end; '; put ' if _error_ then call symputx(''syscc'',1012); '; put ' run; '; put ' %let fmtds=&syslast; '; @@ -15282,12 +15348,15 @@ data _null_; put ' %end; '; put ' other = [best.]; '; put ' '; + put ' /* configure varlenchk - as we are explicitly shortening the variables */ '; + put ' %let optval=%sysfunc(getoption(varlenchk)); '; + put ' options varlenchk=NOWARN; '; put ' data &tempds; '; put ' attrib _all_ label=''''; '; put ' %do i=1 %to &numcols; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; '; - put ' length &&name&i $32767; '; - put ' format &&name&i $32767.; '; + put ' length &&name&i $&&fmtlen&i...; '; + put ' format &&name&i $&&fmtlen&i...; '; put ' %end; '; put ' %end; '; put ' %if &fmt=Y %then %do; '; @@ -15320,6 +15389,7 @@ data _null_; put ' %end; '; put ' %end; '; put ' run; '; + put ' options varlenchk=&optval; '; put ' '; put ' filename _sjs3 temp lrecl=131068 ; '; put ' data _null_; '; @@ -19999,14 +20069,13 @@ data _null_; file &sasjsref termstr=crlf lrecl=512; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; /* WEBOUT BEGIN */ - put ' '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put ' ,engine=DATASTEP '; put ' ,missing=NULL '; put ' ,showmeta=N '; put ' ,maxobs=MAX '; put ')/*/STORE SOURCE*/; '; - put '%local tempds colinfo fmtds i numcols stmt_obs; '; + put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%let numcols=0; '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put ' '; @@ -20065,10 +20134,14 @@ data _null_; put ' end; '; put ' /* 32 char unique name */ '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); '; put ' '; put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' /* overwritten when fmt=Y */ '; + put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; @@ -20076,6 +20149,9 @@ data _null_; put ' run; '; put ' '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' proc sql; '; + put ' select count(*) into: lastobs from &ds; '; + put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); '; put ' '; put ' %if &engine=PROCJSON %then %do; '; put ' %if &missing=STRING %then %do; '; @@ -20112,24 +20188,49 @@ data _null_; put ' %end; '; put ' '; put ' %if &fmt=Y %then %do; '; - put ' data _data_; '; + put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' /* need to find max length of formatted values */ '; + put ' data _data_(compress=char); '; put ' /* rename on entry */ '; put ' set &ds(rename=( '; put ' %do i=1 %to &numcols; '; put ' &&name&i=&&newname&i '; put ' %end; '; put ' )); '; + put ' &stmt_obs; '; + put ' /* formatted values can be up to length 32767 */ '; + put ' length '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i '; + put ' %end; '; + put ' $32767; '; + put ' retain &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&maxlenv&i '; + put ' %end; '; + put ' 0; '; + put ' drop &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&newname&i &&maxlenv&i '; + put ' %end; '; + put ' ; '; put ' %do i=1 %to &numcols; '; - put ' /* formatted values can be up to length 32767 */ '; - put ' length &&name&i $32767; '; put ' %if &&typelong&i=num %then %do; '; - put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); '; put ' %end; '; put ' %else %do; '; put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' %end; '; - put ' drop &&newname&i; '; + put ' /* grab max length of each value as we move down the data step */ '; + put ' &tempvar=length(&&name&i); '; + put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; '; put ' %end; '; + put ' if _n_=&lastobs then do; '; + put ' /* add a 20% buffer in case of special chars that need to be escaped */ '; + put ' %do i=1 %to &numcols; '; + put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); '; + put ' %end; '; + put ' end; '; put ' if _error_ then call symputx(''syscc'',1012); '; put ' run; '; put ' %let fmtds=&syslast; '; @@ -20147,12 +20248,15 @@ data _null_; put ' %end; '; put ' other = [best.]; '; put ' '; + put ' /* configure varlenchk - as we are explicitly shortening the variables */ '; + put ' %let optval=%sysfunc(getoption(varlenchk)); '; + put ' options varlenchk=NOWARN; '; put ' data &tempds; '; put ' attrib _all_ label=''''; '; put ' %do i=1 %to &numcols; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; '; - put ' length &&name&i $32767; '; - put ' format &&name&i $32767.; '; + put ' length &&name&i $&&fmtlen&i...; '; + put ' format &&name&i $&&fmtlen&i...; '; put ' %end; '; put ' %end; '; put ' %if &fmt=Y %then %do; '; @@ -20185,6 +20289,7 @@ data _null_; put ' %end; '; put ' %end; '; put ' run; '; + put ' options varlenchk=&optval; '; put ' '; put ' filename _sjs3 temp lrecl=131068 ; '; put ' data _null_; '; @@ -22377,14 +22482,13 @@ data _null_; file &adapter; put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */"; /* WEBOUT BEGIN */ - put ' '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put ' ,engine=DATASTEP '; put ' ,missing=NULL '; put ' ,showmeta=N '; put ' ,maxobs=MAX '; put ')/*/STORE SOURCE*/; '; - put '%local tempds colinfo fmtds i numcols stmt_obs; '; + put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%let numcols=0; '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put ' '; @@ -22443,10 +22547,14 @@ data _null_; put ' end; '; put ' /* 32 char unique name */ '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); '; put ' '; put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' /* overwritten when fmt=Y */ '; + put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; @@ -22454,6 +22562,9 @@ data _null_; put ' run; '; put ' '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' proc sql; '; + put ' select count(*) into: lastobs from &ds; '; + put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); '; put ' '; put ' %if &engine=PROCJSON %then %do; '; put ' %if &missing=STRING %then %do; '; @@ -22490,24 +22601,49 @@ data _null_; put ' %end; '; put ' '; put ' %if &fmt=Y %then %do; '; - put ' data _data_; '; + put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' /* need to find max length of formatted values */ '; + put ' data _data_(compress=char); '; put ' /* rename on entry */ '; put ' set &ds(rename=( '; put ' %do i=1 %to &numcols; '; put ' &&name&i=&&newname&i '; put ' %end; '; put ' )); '; + put ' &stmt_obs; '; + put ' /* formatted values can be up to length 32767 */ '; + put ' length '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i '; + put ' %end; '; + put ' $32767; '; + put ' retain &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&maxlenv&i '; + put ' %end; '; + put ' 0; '; + put ' drop &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&newname&i &&maxlenv&i '; + put ' %end; '; + put ' ; '; put ' %do i=1 %to &numcols; '; - put ' /* formatted values can be up to length 32767 */ '; - put ' length &&name&i $32767; '; put ' %if &&typelong&i=num %then %do; '; - put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); '; put ' %end; '; put ' %else %do; '; put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' %end; '; - put ' drop &&newname&i; '; + put ' /* grab max length of each value as we move down the data step */ '; + put ' &tempvar=length(&&name&i); '; + put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; '; put ' %end; '; + put ' if _n_=&lastobs then do; '; + put ' /* add a 20% buffer in case of special chars that need to be escaped */ '; + put ' %do i=1 %to &numcols; '; + put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); '; + put ' %end; '; + put ' end; '; put ' if _error_ then call symputx(''syscc'',1012); '; put ' run; '; put ' %let fmtds=&syslast; '; @@ -22525,12 +22661,15 @@ data _null_; put ' %end; '; put ' other = [best.]; '; put ' '; + put ' /* configure varlenchk - as we are explicitly shortening the variables */ '; + put ' %let optval=%sysfunc(getoption(varlenchk)); '; + put ' options varlenchk=NOWARN; '; put ' data &tempds; '; put ' attrib _all_ label=''''; '; put ' %do i=1 %to &numcols; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; '; - put ' length &&name&i $32767; '; - put ' format &&name&i $32767.; '; + put ' length &&name&i $&&fmtlen&i...; '; + put ' format &&name&i $&&fmtlen&i...; '; put ' %end; '; put ' %end; '; put ' %if &fmt=Y %then %do; '; @@ -22563,6 +22702,7 @@ data _null_; put ' %end; '; put ' %end; '; put ' run; '; + put ' options varlenchk=&optval; '; put ' '; put ' filename _sjs3 temp lrecl=131068 ; '; put ' data _null_; '; diff --git a/base/mp_jsonout.sas b/base/mp_jsonout.sas index a37a56c..d000ec3 100644 --- a/base/mp_jsonout.sas +++ b/base/mp_jsonout.sas @@ -70,14 +70,13 @@ @source https://github.com/sasjs/core **/ - %macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y ,engine=DATASTEP ,missing=NULL ,showmeta=N ,maxobs=MAX )/*/STORE SOURCE*/; -%local tempds colinfo fmtds i numcols stmt_obs; +%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; %let numcols=0; %if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); @@ -136,10 +135,14 @@ end; /* 32 char unique name */ newname='sasjs'!!substr(cats(put(md5(name),$hex32.)),1,27); + maxlenv='maxlen'!!substr(cats(put(md5(name),$hex32.)),1,26); call symputx(cats('name',_n_),name,'l'); call symputx(cats('newname',_n_),newname,'l'); + call symputx(cats('maxlenv',_n_),maxlenv,'l'); call symputx(cats('length',_n_),length,'l'); + /* overwritten when fmt=Y */ + call symputx(cats('fmtlen',_n_),min(32767,ceil((length+3)*1.2)),'l'); call symputx(cats('fmt',_n_),fmt,'l'); call symputx(cats('type',_n_),type,'l'); call symputx(cats('typelong',_n_),typelong,'l'); @@ -147,6 +150,9 @@ run; %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); + proc sql; + select count(*) into: lastobs from &ds; + %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); %if &engine=PROCJSON %then %do; %if &missing=STRING %then %do; @@ -183,24 +189,49 @@ %end; %if &fmt=Y %then %do; - data _data_; + %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); + /* need to find max length of formatted values */ + data _data_(compress=char); /* rename on entry */ set &ds(rename=( %do i=1 %to &numcols; &&name&i=&&newname&i %end; )); + &stmt_obs; + /* formatted values can be up to length 32767 */ + length + %do i=1 %to &numcols; + &&name&i + %end; + $32767; + retain &tempvar + %do i=1 %to &numcols; + &&maxlenv&i + %end; + 0; + drop &tempvar + %do i=1 %to &numcols; + &&newname&i &&maxlenv&i + %end; + ; %do i=1 %to &numcols; - /* formatted values can be up to length 32767 */ - length &&name&i $32767; %if &&typelong&i=num %then %do; - &&name&i=left(put(&&newname&i,&&fmt&i)); + &&name&i=cats(put(&&newname&i,&&fmt&i)); %end; %else %do; &&name&i=put(&&newname&i,&&fmt&i); %end; - drop &&newname&i; + /* grab max length of each value as we move down the data step */ + &tempvar=length(&&name&i); + if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; %end; + if _n_=&lastobs then do; + /* add a 20% buffer in case of special chars that need to be escaped */ + %do i=1 %to &numcols; + call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),'l'); + %end; + end; if _error_ then call symputx('syscc',1012); run; %let fmtds=&syslast; @@ -218,12 +249,15 @@ %end; other = [best.]; + /* configure varlenchk - as we are explicitly shortening the variables */ + %let optval=%sysfunc(getoption(varlenchk)); + options varlenchk=NOWARN; data &tempds; attrib _all_ label=''; %do i=1 %to &numcols; %if &&typelong&i=char or &fmt=Y %then %do; - length &&name&i $32767; - format &&name&i $32767.; + length &&name&i $&&fmtlen&i...; + format &&name&i $&&fmtlen&i...; %end; %end; %if &fmt=Y %then %do; @@ -256,6 +290,7 @@ %end; %end; run; + options varlenchk=&optval; filename _sjs3 temp lrecl=131068 ; data _null_; diff --git a/meta/mm_createwebservice.sas b/meta/mm_createwebservice.sas index f7ecdf6..26e7412 100644 --- a/meta/mm_createwebservice.sas +++ b/meta/mm_createwebservice.sas @@ -93,14 +93,13 @@ data _null_; file sasjs lrecl=3000 ; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; /* WEBOUT BEGIN */ - put ' '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put ' ,engine=DATASTEP '; put ' ,missing=NULL '; put ' ,showmeta=N '; put ' ,maxobs=MAX '; put ')/*/STORE SOURCE*/; '; - put '%local tempds colinfo fmtds i numcols stmt_obs; '; + put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%let numcols=0; '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put ' '; @@ -159,10 +158,14 @@ data _null_; put ' end; '; put ' /* 32 char unique name */ '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); '; put ' '; put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' /* overwritten when fmt=Y */ '; + put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; @@ -170,6 +173,9 @@ data _null_; put ' run; '; put ' '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' proc sql; '; + put ' select count(*) into: lastobs from &ds; '; + put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); '; put ' '; put ' %if &engine=PROCJSON %then %do; '; put ' %if &missing=STRING %then %do; '; @@ -206,24 +212,49 @@ data _null_; put ' %end; '; put ' '; put ' %if &fmt=Y %then %do; '; - put ' data _data_; '; + put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' /* need to find max length of formatted values */ '; + put ' data _data_(compress=char); '; put ' /* rename on entry */ '; put ' set &ds(rename=( '; put ' %do i=1 %to &numcols; '; put ' &&name&i=&&newname&i '; put ' %end; '; put ' )); '; + put ' &stmt_obs; '; + put ' /* formatted values can be up to length 32767 */ '; + put ' length '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i '; + put ' %end; '; + put ' $32767; '; + put ' retain &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&maxlenv&i '; + put ' %end; '; + put ' 0; '; + put ' drop &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&newname&i &&maxlenv&i '; + put ' %end; '; + put ' ; '; put ' %do i=1 %to &numcols; '; - put ' /* formatted values can be up to length 32767 */ '; - put ' length &&name&i $32767; '; put ' %if &&typelong&i=num %then %do; '; - put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); '; put ' %end; '; put ' %else %do; '; put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' %end; '; - put ' drop &&newname&i; '; + put ' /* grab max length of each value as we move down the data step */ '; + put ' &tempvar=length(&&name&i); '; + put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; '; put ' %end; '; + put ' if _n_=&lastobs then do; '; + put ' /* add a 20% buffer in case of special chars that need to be escaped */ '; + put ' %do i=1 %to &numcols; '; + put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); '; + put ' %end; '; + put ' end; '; put ' if _error_ then call symputx(''syscc'',1012); '; put ' run; '; put ' %let fmtds=&syslast; '; @@ -241,12 +272,15 @@ data _null_; put ' %end; '; put ' other = [best.]; '; put ' '; + put ' /* configure varlenchk - as we are explicitly shortening the variables */ '; + put ' %let optval=%sysfunc(getoption(varlenchk)); '; + put ' options varlenchk=NOWARN; '; put ' data &tempds; '; put ' attrib _all_ label=''''; '; put ' %do i=1 %to &numcols; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; '; - put ' length &&name&i $32767; '; - put ' format &&name&i $32767.; '; + put ' length &&name&i $&&fmtlen&i...; '; + put ' format &&name&i $&&fmtlen&i...; '; put ' %end; '; put ' %end; '; put ' %if &fmt=Y %then %do; '; @@ -279,6 +313,7 @@ data _null_; put ' %end; '; put ' %end; '; put ' run; '; + put ' options varlenchk=&optval; '; put ' '; put ' filename _sjs3 temp lrecl=131068 ; '; put ' data _null_; '; diff --git a/server/ms_createwebservice.sas b/server/ms_createwebservice.sas index 7c0bc1f..d2c40a6 100644 --- a/server/ms_createwebservice.sas +++ b/server/ms_createwebservice.sas @@ -94,14 +94,13 @@ data _null_; file &sasjsref termstr=crlf lrecl=512; put "/* Created on %sysfunc(datetime(),datetime19.) by %mf_getuser() */"; /* WEBOUT BEGIN */ - put ' '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put ' ,engine=DATASTEP '; put ' ,missing=NULL '; put ' ,showmeta=N '; put ' ,maxobs=MAX '; put ')/*/STORE SOURCE*/; '; - put '%local tempds colinfo fmtds i numcols stmt_obs; '; + put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%let numcols=0; '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put ' '; @@ -160,10 +159,14 @@ data _null_; put ' end; '; put ' /* 32 char unique name */ '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); '; put ' '; put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' /* overwritten when fmt=Y */ '; + put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; @@ -171,6 +174,9 @@ data _null_; put ' run; '; put ' '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' proc sql; '; + put ' select count(*) into: lastobs from &ds; '; + put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); '; put ' '; put ' %if &engine=PROCJSON %then %do; '; put ' %if &missing=STRING %then %do; '; @@ -207,24 +213,49 @@ data _null_; put ' %end; '; put ' '; put ' %if &fmt=Y %then %do; '; - put ' data _data_; '; + put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' /* need to find max length of formatted values */ '; + put ' data _data_(compress=char); '; put ' /* rename on entry */ '; put ' set &ds(rename=( '; put ' %do i=1 %to &numcols; '; put ' &&name&i=&&newname&i '; put ' %end; '; put ' )); '; + put ' &stmt_obs; '; + put ' /* formatted values can be up to length 32767 */ '; + put ' length '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i '; + put ' %end; '; + put ' $32767; '; + put ' retain &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&maxlenv&i '; + put ' %end; '; + put ' 0; '; + put ' drop &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&newname&i &&maxlenv&i '; + put ' %end; '; + put ' ; '; put ' %do i=1 %to &numcols; '; - put ' /* formatted values can be up to length 32767 */ '; - put ' length &&name&i $32767; '; put ' %if &&typelong&i=num %then %do; '; - put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); '; put ' %end; '; put ' %else %do; '; put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' %end; '; - put ' drop &&newname&i; '; + put ' /* grab max length of each value as we move down the data step */ '; + put ' &tempvar=length(&&name&i); '; + put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; '; put ' %end; '; + put ' if _n_=&lastobs then do; '; + put ' /* add a 20% buffer in case of special chars that need to be escaped */ '; + put ' %do i=1 %to &numcols; '; + put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); '; + put ' %end; '; + put ' end; '; put ' if _error_ then call symputx(''syscc'',1012); '; put ' run; '; put ' %let fmtds=&syslast; '; @@ -242,12 +273,15 @@ data _null_; put ' %end; '; put ' other = [best.]; '; put ' '; + put ' /* configure varlenchk - as we are explicitly shortening the variables */ '; + put ' %let optval=%sysfunc(getoption(varlenchk)); '; + put ' options varlenchk=NOWARN; '; put ' data &tempds; '; put ' attrib _all_ label=''''; '; put ' %do i=1 %to &numcols; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; '; - put ' length &&name&i $32767; '; - put ' format &&name&i $32767.; '; + put ' length &&name&i $&&fmtlen&i...; '; + put ' format &&name&i $&&fmtlen&i...; '; put ' %end; '; put ' %end; '; put ' %if &fmt=Y %then %do; '; @@ -280,6 +314,7 @@ data _null_; put ' %end; '; put ' %end; '; put ' run; '; + put ' options varlenchk=&optval; '; put ' '; put ' filename _sjs3 temp lrecl=131068 ; '; put ' data _null_; '; diff --git a/viya/mv_createwebservice.sas b/viya/mv_createwebservice.sas index ce27fc7..69dd867 100644 --- a/viya/mv_createwebservice.sas +++ b/viya/mv_createwebservice.sas @@ -236,14 +236,13 @@ data _null_; file &adapter; put "/* Created on %sysfunc(datetime(),datetime19.) by &sysuserid */"; /* WEBOUT BEGIN */ - put ' '; put '%macro mp_jsonout(action,ds,jref=_webout,dslabel=,fmt=Y '; put ' ,engine=DATASTEP '; put ' ,missing=NULL '; put ' ,showmeta=N '; put ' ,maxobs=MAX '; put ')/*/STORE SOURCE*/; '; - put '%local tempds colinfo fmtds i numcols stmt_obs; '; + put '%local tempds colinfo fmtds i numcols stmt_obs tempvar lastobs optval; '; put '%let numcols=0; '; put '%if &maxobs ne MAX %then %let stmt_obs=%str(if _n_>&maxobs then stop;); '; put ' '; @@ -302,10 +301,14 @@ data _null_; put ' end; '; put ' /* 32 char unique name */ '; put ' newname=''sasjs''!!substr(cats(put(md5(name),$hex32.)),1,27); '; + put ' maxlenv=''maxlen''!!substr(cats(put(md5(name),$hex32.)),1,26); '; put ' '; put ' call symputx(cats(''name'',_n_),name,''l''); '; put ' call symputx(cats(''newname'',_n_),newname,''l''); '; + put ' call symputx(cats(''maxlenv'',_n_),maxlenv,''l''); '; put ' call symputx(cats(''length'',_n_),length,''l''); '; + put ' /* overwritten when fmt=Y */ '; + put ' call symputx(cats(''fmtlen'',_n_),min(32767,ceil((length+3)*1.2)),''l''); '; put ' call symputx(cats(''fmt'',_n_),fmt,''l''); '; put ' call symputx(cats(''type'',_n_),type,''l''); '; put ' call symputx(cats(''typelong'',_n_),typelong,''l''); '; @@ -313,6 +316,9 @@ data _null_; put ' run; '; put ' '; put ' %let tempds=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' proc sql; '; + put ' select count(*) into: lastobs from &ds; '; + put ' %if &maxobs ne MAX %then %let lastobs=%sysfunc(min(&lastobs,&maxobs)); '; put ' '; put ' %if &engine=PROCJSON %then %do; '; put ' %if &missing=STRING %then %do; '; @@ -349,24 +355,49 @@ data _null_; put ' %end; '; put ' '; put ' %if &fmt=Y %then %do; '; - put ' data _data_; '; + put ' %let tempvar=%substr(_%sysfunc(compress(%sysfunc(uuidgen()),-)),1,32); '; + put ' /* need to find max length of formatted values */ '; + put ' data _data_(compress=char); '; put ' /* rename on entry */ '; put ' set &ds(rename=( '; put ' %do i=1 %to &numcols; '; put ' &&name&i=&&newname&i '; put ' %end; '; put ' )); '; + put ' &stmt_obs; '; + put ' /* formatted values can be up to length 32767 */ '; + put ' length '; + put ' %do i=1 %to &numcols; '; + put ' &&name&i '; + put ' %end; '; + put ' $32767; '; + put ' retain &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&maxlenv&i '; + put ' %end; '; + put ' 0; '; + put ' drop &tempvar '; + put ' %do i=1 %to &numcols; '; + put ' &&newname&i &&maxlenv&i '; + put ' %end; '; + put ' ; '; put ' %do i=1 %to &numcols; '; - put ' /* formatted values can be up to length 32767 */ '; - put ' length &&name&i $32767; '; put ' %if &&typelong&i=num %then %do; '; - put ' &&name&i=left(put(&&newname&i,&&fmt&i)); '; + put ' &&name&i=cats(put(&&newname&i,&&fmt&i)); '; put ' %end; '; put ' %else %do; '; put ' &&name&i=put(&&newname&i,&&fmt&i); '; put ' %end; '; - put ' drop &&newname&i; '; + put ' /* grab max length of each value as we move down the data step */ '; + put ' &tempvar=length(&&name&i); '; + put ' if &tempvar>&&maxlenv&i then &&maxlenv&i=&tempvar; '; put ' %end; '; + put ' if _n_=&lastobs then do; '; + put ' /* add a 20% buffer in case of special chars that need to be escaped */ '; + put ' %do i=1 %to &numcols; '; + put ' call symputx("fmtlen&i",min(32767,ceil((&&maxlenv&i+3)*1.2)),''l''); '; + put ' %end; '; + put ' end; '; put ' if _error_ then call symputx(''syscc'',1012); '; put ' run; '; put ' %let fmtds=&syslast; '; @@ -384,12 +415,15 @@ data _null_; put ' %end; '; put ' other = [best.]; '; put ' '; + put ' /* configure varlenchk - as we are explicitly shortening the variables */ '; + put ' %let optval=%sysfunc(getoption(varlenchk)); '; + put ' options varlenchk=NOWARN; '; put ' data &tempds; '; put ' attrib _all_ label=''''; '; put ' %do i=1 %to &numcols; '; put ' %if &&typelong&i=char or &fmt=Y %then %do; '; - put ' length &&name&i $32767; '; - put ' format &&name&i $32767.; '; + put ' length &&name&i $&&fmtlen&i...; '; + put ' format &&name&i $&&fmtlen&i...; '; put ' %end; '; put ' %end; '; put ' %if &fmt=Y %then %do; '; @@ -422,6 +456,7 @@ data _null_; put ' %end; '; put ' %end; '; put ' run; '; + put ' options varlenchk=&optval; '; put ' '; put ' filename _sjs3 temp lrecl=131068 ; '; put ' data _null_; ';