Versioning

Fork/mirror of https://gitlab.com/depesz/Versioning
git clone https://ccx.te2000.cz/git/Versioning
Log | Files | Refs | README | LICENSE

apply_patchset (4348B)


      1 #!/bin/zsh
      2 . $0:h/common.zsh || exit $?
      3 
      4 main() {
      5 	if (( $# < 1 )); then
      6 		warn_msg usage: "cd <patchdir> && apply_patchset <one or more patch names>"
      7 		exit 100
      8 	fi
      9 	local patch
     10 	for patch in "$@"; do
     11 		if [[ $patch == *[\\\']* ]]; then
     12 			err_msg ERROR: "disallowed character in patch name: $1"
     13 			exit 100
     14 		fi
     15 	done
     16 	- parse_patch_set $@
     17 	- load_installed_patches
     18 	- verify_patch_set $@
     19 	- install_patch_set $@
     20 }
     21 typeset -f -t main
     22 
     23 # ---
     24 
     25 load_installed_patches() {
     26 	typeset -ga installed_patches conflicting_patches
     27 	installed_patches=( ${(f)"$( psql -AP tuples_only=on -c 'select patch_name from _v.patches' )"} ) || \
     28 		exit $?
     29 	conflicting_patches=( ${(f)"$( psql -AP tuples_only=on -c 'select distinct con from _v.patches, lateral unnest(conflicts) con;' )"} ) || \
     30 		exit $?
     31 }
     32 
     33 # ---
     34 
     35 parse_patch_set() {
     36 	typeset -gA patch_parsed patch_deps patch_cons
     37 	local patch
     38 	for patch in "$@"; do
     39 		- parse_patch $patch
     40 	done
     41 }
     42 
     43 parse_patch() {
     44 	(( $+patch_parsed[$1] )) && return 0
     45 	if [[ $1 == *[\\\']* ]]; then
     46 		printf >&2 'ERROR: disallowed character in patch name: %s\n' $1
     47 		exit 1
     48 	fi
     49 
     50 	local line have_start=false
     51 	local -a deps cons
     52 	<$1 while IFS= read line; do
     53 		if ! $have_start; then
     54 			if [[ $line == "-- VPATCH" ]]; then
     55 				have_start=true
     56 				continue
     57 			else
     58 				die "$1 doesn't have VPATCH header"
     59 			fi
     60 		else
     61 			if [[ $line == '' ]]; then
     62 				break
     63 			fi
     64 			line=${line##--[[:space:]]#}
     65 			case $line in
     66 				(VDEP:*) deps+=${line#VDEP:};;
     67 				(VCON:*) cons+=${line#VCON:};;
     68 				(*) die "unexpected line in $1: ${(qqq)line}";
     69 			esac
     70 		fi
     71 	done
     72 
     73 	patch_deps[$1]=${(pj:\0:)deps}
     74 	patch_cons[$1]=${(pj:\0:)cons}
     75 	patch_parsed[$1]=1
     76 
     77 	local dep
     78 	for dep in $deps; do
     79 		- parse_patch $dep
     80 	done
     81 }
     82 
     83 # ---
     84 
     85 verify_patch_set() {
     86 	unset verify_install
     87 	unset verify_conflict
     88 	typeset -gA verify_install
     89 	typeset -gA verify_conflict
     90 	local patch
     91 	for patch in "$@"; do
     92 		- verify_patch $patch '<commandline>'
     93 	done
     94 }
     95 
     96 print_dep_traceback() {
     97 	local dep indent
     98 	indent=${1:->}
     99 	(($+verify_install[$1])) || return
    100 	for dep in ${(f)verify_install[$1]}; do
    101 		- warn_msg $indent patch ${(qqq)1} was pulled in by ${(qqq)dep}
    102 		- print_dep_traceback $dep "$indent >"
    103 	done
    104 }
    105 
    106 print_con_traceback() {
    107 	local dep indent
    108 	indent=${1:-!}
    109 	(($+verify_install[$1])) || return
    110 	for dep in ${(f)verify_install[$1]}; do
    111 		- warn_msg $indent conflict with ${(qqq)1} is caused by ${(qqq)dep}
    112 		- print_dep_traceback $dep "$indent >"
    113 	done
    114 }
    115 
    116 extend_install_list() {
    117 	[[ $# == 2 ]] || die "exactly two arguments required"
    118 	verify_install[$1]+=$2$'\n'
    119 	if (( ${conflicting_patches[(I)$1]} )); then
    120 		- err_msg ERROR cannot install patch $1 as it conflicts with current database
    121 		- print_dep_traceback $1
    122 		exit 3
    123 	elif (( $+verify_conflict[$1] )); then
    124 		- err_msg ERROR cannot install patch $1 as it conflicts with another patch
    125 		- print_con_traceback $1
    126 		- print_dep_traceback $1
    127 		exit 3
    128 	fi
    129 }
    130 
    131 extend_conflict_list() {
    132 	[[ $# == 2 ]] || die "exactly two arguments required"
    133 	verify_conflict[$1]+=$2$'\n'
    134 	if (( ${installed_patches[(I)$1]} )); then
    135 		- err_msg ERROR cannot install patch $2 as it creates conflict with $1 in the current database
    136 		- print_con_traceback $1
    137 		exit 3
    138 	elif (( $+verify_install[$1] )); then
    139 		- err_msg ERROR cannot install patch $2 as it creates conflict with another patch
    140 		- print_con_traceback $1
    141 		- print_dep_traceback $1
    142 		exit 3
    143 	fi
    144 }
    145 
    146 verify_patch() {
    147 	[[ $# == 2 ]] || die "exactly two arguments required"
    148 	# (( ${installed_patches[(I)$1]} )) && return 0
    149 	(( $+verify_install[$1] )) && return 0
    150 	local -a deps cons
    151 	local dep con
    152 	extend_install_list $1 $2
    153 	for dep in ${(ps:\0:)patch_deps[$1]}; do
    154 		- verify_patch $dep $1
    155 	done
    156 	for con in ${(ps:\0:)patch_cons[$1]}; do
    157 		- extend_conflict_list $con $1
    158 	done
    159 }
    160 
    161 # ---
    162 
    163 install_patch_set() {
    164 	local patch
    165 	for patch in "$@"; do
    166 		- install_patch $patch
    167 	done
    168 }
    169 
    170 install_patch() {
    171 	(( ${installed_patches[(I)$1]} )) && return 0
    172 	if [[ $1 == *[\\\']* ]]; then
    173 		printf >&2 'ERROR: disallowed character in patch name: %s\n' $1
    174 		exit 1
    175 	fi
    176 
    177 	local dep
    178 	for dep in ${(ps:\0:)patch_deps[$1]}; do
    179 		- install_patch $dep
    180 	done
    181 
    182 	printf '%s\n' \
    183 		'\set ON_ERROR_STOP' \
    184 		"select _v.register_patch('$1', NULL, NULL);" \
    185 		"\i $1" \
    186 		| psql -1 -e -f - || die ERROR: "installing patch ${(qqq)1} failed with exitcode $#"
    187 	installed_patches+=$1
    188 }
    189 
    190 # ---
    191 
    192 main "$@"