1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
|
#!/bin/sh
# Copyright (C) 2022 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# shellcheck disable=SC1091
# SC1091: Not following: ... was not specified as input (see shellcheck -x).
# Exit with error if commands exit with non-zero and if undefined variables are
# used.
set -eu
# shellcheck disable=SC2034
# SC2034: ... appears unused. Verify use (or export if used externally).
prefix="@prefix@"
# Include utilities. Use installed version if available and
# use build version if it isn't.
if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
. "@datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh"
else
. "@abs_top_builddir@/src/bin/admin/admin-utils.sh"
fi
VERSION=$(pgsql_version "$@")
if [ "$VERSION" != "9.0" ]; then
printf 'This script upgrades 9.0 to 10.0. '
printf 'Reported version is %s. Skipping upgrade.\n' "${VERSION}"
exit 0
fi
psql "$@" >/dev/null <<EOF
START TRANSACTION;
-- This starts schema update to 10.0.
-- It adds corrections for client classes for CB
-- Replace setClientClass4Order():
-- 1. l_depend_on_known_indirectly needs to be BOOL
-- 2. follow_class_index needs to be BIGINT
-- -----------------------------------------------------------------------
-- Stored procedure positioning an inserted or updated client class
-- within the class hierarchy, depending on the value of the
-- new_follow_class_name parameter.
--
-- Parameters:
-- - id id of the positioned class,
-- - new_follow_class_name name of the class after which this class should be
-- positioned within the class hierarchy.
-- - old_follow_class_name previous name of the class after which this
-- class was positioned within the class hierarchy.
-- -----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION setClientClass4Order(id BIGINT,
new_follow_class_name VARCHAR(128),
old_follow_class_name VARCHAR(128))
RETURNS VOID
LANGUAGE plpgsql
AS \$\$
DECLARE
-- Used to fetch class's current value for depend_on_known_indirectly
l_depend_on_known_indirectly BOOL := false;
-- Optionally set if the follow_class_name column value is specified.
follow_class_index BIGINT;
BEGIN
-- Fetch the class's current value of depend_on_known_indirectly.
SELECT depend_on_known_indirectly INTO l_depend_on_known_indirectly
FROM dhcp4_client_class_order WHERE id = class_id;
-- Save it to the current session for use elsewhere during this transaction.
-- Note this does not work prior to Postgres 9.2 unless the variables are
-- defined in postgresql.conf. I think for now we put up with CB not supported
-- prior to 9.2 or we tell people how to edit the conf file.
PERFORM set_session_value('kea.depend_on_known_indirectly', l_depend_on_known_indirectly);
-- Bail if the class is updated without re-positioning.
IF(
l_depend_on_known_indirectly IS NOT NULL AND
((new_follow_class_name IS NULL AND old_follow_class_name IS NULL) OR
(new_follow_class_name = old_follow_class_name))
) THEN
-- The depend_on_known_indirectly is set to 0 because this procedure is invoked
-- whenever the dhcp4_client_class record is updated. Such update may include
-- test expression changes impacting the dependency on KNOWN/UNKNOWN classes.
-- This value will be later adjusted when dependencies are inserted.
-- TKM should we update the session value also or is it moot?
UPDATE dhcp4_client_class_order SET depend_on_known_indirectly = false
WHERE class_id = id;
RETURN;
END IF;
IF new_follow_class_name IS NOT NULL THEN
-- Get the position of the class after which the new class should be added.
SELECT o.order_index INTO follow_class_index
FROM dhcp4_client_class AS c
INNER JOIN dhcp4_client_class_order AS o
ON c.id = o.class_id
WHERE c.name = new_follow_class_name;
IF follow_class_index IS NULL THEN
-- The class with a name specified with new_follow_class_name does
-- not exist.
RAISE EXCEPTION 'Class %s does not exist.', new_follow_class_name
USING ERRCODE = 'sql_routine_exception';
END IF;
-- We need to place the new class at the position of follow_class_index + 1.
-- There may be a class at this position already.
IF EXISTS(SELECT * FROM dhcp4_client_class_order WHERE order_index = follow_class_index + 1) THEN
-- There is a class at this position already. Let's move all classes
-- starting from this position by one to create a spot for the new
-- class.
UPDATE dhcp4_client_class_order
SET order_index = order_index + 1
WHERE order_index >= follow_class_index + 1;
-- TKM postgresql doesn't like order by here, does it matter?
-- ORDER BY order_index DESC;
END IF;
ELSE
-- A caller did not specify the new_follow_class_name value. Let's append the
-- new class at the end of the hierarchy.
SELECT MAX(order_index) INTO follow_class_index FROM dhcp4_client_class_order;
IF follow_class_index IS NULL THEN
-- Apparently, there are no classes. Let's start from 0.
follow_class_index = 0;
END IF;
END IF;
-- Check if moving the class doesn't break dependent classes.
IF EXISTS(
SELECT 1 FROM dhcp4_client_class_dependency AS d
INNER JOIN dhcp4_client_class_order AS o
ON d.class_id = o.class_id
WHERE d.dependency_id = id AND o.order_index < follow_class_index + 1
LIMIT 1
) THEN
RAISE EXCEPTION 'Unable to move class with id %s because it would break its dependencies', id
USING ERRCODE = 'sql_routine_exception';
END IF;
-- The depend_on_known_indirectly is set to 0 because this procedure is invoked
-- whenever the dhcp4_client_class record is updated. Such update may include
-- test expression changes impacting the dependency on KNOWN/UNKNOWN classes.
-- This value will be later adjusted when dependencies are inserted.
-- TKM - note that ON CONFLICT requires PostgreSQL 9.5 or later.
UPDATE dhcp4_client_class_order
SET order_index = follow_class_index + 1,
depend_on_known_indirectly = l_depend_on_known_indirectly
WHERE class_id = id;
IF FOUND THEN
RETURN;
END IF;
INSERT INTO dhcp4_client_class_order(class_id, order_index, depend_on_known_indirectly)
VALUES (id, follow_class_index + 1, false);
RETURN;
END;\$\$;
-- Replace setClientClass6Order():
-- 1. l_depend_on_known_indirectly needs to be BOOL
-- 2. follow_class_index needs to be BIGINT
-- -----------------------------------------------------------------------
-- Stored procedure positioning an inserted or updated client class
-- within the class hierarchy, depending on the value of the
-- new_follow_class_name parameter.
--
-- Parameters:
-- - id id of the positioned class,
-- - new_follow_class_name name of the class after which this class should be
-- positioned within the class hierarchy.
-- - old_follow_class_name previous name of the class after which this
-- class was positioned within the class hierarchy.
-- -----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION setClientClass6Order(id BIGINT,
new_follow_class_name VARCHAR(128),
old_follow_class_name VARCHAR(128))
RETURNS VOID
LANGUAGE plpgsql
AS \$\$
DECLARE
-- Used to fetch class's current value for depend_on_known_indirectly
l_depend_on_known_indirectly BOOL := false;
-- Optionally set if the follow_class_name column value is specified.
follow_class_index BIGINT;
BEGIN
-- Fetch the class's current value of depend_on_known_indirectly.
SELECT depend_on_known_indirectly INTO l_depend_on_known_indirectly
FROM dhcp6_client_class_order WHERE id = class_id;
-- Save it to the current session for use elsewhere during this transaction.
-- Note this does not work prior to Postgres 9.2 unless the variables are
-- defined in postgresql.conf. I think for now we put up with CB not supported
-- prior to 9.2 or we tell people how to edit the conf file.
PERFORM set_session_value('kea.depend_on_known_indirectly', l_depend_on_known_indirectly);
-- Bail if the class is updated without re-positioning.
IF(
l_depend_on_known_indirectly IS NOT NULL AND
((new_follow_class_name IS NULL AND old_follow_class_name IS NULL) OR
(new_follow_class_name = old_follow_class_name))
) THEN
-- The depend_on_known_indirectly is set to 0 because this procedure is invoked
-- whenever the dhcp6_client_class record is updated. Such update may include
-- test expression changes impacting the dependency on KNOWN/UNKNOWN classes.
-- This value will be later adjusted when dependencies are inserted.
-- TKM should we update the session value also or is it moot?
UPDATE dhcp6_client_class_order SET depend_on_known_indirectly = false
WHERE class_id = id;
RETURN;
END IF;
IF new_follow_class_name IS NOT NULL THEN
-- Get the position of the class after which the new class should be added.
SELECT o.order_index INTO follow_class_index
FROM dhcp6_client_class AS c
INNER JOIN dhcp6_client_class_order AS o
ON c.id = o.class_id
WHERE c.name = new_follow_class_name;
IF follow_class_index IS NULL THEN
-- The class with a name specified with new_follow_class_name does
-- not exist.
RAISE EXCEPTION 'Class %s does not exist.', new_follow_class_name
USING ERRCODE = 'sql_routine_exception';
END IF;
-- We need to place the new class at the position of follow_class_index + 1.
-- There may be a class at this position already.
IF EXISTS(SELECT * FROM dhcp6_client_class_order WHERE order_index = follow_class_index + 1) THEN
-- There is a class at this position already. Let's move all classes
-- starting from this position by one to create a spot for the new
-- class.
UPDATE dhcp6_client_class_order
SET order_index = order_index + 1
WHERE order_index >= follow_class_index + 1;
-- TKM postgresql doesn't like order by here, does it matter?
-- ORDER BY order_index DESC;
END IF;
ELSE
-- A caller did not specify the new_follow_class_name value. Let's append the
-- new class at the end of the hierarchy.
SELECT MAX(order_index) INTO follow_class_index FROM dhcp6_client_class_order;
IF follow_class_index IS NULL THEN
-- Apparently, there are no classes. Let's start from 0.
follow_class_index = 0;
END IF;
END IF;
-- Check if moving the class doesn't break dependent classes.
IF EXISTS(
SELECT 1 FROM dhcp6_client_class_dependency AS d
INNER JOIN dhcp6_client_class_order AS o
ON d.class_id = o.class_id
WHERE d.dependency_id = id AND o.order_index < follow_class_index + 1
LIMIT 1
) THEN
RAISE EXCEPTION 'Unable to move class with id %s because it would break its dependencies', id
USING ERRCODE = 'sql_routine_exception';
END IF;
-- The depend_on_known_indirectly is set to 0 because this procedure is invoked
-- whenever the dhcp6_client_class record is updated. Such update may include
-- test expression changes impacting the dependency on KNOWN/UNKNOWN classes.
-- This value will be later adjusted when dependencies are inserted.
-- TKM - note that ON CONFLICT requires PostgreSQL 9.5 or later.
UPDATE dhcp6_client_class_order
SET order_index = follow_class_index + 1,
depend_on_known_indirectly = l_depend_on_known_indirectly
WHERE class_id = id;
IF FOUND THEN
RETURN;
END IF;
INSERT INTO dhcp6_client_class_order(class_id, order_index, depend_on_known_indirectly)
VALUES (id, follow_class_index + 1, false);
RETURN;
END;\$\$;
-- Change primary key to composite, dependency table can have multiple rows
-- per class id.
ALTER TABLE dhcp4_client_class_dependency DROP CONSTRAINT dhcp4_client_class_dependency_pkey;
ALTER TABLE dhcp4_client_class_dependency ADD PRIMARY KEY(class_id, dependency_id);
ALTER TABLE dhcp6_client_class_dependency DROP CONSTRAINT dhcp6_client_class_dependency_pkey;
ALTER TABLE dhcp6_client_class_dependency ADD PRIMARY KEY(class_id, dependency_id);
-- Replace triggers that verify class dependency.
-- Because they are BEFORE INSERT triggers they need to return NEW not NULL.
-- -----------------------------------------------------------------------
-- Trigger verifying if class dependency is met. It includes checking
-- if referenced classes exist, are associated with the same server
-- or all servers, and are defined before the class specified with
-- class_id.
-- -----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION func_dhcp4_client_class_check_dependency_BINS()
RETURNS trigger AS \$dhcp4_client_class_check_dependency_BINS\$
BEGIN
PERFORM checkDHCPv4ClientClassDependency(NEW.class_id, NEW.dependency_id);
RETURN NEW;
END;
\$dhcp4_client_class_check_dependency_BINS\$
LANGUAGE plpgsql;
-- -----------------------------------------------------------------------
-- Trigger verifying if class dependency is met. It includes checking
-- if referenced classes exist, are associated with the same server
-- or all servers, and are defined before the class specified with
-- class_id.
-- -----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION func_dhcp6_client_class_check_dependency_BINS()
RETURNS trigger AS \$dhcp6_client_class_check_dependency_BINS\$
BEGIN
PERFORM checkDHCPv6ClientClassDependency(NEW.class_id, NEW.dependency_id);
RETURN NEW;
END;
\$dhcp6_client_class_check_dependency_BINS\$
LANGUAGE plpgsql;
-- Update the schema version number.
UPDATE schema_version
SET version = '10', minor = '0';
-- Commit the script transaction.
COMMIT;
EOF
|