Files
dc/sas/sasjs/db/migrations/20261019_v7.19_release.sas
T
dc 7fb981daa9 fix(derived): guard the bitemporal cut-off, coalesce the selectbox seed
mpe_derived used &dc_dttmtfmt in its where= but the guard only checked libds and
dclib, so an unset value turned the filter into 'lt tx_to' and the control-table
step failed with a syntax error a long way from the cause.  It is now part of
the guard, named in the message.

The 7.19 migration took max(selectbox_rk) into a macro variable, which is
missing on an install whose MPE_SELECTBOX is still empty - %eval(&maxrk+1) then
fails.  coalesce(max(selectbox_rk),0) makes the first row start at 1.
2026-10-27 20:41:21 +00:00

193 lines
7.1 KiB
SAS

/**
@file
@brief migration script to move to v7.19 of Data Controller
REQUIRED CHANGE - adds the MPE_DERIVED table, which holds derived (computed)
variable definitions for the EDIT screen, and registers it in MPE_TABLES so
that it can be maintained through the app with the usual approve workflow.
Replace YOURDCLIB and the library path below, then run the script. Any
MPE_DERIVED rows added before this script runs will be lost when the table
is created, so run it before configuring any derived variables.
**/
%let dclib=YOURDCLIB;
libname &dclib "/YOUR/DATACONTROLLER/LIBRARY/PATH";
proc sql;
create table &dclib..mpe_derived
(
tx_from num not null format=datetime19.3,
libref varchar(8) not null,
dsn varchar(32) not null,
var_nm varchar(32) not null,
var_deps varchar(4000),
var_type varchar(1) not null,
var_len num not null,
var_fmt varchar(32),
var_label varchar(256),
var_logic varchar(4000) not null,
active num not null,
tx_to num not null format=datetime19.3
);
quit;
proc datasets lib=&dclib noprint;
modify mpe_derived;
index create
pk_mpe_derived=(tx_from libref dsn var_nm)
/nomiss unique;
quit;
proc sql;
insert into &dclib..mpe_tables
set tx_from=0
,tx_to='31DEC5999:23:59:59'dt
,libref="&dclib"
,dsn='MPE_DERIVED'
,num_of_approvals_required=1
,loadtype='TXTEMPORAL'
,buskey='LIBREF DSN VAR_NM'
,post_edit_hook='services/hooks/mpe_derived_postedit'
,notes='Derived (computed) variables for the EDIT screen'
,var_txfrom='TX_FROM'
,var_txto='TX_TO'
;
quit;
/* Dropdown values for VAR_TYPE and ACTIVE. These rows, and the field rules
* further down, mirror the MPE_DERIVED rows in mpe_makedata.sas - keep the two
* in step, or an upgraded install and a freshly seeded one behave differently. */
proc sql noprint;
/* coalesce: an install with no selectbox rows yet returns a missing value,
and %eval(&maxrk+1) below would then fail */
select coalesce(max(selectbox_rk),0) into: maxrk from &dclib..mpe_selectbox;
quit;
%let rk1=%eval(&maxrk+1);
%let rk2=%eval(&maxrk+2);
%let rk3=%eval(&maxrk+3);
%let rk4=%eval(&maxrk+4);
proc sql;
insert into &dclib..mpe_selectbox
set selectbox_rk=&rk1
,ver_from_dttm=0
,select_lib="&dclib"
,select_ds="MPE_DERIVED"
,base_column="VAR_TYPE"
,selectbox_value='C'
,selectbox_order=1
,ver_to_dttm='31DEC5999:23:59:59'dt;
insert into &dclib..mpe_selectbox
set selectbox_rk=&rk2
,ver_from_dttm=0
,select_lib="&dclib"
,select_ds="MPE_DERIVED"
,base_column="VAR_TYPE"
,selectbox_value='N'
,selectbox_order=2
,ver_to_dttm='31DEC5999:23:59:59'dt;
/* ACTIVE - a fixed pair, not a lookup against another table */
insert into &dclib..mpe_selectbox
set selectbox_rk=&rk3
,ver_from_dttm=0
,select_lib="&dclib"
,select_ds="MPE_DERIVED"
,base_column="ACTIVE"
,selectbox_value='1'
,selectbox_order=1
,ver_to_dttm='31DEC5999:23:59:59'dt;
insert into &dclib..mpe_selectbox
set selectbox_rk=&rk4
,ver_from_dttm=0
,select_lib="&dclib"
,select_ds="MPE_DERIVED"
,base_column="ACTIVE"
,selectbox_value='0'
,selectbox_order=2
,ver_to_dttm='31DEC5999:23:59:59'dt;
quit;
/* Field rules for the EDIT screen. These are client side - the server side
* enforcement is the post edit hook registered above. */
proc sql;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="LIBREF"
,rule_type='CASE',rule_value='UPCASE',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
/* the libraries the user can edit, as MPE_VALIDATIONS.BASE_LIB does */
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="LIBREF"
,rule_type='SOFTSELECT_HOOK'
,rule_value="services/validations/libraries_editable"
,rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="DSN"
,rule_type='CASE',rule_value='UPCASE',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
/* tables in the LIBREF on the same row - the hook reads it from source_row */
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="DSN"
,rule_type='SOFTSELECT_HOOK'
,rule_value="services/validations/tables_editable"
,rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_NM"
,rule_type='CASE',rule_value='UPCASE',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_NM"
,rule_type='HARDREGEX',rule_value='^[A-Za-z_][A-Za-z0-9_]*$',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_DEPS"
,rule_type='CASE',rule_value='UPCASE',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_DEPS"
,rule_type='HARDREGEX',rule_value='^[A-Za-z0-9_ ]*$',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_TYPE"
,rule_type='CASE',rule_value='UPCASE',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_TYPE"
,rule_type='HARDSELECT',rule_value=' ',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
/* ACTIVE has no NOTNULL rule: the column is NOT NULL in the model, and the
* EDIT screen offers the MPE_SELECTBOX values for it. */
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_LEN"
,rule_type='MINVAL',rule_value='1',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_LEN"
,rule_type='MAXVAL',rule_value='32767',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_FMT"
,rule_type='CASE',rule_value='UPCASE',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_FMT"
,rule_type='HARDREGEX'
,rule_value='^(\$?[A-Za-z][A-Za-z0-9_]*(\.[0-9]+)?\.?|\$?[0-9]+(\.[0-9]+)?\.?)?$',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_LABEL"
,rule_type='HARDREGEX'
,rule_value='^[^\x00-\x1F\x7F]*$',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
insert into &dclib..MPE_VALIDATIONS set
tx_from=0,base_lib="&dclib",base_ds="MPE_DERIVED",base_col="VAR_LOGIC"
,rule_type='HARDREGEX'
,rule_value='^(?!.*(/\*|\*/))[^;&%]*$',rule_active=1
,tx_to='31DEC5999:23:59:59'dt;
quit;