run_command \
"${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}"
version="${OUTPUT}"
- assert_str_eq "34.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
+ assert_str_eq "35.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
# Let's wipe the whole database
mysql_wipe
assert_str_eq '3001::1' "${OUTPUT}"
}
+mysql_upgrade_34_to_35_test() {
+ qry="DELETE from lease6; DELETE from flq_pool6; DELETE from free_lease6"
+ run_statement "Pre-test deletes" "${qry}"
+
+ # Steps 1 - 3 Verifies updated recreate on delegated_len change logic.
+
+ # Step 1 - create and verify an NA pool.
+ qry="CALL sflqCreateFlqPool6('3001::', '3001::ffff', 0, 128, 1, 0)"
+ run_statement "Step 1.1" "${qry}"
+
+ qry="SELECT count(*) from flq_pool6 where start_address = '3001::' AND delegated_len = 128"
+ run_statement "Step 1.2" "${qry}" "1"
+
+ qry="SELECT count(*) from free_lease6"
+ run_statement "Step 1.3" "${qry}" "65536"
+
+ # Step 2 - set recreate to true and call create again.
+ qry="DELETE from free_lease6"
+ run_statement "Step 2.1" "${qry}"
+
+ qry="CALL sflqCreateFlqPool6('3001::', '3001::ffff', 0, 128, 1, 1)"
+ run_statement "Step 2.2." "${qry}"
+
+ qry="SELECT count(*) from free_lease6"
+ run_statement "Step 2.3" "${qry}" "65536"
+
+ # Step 3 - call create with different delegated_len.
+ qry="CALL sflqCreateFlqPool6('3001::', '3001::ffff', 0, 127, 1, 0)"
+ run_statement "Step 3.1" "${qry}"
+
+ qry="SELECT count(*) from free_lease6"
+ run_statement "Step 3.2" "${qry}" '32768'
+
+ # Step 4 - Verifies that MySQL does not have the one too many prefixes issue.
+ qry="DELETE from flq_pool6;DELETE from free_lease6"
+ run_statement "Step 4.1" "${qry}"
+
+ qry="CALL sflqCreateFlqPool6('2001::fff0', '2001::ffff', 2, 128, 1, 0)"
+ run_statement "Step 4.2" "${qry}"
+
+ # Verify free_lease6 has only the expected addresses.
+ sql="SELECT count(bin_address) from free_lease6 \
+ where bin_address >= inet6_aton('2001::fff0') \
+ and bin_address <= inet6_aton('2001::ffff')"
+ run_statement "Step 4.3" "$sql" "16"
+
+ sql="SELECT count(bin_address) from free_lease6"
+ run_statement "Step 4.4 failed" "$sql" "16"
+
+ qry="DELETE from lease6; DELETE from flq_pool6; DELETE from free_lease6"
+ run_statement "Post-test deletes" "${qry}"
+}
+
mysql_upgrade_test() {
test_start "mysql.upgrade"
# Verify that the upgraded schema reports the latest version.
version=$("${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}")
- assert_str_eq "34.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
+ assert_str_eq "35.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
# Let's check that the new tables are indeed there.
# Check upgrade from 33.0 to 34.0.
mysql_upgrade_33_to_34_test
+ # Check upgrade from 34.0 to 35.0.
+ mysql_upgrade_34_to_35_test
+
# Let's wipe the whole database
mysql_wipe
run_command \
"${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}"
version="${OUTPUT}"
- assert_str_eq "33.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
+ assert_str_eq "34.0" "${version}" "Expected kea-admin to return %s, returned value was %s"
# Let's wipe the whole database
pgsql_wipe
assert_str_eq '3001::' "${OUTPUT}"
}
+pgsql_upgrade_33_to_34_test() {
+ qry="DELETE from lease6; DELETE from flq_pool6; DELETE from free_lease6"
+ run_statement "Pre-test deletes" "${qry}"
+
+ # Steps 1 - 3 Verifies updated recreate on delegated_len change logic.
+
+ # Step 1 - create and verify an NA pool.
+ qry="SELECT sflqCreateFlqPool6('3001::'::inet, '3001::ffff'::inet,\
+ 0::smallint, 128::smallint, 1::bigint, 'f')"
+ run_statement "Step 1.1" "${qry}"
+
+ qry="SELECT count(*) from flq_pool6 where start_address = '3001::' AND delegated_len = 128"
+ run_statement "Step 1.2" "${qry}" "1"
+
+ qry="SELECT count(*) from free_lease6"
+ run_statement "Step 1.3" "${qry}" "65536"
+
+ # Step 2 - set recreate to true and call create again.
+ qry="DELETE from free_lease6"
+ run_statement "Step 2.1" "${qry}"
+
+ qry="SELECT sflqCreateFlqPool6('3001::'::inet, '3001::ffff'::inet,\
+ 0::smallint, 128::smallint, 1::bigint, 't')"
+ run_statement "Step 2.2." "${qry}"
+
+ qry="SELECT count(*) from free_lease6"
+ run_statement "Step 2.3" "${qry}" "65536"
+
+ # Step 3 - call create with different delegated_len.
+ qry="SELECT sflqCreateFlqPool6('3001::'::inet, '3001::ffff'::inet,\
+ 0::smallint, 127::smallint, 1::bigint, 'f')"
+ run_statement "Step 3.1" "${qry}"
+
+ qry="SELECT count(*) from free_lease6"
+ run_statement "Step 3.2" "${qry}" '32768'
+
+ # Step 4 - Verifies that MySQL does not have the one too many prefixes issue.
+ qry="DELETE from flq_pool6;DELETE from free_lease6"
+ run_statement "Step 4.1" "${qry}"
+
+ qry="SELECT sflqCreateFlqPool6('2001::fff0'::inet, '2001::ffff'::inet,\
+ 2::smallint, 128::smallint, 1::bigint, 'f')"
+ run_statement "Step 4.2" "${qry}"
+
+ # Verify free_lease6 has only the expected addresses.
+ sql="SELECT count(bin_address) from free_lease6 \
+ where bin_address >= inetToBytea('2001::fff0'::inet) \
+ and bin_address <= inetToBytea('2001::ffff'::inet)"
+ run_statement "Step 4.3" "$sql" "16"
+
+ sql="SELECT count(bin_address) from free_lease6"
+ run_statement "Step 4.4 failed" "$sql" "16"
+
+ qry="DELETE from lease6; DELETE from flq_pool6; DELETE from free_lease6"
+ run_statement "Postrtest deletes" "${qry}"
+}
+
pgsql_upgrade_test() {
test_start "pgsql.upgrade"
# Verify upgraded schema reports the latest version.
version=$("${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}")
- assert_str_eq "33.0" "${version}" 'Expected kea-admin to return %s, returned value was %s'
+ assert_str_eq "34.0" "${version}" 'Expected kea-admin to return %s, returned value was %s'
# Check 1.0 to 2.0 upgrade
pgsql_upgrade_1_0_to_2_0_test
# Check 32 to 33 upgrade
pgsql_upgrade_32_to_33_test
+ # Check 33 to 34 upgrade
+ pgsql_upgrade_33_to_34_test
+
# Let's wipe the whole database
pgsql_wipe
int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), &ibind_vec[0]);
checkError(ctx, status, stindex, "unable to bind parameters");
- // Execute
+ // Execute the create inside a transaction.
+ ScopedMySqlTransactionPtr trans(new MySqlTransaction(ctx->conn_));
status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
if (status != 0) {
// Failure: check for the special case of duplicate entry. If this is
- // the case, we ignore it.
+ // the case, we don't consider it an errror but still need to rollback
+ // to relinquish lock.
if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
return (false);
}
checkError(ctx, status, stindex, "unable to execute");
}
+ // Commit any changes.
+ trans->commit();
+
return (true);
}
int status = mysql_stmt_bind_param(ctx->conn_.getStatement(stindex), &ibind_vec[0]);
checkError(ctx, status, stindex, "unable to bind parameters");
- // Execute
+ // Execute the create inside a transaction.
+ ScopedMySqlTransactionPtr trans(new MySqlTransaction(ctx->conn_));
status = MysqlExecuteStatement(ctx->conn_.getStatement(stindex));
if (status != 0) {
// Failure: check for the special case of duplicate entry. If this is
- // the case, we ignore it.
+ // the case, we don't consider it an errror but still need to rollback
+ // to relinquish lock.
if (mysql_errno(ctx->conn_.mysql_) == ER_DUP_ENTRY) {
return (false);
}
checkError(ctx, status, stindex, "unable to execute");
}
+ // Commit any changes.
+ trans->commit();
+
return (true);
}
ibind.add(recreate);
// Execute the select.
+ ScopedPgSqlTransactionPtr trans(new PgSqlTransaction(ctx->conn_));
PgSqlResult r(PQexecPrepared(ctx->conn_,
tagged_statements[stindex].name,
tagged_statements[stindex].nbparams,
ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
}
+ trans->commit();
+
return (true);
}
ibind.add(subnet_id);
ibind.add(recreate);
+ ScopedPgSqlTransactionPtr trans(new PgSqlTransaction(ctx->conn_));
// Execute the select.
PgSqlResult r(PQexecPrepared(ctx->conn_,
tagged_statements[stindex].name,
ctx->conn_.checkStatementError(r, tagged_statements[stindex]);
}
+ trans->commit();
return (true);
}
test_pool->subnet_id_, false));
}
- // Fetching all pools should find all three.
+ // Fetching all pools should find all three.
ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6GetAll());
ASSERT_TRUE(pool_infos);
ASSERT_EQ(3, pool_infos->size());
checkPoolInfos(*(*pool_infos)[1], *test_pools[2], __LINE__);
}
+typedef boost::shared_ptr<thread> ThreadPtr;
+
void
GenericLeaseMgrTest::sflqCreateFlqPool4Concurrent() {
// Enable Multi-Threading.
isc::test::MultiThreadingTest mt(true);
- // Create the same pool in different threads.
- bool ret1 = false;
- bool ret2 = false;
IOAddress start_address("192.0.0.0");
IOAddress end_address("192.0.255.255");
- thread th1([this, &ret1, start_address, end_address]() {
- ASSERT_NO_THROW_LOG(ret1 =
- lmptr_->sflqCreateFlqPool4(start_address, end_address, false));
- });
- thread th2([this, &ret2, start_address, end_address]() {
- ASSERT_NO_THROW_LOG(ret2 =
- lmptr_->sflqCreateFlqPool4(start_address, end_address, false));
- });
+ // First we'll do concurrent creates with recreate = false;
+ auto num_threads = 5;
+ std::vector<bool> rets(num_threads);
+ std::list<ThreadPtr> threads;
+ for (int i = 0; i < num_threads; ++i) {
+ rets[i] = false;
+ ThreadPtr th(new thread([this, &rets, i, start_address, end_address]() {
+ ASSERT_NO_THROW_LOG(rets[i] =
+ lmptr_->sflqCreateFlqPool4(start_address, end_address, 1, false));
+ }));
+
+ threads.push_back(th);
+ }
+
+ for (auto th : threads) {
+ th->join();
+ }
- th1.join();
- th2.join();
+ // Count the true returns.
+ int true_cnt = 0;
+ for (auto ret : rets) {
+ if (ret) {
+ ++true_cnt;
+ }
+ }
- // One thread should create the pool, the other should not.
- ASSERT_NE(ret1, ret2);
+ // Only 1 should be true (i.e. did the create).
+ ASSERT_EQ(1, true_cnt);
- // Verify the pool and free leases were created.
+ // Verify the pool and free leases are correct.
SflqPoolInfoCollectionPtr pool_infos;
ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(start_address, end_address));
ASSERT_TRUE(pool_infos);
ASSERT_EQ(1, pool_infos->size());
ASSERT_EQ(65536, (*pool_infos)[0]->free_leases_);
+
+ // Now let's do concurrent recreates.
+ threads.clear();
+ for (int i = 0; i < num_threads; ++i) {
+ rets[i] = false;
+ ThreadPtr th(new thread([this, &rets, i, start_address, end_address]() {
+ ASSERT_NO_THROW_LOG(rets[i] =
+ lmptr_->sflqCreateFlqPool4(start_address, end_address, 1, true));
+ EXPECT_TRUE(rets[i]) << "not true pass: " << i;
+ }));
+
+ threads.push_back(th);
+ }
+
+ for (auto th : threads) {
+ th->join();
+ }
+
+ true_cnt = 0;
+ for (auto ret : rets) {
+ if (ret) {
+ ++true_cnt;
+ }
+ }
+
+ // All should report true.
+ ASSERT_EQ(num_threads, true_cnt);
+
+ // Verify the pool and free leases are correct.
+ ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool4Get(start_address, end_address));
+ ASSERT_TRUE(pool_infos);
+ ASSERT_EQ(1, pool_infos->size());
+ ASSERT_EQ(65536, (*pool_infos)[0]->free_leases_);
}
+
void
GenericLeaseMgrTest::sflqCreateFlqPool6Concurrent() {
// Enable Multi-Threading.
isc::test::MultiThreadingTest mt(true);
// Create the same pool in different threads.
- bool ret1 = false;
- bool ret2 = false;
IOAddress start_address("3001::");
IOAddress end_address("3001::FFFF");
- thread th1([this, &ret1, start_address, end_address]() {
- ASSERT_NO_THROW_LOG(ret1 =
- lmptr_->sflqCreateFlqPool6(start_address, end_address,
- Lease::TYPE_NA, 128, 1, false));
- });
- thread th2([this, &ret2, start_address, end_address]() {
- ASSERT_NO_THROW_LOG(ret2 =
+ // First we'll do concurrent creates with recreate = false;
+ auto num_threads = 5;
+ std::vector<bool> rets(num_threads);
+ std::list<ThreadPtr> threads;
+ for (int i = 0; i < num_threads; ++i) {
+ rets[i] = false;
+ ThreadPtr th(new thread([this, &rets, i, start_address, end_address]() {
+ ASSERT_NO_THROW_LOG(rets[i] =
lmptr_->sflqCreateFlqPool6(start_address, end_address,
- Lease::TYPE_NA, 128, 1, false));
- });
+ Lease::TYPE_PD, 128, 1, false));
+ }));
- th1.join();
- th2.join();
+ threads.push_back(th);
+ }
- // One thread should create the pool, the other should not.
- ASSERT_NE(ret1, ret2);
+ for (auto th : threads) {
+ th->join();
+ }
+ // Count the true returns.
+ int true_cnt = 0;
+ for (auto ret : rets) {
+ if (ret) {
+ ++true_cnt;
+ }
+ }
- // Verify the pool and free leases were created.
+ // Only 1 should be true (i.e. did the create).
+ ASSERT_EQ(1, true_cnt);
+
+ // Verify the pool and free leases are correct.
SflqPoolInfoCollectionPtr pool_infos;
ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(start_address, end_address));
ASSERT_TRUE(pool_infos);
ASSERT_EQ(1, pool_infos->size());
ASSERT_EQ(65536, (*pool_infos)[0]->free_leases_);
+ ASSERT_EQ(Lease::TYPE_PD, (*pool_infos)[0]->lease_type_);
+
+ // Now let's do concurrent recreates.
+ threads.clear();
+ for (int i = 0; i < num_threads; ++i) {
+ rets[i] = false;
+ ThreadPtr th(new thread([this, &rets, i, start_address, end_address]() {
+ ASSERT_NO_THROW_LOG(rets[i] =
+ lmptr_->sflqCreateFlqPool6(start_address, end_address,
+ Lease::TYPE_PD, 128, 1, true));
+ }));
+
+ threads.push_back(th);
+ }
+
+ for (auto th : threads) {
+ th->join();
+ }
+
+ true_cnt = 0;
+ for (auto ret : rets) {
+ if (ret) {
+ ++true_cnt;
+ }
+ }
+
+ // All should report true.
+ ASSERT_EQ(num_threads, true_cnt);
+
+ // Verify the pool and free leases are correct.
+ ASSERT_NO_THROW_LOG(pool_infos = lmptr_->sflqPool6Get(start_address, end_address));
+ ASSERT_TRUE(pool_infos);
+ ASSERT_EQ(1, pool_infos->size());
+ ASSERT_EQ(65536, (*pool_infos)[0]->free_leases_);
+ ASSERT_EQ(Lease::TYPE_PD, (*pool_infos)[0]->lease_type_);
+
+ // Now let's try creates with a different delegated_len.
+ // This should make them act like recreates.
+ threads.clear();
+ for (int i = 0; i < num_threads; ++i) {
+ rets[i] = false;
+ ThreadPtr th(new thread([this, &rets, i, start_address, end_address]() {
+ ASSERT_NO_THROW_LOG(rets[i] =
+ lmptr_->sflqCreateFlqPool6(start_address, end_address,
+ Lease::TYPE_PD, 127, 1, false));
+ }));
+
+ threads.push_back(th);
+ }
+
+ for (auto th : threads) {
+ th->join();
+ }
+
+ true_cnt = 0;
+ for (auto ret : rets) {
+ if (ret) {
+ ++true_cnt;
+ }
+ }
+
+ // All should report true.
+ ASSERT_EQ(num_threads, true_cnt);
}
} // namespace test
/// @name Current database schema version values.
//@{
-const uint32_t MYSQL_SCHEMA_VERSION_MAJOR = 34;
+const uint32_t MYSQL_SCHEMA_VERSION_MAJOR = 35;
const uint32_t MYSQL_SCHEMA_VERSION_MINOR = 0;
//@}
namespace db {
/// @brief Define the PostgreSQL backend version.
-const uint32_t PGSQL_SCHEMA_VERSION_MAJOR = 33;
+const uint32_t PGSQL_SCHEMA_VERSION_MAJOR = 34;
const uint32_t PGSQL_SCHEMA_VERSION_MINOR = 0;
// Maximum number of parameters that can be used a statement
UPDATE schema_version
SET version = '34', minor = '0';
--- This line concludes the schema upgrade to version 34.0.
+-- This line starts the schema upgrade to version 35.0.
+
+-- Remove transaction statements.
+-- Populate flq_pool4 and free_lease4 based on an address range.
+-- This must be called from within a transaction.
+DROP PROCEDURE IF EXISTS sflqCreateFlqPool4;
+DELIMITER $$
+CREATE PROCEDURE sflqCreateFlqPool4(IN p_start_address INT UNSIGNED,
+ IN p_end_address INT UNSIGNED,
+ IN p_subnet_id INT UNSIGNED,
+ IN p_recreate TINYINT)
+BEGIN
+ DECLARE loop_limit INT UNSIGNED;
+ DECLARE next_start_address INT UNSIGNED;
+ DECLARE next_end_address INT UNSIGNED;
+ DECLARE remaining INT UNSIGNED;
+ DECLARE error_msg VARCHAR(512);
+
+ IF (p_start_address = inet_aton('0.0.0.0') OR
+ p_start_address > p_end_address)
+ THEN
+ SET error_msg = CONCAT('Invalid range: ', inet_ntoa(p_start_address),
+ ' - ', inet_ntoa(p_end_address));
+ SIGNAL SQLSTATE '45000'
+ SET MESSAGE_TEXT = error_msg;
+ END IF;
+
+ -- Create the flq_pool4 row. Concurrent attempts will hang until
+ -- this one commits and then they will fail with duplicate key
+ -- error. Callers should treat the duplicate error as success.
+ IF p_recreate = 1
+ THEN
+ -- (Re)creating should ignore duplicate on insert
+ INSERT INTO flq_pool4 (start_address, end_address, subnet_id)
+ VALUES (p_start_address, p_end_address, p_subnet_id)
+ ON DUPLICATE KEY UPDATE start_address = start_address;
+ ELSE
+ INSERT INTO flq_pool4 (start_address, end_address, subnet_id)
+ VALUES (p_start_address, p_end_address, p_subnet_id);
+ END IF;
+
+ -- Wipe out existing free addresses. This ensures we fix any mixed allocation entries.
+ DELETE FROM free_lease4 WHERE address >= p_start_address and address <= p_end_address;
+
+ -- Insert available leases into free_lease4 table. If insert fails on
+ -- duplicate ignore it. MySql limits recursive queries so we iterate
+ -- pool address range in chunks.
+ CALL setLoopLimit();
+ SET loop_limit = @kea_recursion_limit - 1;
+ SET next_start_address = p_start_address;
+ SET next_end_address = p_start_address;
+ start_loop: WHILE next_start_address <= p_end_address DO
+ SET remaining = p_end_address - next_start_address;
+ IF (remaining > loop_limit)
+ THEN
+ SET next_end_address = next_end_address + loop_limit;
+ ELSE
+ SET next_end_address = next_end_address + remaining;
+ END IF;
+
+ INSERT INTO free_lease4 (address)
+ WITH RECURSIVE addresses AS (
+ SELECT next_start_address AS avail
+ UNION ALL
+ SELECT avail + 1 FROM addresses WHERE avail < next_end_address
+ )
+ SELECT avail FROM addresses
+ LEFT JOIN lease4 on avail = lease4.address
+ WHERE (lease4.address IS NULL OR lease4.state = 2)
+ ON DUPLICATE KEY UPDATE free_lease4.address = avail;
+
+ IF next_start_address + loop_limit <= 4294967295 THEN
+ SET next_start_address = next_start_address + loop_limit;
+ ELSE
+ LEAVE start_loop;
+ END IF;
+ END WHILE start_loop;
+
+ -- Update the modification time in the flq_pool row.
+ UPDATE flq_pool4 SET modification_ts = now()
+ WHERE (start_address = p_start_address AND end_address = p_end_address);
+END $$
+DELIMITER ;
+
+-- Populate flq_pool6 and free_lease6 based on an address
+-- range and delegated len. Use 128 for NA addresses.
+-- This must be called from within a transaction.
+DROP PROCEDURE IF EXISTS sflqCreateFlqPool6;
+DELIMITER $$
+CREATE PROCEDURE sflqCreateFlqPool6(IN p_start_address VARCHAR(45),
+ IN p_end_address VARCHAR(45),
+ IN p_lease_type TINYINT UNSIGNED,
+ IN p_delegated_len TINYINT UNSIGNED,
+ IN p_subnet_id INT UNSIGNED,
+ IN p_recreate TINYINT)
+BEGIN
+ DECLARE bin_next_address BINARY(16);
+ DECLARE bin_end_address BINARY(16);
+ DECLARE lease_address VARCHAR(45);
+ DECLARE error_msg VARCHAR(512);
+ DECLARE x_delegated_len TINYINT UNSIGNED;
+ DECLARE pool_changed TINYINT;
+
+ SET bin_next_address = INET6_ATON(p_start_address);
+ SET bin_end_address = INET6_ATON(p_end_address);
+
+ IF (p_start_address = "::" OR
+ bin_next_address > bin_end_address)
+ THEN
+ SET error_msg = CONCAT('Invalid range: ', p_start_address,
+ ' - ', p_end_address);
+ SIGNAL SQLSTATE '45000'
+ SET MESSAGE_TEXT = error_msg;
+ END IF;
+
+ IF (p_delegated_len < 1 OR p_delegated_len > 128)
+ THEN
+ SET error_msg = CONCAT('Invalid p_delegated_len: ', p_delegated_len);
+ SIGNAL SQLSTATE '45000'
+ SET MESSAGE_TEXT = error_msg;
+ END IF;
+
+ -- If we are not re-creating the pool, look for a pre-existing pool
+ -- and see if delegated length has changed. If so we need to treat
+ -- it as recreate. This should catch configuration changes to
+ -- pools pre-existing pools.
+ SET pool_changed = 0;
+ IF (p_recreate = 0)
+ THEN
+ SELECT count(*) INTO pool_changed
+ FROM flq_pool6
+ WHERE (start_address = p_start_address AND
+ end_address = p_end_address AND
+ delegated_len != p_delegated_len)
+ LIMIT 1;
+ END IF;
+
+ -- Create the flq_pool6 row. Concurrent attempts will hang until
+ -- this one commits and then they will fail with duplicate key
+ -- error. Callers should treat the duplicate error as success.
+ IF (p_recreate = 1 OR pool_changed = 1)
+ THEN
+ -- (Re)creating should ignore duplicate on insert
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id)
+ ON DUPLICATE KEY UPDATE
+ -- Recreate may have changed these
+ lease_type = p_lease_type,
+ delegated_len = p_delegated_len,
+ subnet_id = p_subnet_id;
+ ELSE
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id);
+ END IF;
+
+ -- Wipe out existing free addresses. This ensures we fix any mixed allocation entries.
+ DELETE FROM free_lease6 WHERE bin_address >= bin_next_address and bin_address <= bin_end_address;
+ -- Enumerate the addresses into a temp table to use in a bulk insert.
+ DROP TEMPORARY TABLE IF EXISTS _flq6_candidates;
+ CREATE TEMPORARY TABLE _flq6_candidates (
+ bin_address BINARY(16) NOT NULL PRIMARY KEY,
+ address VARCHAR(45) NOT NULL
+ ) ENGINE=InnoDB;
+
+ label: WHILE bin_next_address <= bin_end_address
+ DO
+ INSERT INTO _flq6_candidates (bin_address, address)
+ VALUES (bin_next_address, INET6_NTOA(bin_next_address));
+
+ IF (bin_next_address = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ THEN
+ LEAVE label;
+ END IF;
+
+ SET bin_next_address = incrementV6Prefix(bin_next_address, p_delegated_len);
+ END WHILE label;
+
+ -- Bulk insert available leases into free_lease6 table. If insert fails on
+ -- duplicate ignore it.
+ INSERT INTO free_lease6 (address, bin_address)
+ SELECT c.address, c.bin_address
+ FROM _flq6_candidates c
+ WHERE NOT EXISTS (
+ SELECT 1 FROM lease6 l
+ WHERE l.address = c.address AND l.state != 2
+ )
+ ON DUPLICATE KEY UPDATE free_lease6.address = c.address;
+
+ DROP TEMPORARY TABLE _flq6_candidates;
+
+ -- Update the modification time in the flq_pool row.
+ UPDATE flq_pool6 SET modification_ts = now()
+ WHERE (start_address = p_start_address AND end_address = p_end_address);
+END $$
+DELIMITER ;
+
+-- Update the schema version number.
+UPDATE schema_version
+ SET version = '35', minor = '0';
+
+-- This line concludes the schema upgrade to version 35.0.
# Notes:
#
'upgrade_031_to_032.sh',
'upgrade_032_to_033.sh',
'upgrade_033_to_034.sh',
+ 'upgrade_034_to_035.sh',
]
list = run_command(
GRABBER,
--- /dev/null
+#!/bin/sh
+
+# Copyright (C) 2025-2026 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/.
+
+# 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 based on location of this script. Check for sources first,
+# so that the unexpected situations with weird paths fall on the default
+# case of installed.
+script_path=$(cd "$(dirname "${0}")" && pwd)
+if test "${script_path}" = "@abs_top_builddir@/src/share/database/scripts/mysql"; then
+ # shellcheck source=./src/bin/admin/admin-utils.sh.in
+ . "@abs_top_builddir@/src/bin/admin/admin-utils.sh"
+else
+ # shellcheck source=./src/bin/admin/admin-utils.sh.in
+ . "@datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh"
+fi
+
+# Check only major version to allow for intermediary backported schema changes.
+version=$(mysql_version "${@}" | cut -d '.' -f 1)
+if test "${version}" != '34'; then
+ printf 'This script upgrades 34.* to 35.0. '
+ printf 'Reported version is %s. Skipping upgrade.\n' "${version}"
+ exit 0
+fi
+
+# Get the schema name from database argument. We need this to
+# query information_schema for the right database.
+for arg in "${@}"
+do
+ if ! printf '%s' "${arg}" | grep -Eq -- '^--'
+ then
+ schema="$arg"
+ break
+ fi
+done
+
+# Make sure we have the schema.
+if [ -z "$schema" ]
+then
+ printf "Could not find database schema name in cmd line args: %s\n" "${*}"
+ exit 255
+fi
+
+mysql "$@" <<EOF
+
+-- This line starts the schema upgrade to version 35.0.
+
+-- Remove transaction statements.
+-- Populate flq_pool4 and free_lease4 based on an address range.
+-- This must be called from within a transaction.
+DROP PROCEDURE IF EXISTS sflqCreateFlqPool4;
+DELIMITER $$
+CREATE PROCEDURE sflqCreateFlqPool4(IN p_start_address INT UNSIGNED,
+ IN p_end_address INT UNSIGNED,
+ IN p_subnet_id INT UNSIGNED,
+ IN p_recreate TINYINT)
+BEGIN
+ DECLARE loop_limit INT UNSIGNED;
+ DECLARE next_start_address INT UNSIGNED;
+ DECLARE next_end_address INT UNSIGNED;
+ DECLARE remaining INT UNSIGNED;
+ DECLARE error_msg VARCHAR(512);
+
+ IF (p_start_address = inet_aton('0.0.0.0') OR
+ p_start_address > p_end_address)
+ THEN
+ SET error_msg = CONCAT('Invalid range: ', inet_ntoa(p_start_address),
+ ' - ', inet_ntoa(p_end_address));
+ SIGNAL SQLSTATE '45000'
+ SET MESSAGE_TEXT = error_msg;
+ END IF;
+
+ -- Create the flq_pool4 row. Concurrent attempts will hang until
+ -- this one commits and then they will fail with duplicate key
+ -- error. Callers should treat the duplicate error as success.
+ IF p_recreate = 1
+ THEN
+ -- (Re)creating should ignore duplicate on insert
+ INSERT INTO flq_pool4 (start_address, end_address, subnet_id)
+ VALUES (p_start_address, p_end_address, p_subnet_id)
+ ON DUPLICATE KEY UPDATE start_address = start_address;
+ ELSE
+ INSERT INTO flq_pool4 (start_address, end_address, subnet_id)
+ VALUES (p_start_address, p_end_address, p_subnet_id);
+ END IF;
+
+ -- Wipe out existing free addresses. This ensures we fix any mixed allocation entries.
+ DELETE FROM free_lease4 WHERE address >= p_start_address and address <= p_end_address;
+
+ -- Insert available leases into free_lease4 table. If insert fails on
+ -- duplicate ignore it. MySql limits recursive queries so we iterate
+ -- pool address range in chunks.
+ CALL setLoopLimit();
+ SET loop_limit = @kea_recursion_limit - 1;
+ SET next_start_address = p_start_address;
+ SET next_end_address = p_start_address;
+ start_loop: WHILE next_start_address <= p_end_address DO
+ SET remaining = p_end_address - next_start_address;
+ IF (remaining > loop_limit)
+ THEN
+ SET next_end_address = next_end_address + loop_limit;
+ ELSE
+ SET next_end_address = next_end_address + remaining;
+ END IF;
+
+ INSERT INTO free_lease4 (address)
+ WITH RECURSIVE addresses AS (
+ SELECT next_start_address AS avail
+ UNION ALL
+ SELECT avail + 1 FROM addresses WHERE avail < next_end_address
+ )
+ SELECT avail FROM addresses
+ LEFT JOIN lease4 on avail = lease4.address
+ WHERE (lease4.address IS NULL OR lease4.state = 2)
+ ON DUPLICATE KEY UPDATE free_lease4.address = avail;
+
+ IF next_start_address + loop_limit <= 4294967295 THEN
+ SET next_start_address = next_start_address + loop_limit;
+ ELSE
+ LEAVE start_loop;
+ END IF;
+ END WHILE start_loop;
+
+ -- Update the modification time in the flq_pool row.
+ UPDATE flq_pool4 SET modification_ts = now()
+ WHERE (start_address = p_start_address AND end_address = p_end_address);
+END $$
+DELIMITER ;
+
+-- Populate flq_pool6 and free_lease6 based on an address
+-- range and delegated len. Use 128 for NA addresses.
+-- This must be called from within a transaction.
+DROP PROCEDURE IF EXISTS sflqCreateFlqPool6;
+DELIMITER $$
+CREATE PROCEDURE sflqCreateFlqPool6(IN p_start_address VARCHAR(45),
+ IN p_end_address VARCHAR(45),
+ IN p_lease_type TINYINT UNSIGNED,
+ IN p_delegated_len TINYINT UNSIGNED,
+ IN p_subnet_id INT UNSIGNED,
+ IN p_recreate TINYINT)
+BEGIN
+ DECLARE bin_next_address BINARY(16);
+ DECLARE bin_end_address BINARY(16);
+ DECLARE lease_address VARCHAR(45);
+ DECLARE error_msg VARCHAR(512);
+ DECLARE x_delegated_len TINYINT UNSIGNED;
+ DECLARE pool_changed TINYINT;
+
+ SET bin_next_address = INET6_ATON(p_start_address);
+ SET bin_end_address = INET6_ATON(p_end_address);
+
+ IF (p_start_address = "::" OR
+ bin_next_address > bin_end_address)
+ THEN
+ SET error_msg = CONCAT('Invalid range: ', p_start_address,
+ ' - ', p_end_address);
+ SIGNAL SQLSTATE '45000'
+ SET MESSAGE_TEXT = error_msg;
+ END IF;
+
+ IF (p_delegated_len < 1 OR p_delegated_len > 128)
+ THEN
+ SET error_msg = CONCAT('Invalid p_delegated_len: ', p_delegated_len);
+ SIGNAL SQLSTATE '45000'
+ SET MESSAGE_TEXT = error_msg;
+ END IF;
+
+ -- If we are not re-creating the pool, look for a pre-existing pool
+ -- and see if delegated length has changed. If so we need to treat
+ -- it as recreate. This should catch configuration changes to
+ -- pools pre-existing pools.
+ SET pool_changed = 0;
+ IF (p_recreate = 0)
+ THEN
+ SELECT count(*) INTO pool_changed
+ FROM flq_pool6
+ WHERE (start_address = p_start_address AND
+ end_address = p_end_address AND
+ delegated_len != p_delegated_len)
+ LIMIT 1;
+ END IF;
+
+ -- Create the flq_pool6 row. Concurrent attempts will hang until
+ -- this one commits and then they will fail with duplicate key
+ -- error. Callers should treat the duplicate error as success.
+ IF (p_recreate = 1 OR pool_changed = 1)
+ THEN
+ -- (Re)creating should ignore duplicate on insert
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id)
+ ON DUPLICATE KEY UPDATE
+ -- Recreate may have changed these
+ lease_type = p_lease_type,
+ delegated_len = p_delegated_len,
+ subnet_id = p_subnet_id;
+ ELSE
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id);
+ END IF;
+
+ -- Wipe out existing free addresses. This ensures we fix any mixed allocation entries.
+ DELETE FROM free_lease6 WHERE bin_address >= bin_next_address and bin_address <= bin_end_address;
+ -- Enumerate the addresses into a temp table to use in a bulk insert.
+ DROP TEMPORARY TABLE IF EXISTS _flq6_candidates;
+ CREATE TEMPORARY TABLE _flq6_candidates (
+ bin_address BINARY(16) NOT NULL PRIMARY KEY,
+ address VARCHAR(45) NOT NULL
+ ) ENGINE=InnoDB;
+
+ label: WHILE bin_next_address <= bin_end_address
+ DO
+ INSERT INTO _flq6_candidates (bin_address, address)
+ VALUES (bin_next_address, INET6_NTOA(bin_next_address));
+
+ IF (bin_next_address = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
+ THEN
+ LEAVE label;
+ END IF;
+
+ SET bin_next_address = incrementV6Prefix(bin_next_address, p_delegated_len);
+ END WHILE label;
+
+ -- Bulk insert available leases into free_lease6 table. If insert fails on
+ -- duplicate ignore it.
+ INSERT INTO free_lease6 (address, bin_address)
+ SELECT c.address, c.bin_address
+ FROM _flq6_candidates c
+ WHERE NOT EXISTS (
+ SELECT 1 FROM lease6 l
+ WHERE l.address = c.address AND l.state != 2
+ )
+ ON DUPLICATE KEY UPDATE free_lease6.address = c.address;
+
+ DROP TEMPORARY TABLE _flq6_candidates;
+
+ -- Update the modification time in the flq_pool row.
+ UPDATE flq_pool6 SET modification_ts = now()
+ WHERE (start_address = p_start_address AND end_address = p_end_address);
+END $$
+DELIMITER ;
+
+-- Update the schema version number.
+UPDATE schema_version
+ SET version = '35', minor = '0';
+
+-- This line concludes the schema upgrade to version 35.0.
+
+EOF
-- This line concludes the schema upgrade to version 33.0.
+-- This line starts the schema upgrade to version 34.0.
+
+-- Populate flq_pool6 and free_lease6 based on an address
+-- range and delegated len. Use 128 for NA addresses.
+CREATE OR REPLACE FUNCTION sflqCreateFlqPool6(p_start_address INET,
+ p_end_address INET,
+ p_lease_type SMALLINT,
+ p_delegated_len SMALLINT,
+ p_subnet_id BIGINT,
+ p_recreate BOOLEAN)
+RETURNS VOID
+LANGUAGE plpgsql AS $$
+DECLARE
+ current_ts TIMESTAMP WITH TIME ZONE := now();
+ p_end_bin BYTEA := inetToBytea(p_end_address);
+ batch_start_address INET := p_start_address;
+ batch_start_bin BYTEA := inetToBytea(p_start_address);
+ max_prefixes_per_batch INTEGER := 10000;
+ last_bin BYTEA;
+ next_bin BYTEA;
+ pool_changed INTEGER;
+BEGIN
+ IF ((p_start_address = '::'::inet) OR (p_start_address > p_end_address))
+ THEN
+ RAISE EXCEPTION 'Invalid range: % - %', host(p_start_address), host(p_end_address);
+ END IF;
+
+ IF (p_delegated_len < 1 OR p_delegated_len > 128)
+ THEN
+ RAISE EXCEPTION 'Invalid p_delegated_len: %', p_delegated_len;
+ END IF;
+
+ -- If we are not re-creating the pool, look for a pre-existing pool
+ -- and see if delegated length has changed. If so we need to treat
+ -- it as recreate. This should catch configuration changes to
+ -- pre-existing pools.
+ pool_changed = 0;
+ IF (p_recreate = false)
+ THEN
+ SELECT count(*) INTO pool_changed
+ FROM flq_pool6
+ WHERE (start_address = p_start_address AND
+ end_address = p_end_address AND
+ delegated_len != p_delegated_len)
+ LIMIT 1;
+ END IF;
+
+ -- Create the flq_pool6 row. Concurrent attempts will hang until
+ -- this one commits and then they will fail with duplicate key
+ -- error. Callers should treat the duplicate error as success.
+ IF (p_recreate = true OR pool_changed > 0)
+ THEN
+ -- (Re)creating should ignore duplicate on insert
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id)
+ -- ON CONFLICT (start_address, end_address) DO UPDATE SET
+ ON CONFLICT (start_address, end_address) DO UPDATE SET
+ -- Recreate may have changed these
+ lease_type = p_lease_type,
+ delegated_len = p_delegated_len,
+ subnet_id = p_subnet_id;
+ ELSE
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id);
+ END IF;
+
+ -- Wipe out existing free addresses. This ensures we fix any mixed allocation entries.
+ DELETE FROM free_lease6 WHERE address >= p_start_address and address <= p_end_address;
+
+ -- Enumerate prefixes in bounded chunks and insert free entries in bulk.
+ WHILE batch_start_address <= p_end_address
+ LOOP
+ WITH RECURSIVE prefixes(depth, bin_address, address) AS (
+ SELECT 1, batch_start_bin, batch_start_address
+ UNION ALL
+ SELECT prefixes.depth + 1,
+ next_prefix.bin_address,
+ byteaToInet(next_prefix.bin_address)
+ FROM prefixes,
+ LATERAL (SELECT incrementV6Prefix(prefixes.bin_address, p_delegated_len)
+ AS bin_address) AS next_prefix
+ WHERE (next_prefix.bin_address > prefixes.bin_address AND
+ next_prefix.bin_address < p_end_bin AND
+ prefixes.depth < max_prefixes_per_batch)
+ ),
+ ins AS (
+ INSERT INTO free_lease6(address, bin_address)
+ SELECT p.address, p.bin_address
+ FROM prefixes p
+ LEFT JOIN lease6 l
+ ON l.address = p.address AND l.state != 2
+ WHERE l.address IS NULL
+ ON CONFLICT DO NOTHING
+ RETURNING 1
+ )
+ SELECT last_prefix.bin_address INTO STRICT last_bin
+ FROM (
+ SELECT bin_address
+ FROM prefixes
+ ORDER BY depth DESC
+ LIMIT 1
+ ) AS last_prefix;
+
+ next_bin := incrementV6Prefix(last_bin, p_delegated_len);
+
+ -- Stepped past the pool end (binary order), or increment wrapped at
+ -- all-ones IPv6 (:: after FFFF:...:FFFF) so inet compare would loop forever.
+ IF next_bin > p_end_bin OR next_bin < last_bin
+ THEN
+ EXIT;
+ END IF;
+
+ batch_start_bin := next_bin;
+ batch_start_address := byteaToInet(next_bin);
+ END LOOP;
+
+ -- Update the modification time in the flq_pool row.
+ UPDATE flq_pool6 SET modification_ts = now()
+ WHERE (start_address = p_start_address AND end_address = p_end_address);
+END;
+$$;
+
+-- Update the schema version number.
+UPDATE schema_version
+ SET version = '34', minor = '0';
+
+-- This line concludes the schema upgrade to version 34.0.
+
+
-- Commit the script transaction.
COMMIT;
'upgrade_030_to_031.sh',
'upgrade_031_to_032.sh',
'upgrade_032_to_033.sh',
+ 'upgrade_033_to_034.sh',
]
list = run_command(
GRABBER,
--- /dev/null
+#!/bin/sh
+
+# Copyright (C) 2026 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/.
+
+# 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 based on location of this script. Check for sources first,
+# so that the unexpected situations with weird paths fall on the default
+# case of installed.
+script_path=$(cd "$(dirname "${0}")" && pwd)
+if test "${script_path}" = "@abs_top_builddir@/src/share/database/scripts/pgsql"; then
+ # shellcheck source=./src/bin/admin/admin-utils.sh.in
+ . "@abs_top_builddir@/src/bin/admin/admin-utils.sh"
+else
+ # shellcheck source=./src/bin/admin/admin-utils.sh.in
+ . "@datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh"
+fi
+
+# Check only major version to allow for intermediary backported schema changes.
+version=$(pgsql_version "${@}" | cut -d '.' -f 1)
+if test "${version}" != '33'; then
+ printf 'This script upgrades 33.* to 34.0. '
+ printf 'Reported version is %s. Skipping upgrade.\n' "${version}"
+ exit 0
+fi
+
+psql "$@" >/dev/null <<EOF
+START TRANSACTION;
+
+-- This line starts the schema upgrade to version 34.0.
+
+-- Populate flq_pool6 and free_lease6 based on an address
+-- range and delegated len. Use 128 for NA addresses.
+CREATE OR REPLACE FUNCTION sflqCreateFlqPool6(p_start_address INET,
+ p_end_address INET,
+ p_lease_type SMALLINT,
+ p_delegated_len SMALLINT,
+ p_subnet_id BIGINT,
+ p_recreate BOOLEAN)
+RETURNS VOID
+LANGUAGE plpgsql AS \$\$
+DECLARE
+ current_ts TIMESTAMP WITH TIME ZONE := now();
+ p_end_bin BYTEA := inetToBytea(p_end_address);
+ batch_start_address INET := p_start_address;
+ batch_start_bin BYTEA := inetToBytea(p_start_address);
+ max_prefixes_per_batch INTEGER := 10000;
+ last_bin BYTEA;
+ next_bin BYTEA;
+ pool_changed INTEGER;
+BEGIN
+ IF ((p_start_address = '::'::inet) OR (p_start_address > p_end_address))
+ THEN
+ RAISE EXCEPTION 'Invalid range: % - %', host(p_start_address), host(p_end_address);
+ END IF;
+
+ IF (p_delegated_len < 1 OR p_delegated_len > 128)
+ THEN
+ RAISE EXCEPTION 'Invalid p_delegated_len: %', p_delegated_len;
+ END IF;
+
+ -- If we are not re-creating the pool, look for a pre-existing pool
+ -- and see if delegated length has changed. If so we need to treat
+ -- it as recreate. This should catch configuration changes to
+ -- pre-existing pools.
+ pool_changed = 0;
+ IF (p_recreate = false)
+ THEN
+ SELECT count(*) INTO pool_changed
+ FROM flq_pool6
+ WHERE (start_address = p_start_address AND
+ end_address = p_end_address AND
+ delegated_len != p_delegated_len)
+ LIMIT 1;
+ END IF;
+
+ -- Create the flq_pool6 row. Concurrent attempts will hang until
+ -- this one commits and then they will fail with duplicate key
+ -- error. Callers should treat the duplicate error as success.
+ IF (p_recreate = true OR pool_changed > 0)
+ THEN
+ -- (Re)creating should ignore duplicate on insert
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id)
+ -- ON CONFLICT (start_address, end_address) DO UPDATE SET
+ ON CONFLICT (start_address, end_address) DO UPDATE SET
+ -- Recreate may have changed these
+ lease_type = p_lease_type,
+ delegated_len = p_delegated_len,
+ subnet_id = p_subnet_id;
+ ELSE
+ INSERT INTO flq_pool6
+ (start_address, end_address, lease_type, delegated_len, subnet_id)
+ VALUES
+ (p_start_address, p_end_address, p_lease_type, p_delegated_len, p_subnet_id);
+ END IF;
+
+ -- Wipe out existing free addresses. This ensures we fix any mixed allocation entries.
+ DELETE FROM free_lease6 WHERE address >= p_start_address and address <= p_end_address;
+
+ -- Enumerate prefixes in bounded chunks and insert free entries in bulk.
+ WHILE batch_start_address <= p_end_address
+ LOOP
+ WITH RECURSIVE prefixes(depth, bin_address, address) AS (
+ SELECT 1, batch_start_bin, batch_start_address
+ UNION ALL
+ SELECT prefixes.depth + 1,
+ next_prefix.bin_address,
+ byteaToInet(next_prefix.bin_address)
+ FROM prefixes,
+ LATERAL (SELECT incrementV6Prefix(prefixes.bin_address, p_delegated_len)
+ AS bin_address) AS next_prefix
+ WHERE (next_prefix.bin_address > prefixes.bin_address AND
+ next_prefix.bin_address < p_end_bin AND
+ prefixes.depth < max_prefixes_per_batch)
+ ),
+ ins AS (
+ INSERT INTO free_lease6(address, bin_address)
+ SELECT p.address, p.bin_address
+ FROM prefixes p
+ LEFT JOIN lease6 l
+ ON l.address = p.address AND l.state != 2
+ WHERE l.address IS NULL
+ ON CONFLICT DO NOTHING
+ RETURNING 1
+ )
+ SELECT last_prefix.bin_address INTO STRICT last_bin
+ FROM (
+ SELECT bin_address
+ FROM prefixes
+ ORDER BY depth DESC
+ LIMIT 1
+ ) AS last_prefix;
+
+ next_bin := incrementV6Prefix(last_bin, p_delegated_len);
+
+ -- Stepped past the pool end (binary order), or increment wrapped at
+ -- all-ones IPv6 (:: after FFFF:...:FFFF) so inet compare would loop forever.
+ IF next_bin > p_end_bin OR next_bin < last_bin
+ THEN
+ EXIT;
+ END IF;
+
+ batch_start_bin := next_bin;
+ batch_start_address := byteaToInet(next_bin);
+ END LOOP;
+
+ -- Update the modification time in the flq_pool row.
+ UPDATE flq_pool6 SET modification_ts = now()
+ WHERE (start_address = p_start_address AND end_address = p_end_address);
+END;
+\$\$;
+
+-- Update the schema version number.
+UPDATE schema_version
+ SET version = '34', minor = '0';
+
+-- This line concludes the schema upgrade to version 34.0.
+
+-- Commit the script transaction.
+COMMIT;
+
+EOF