From 7b6b0ce28b615cad9ce94029eaef0c0efb1f4443 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 5 Nov 2025 22:29:39 +1300 Subject: [PATCH] Sync locks --- src/Database/Adapter.php | 23 +++++++++++++++++++ src/Database/Adapter/MariaDB.php | 5 ++++ src/Database/Adapter/Mongo.php | 5 ++++ src/Database/Adapter/Pool.php | 5 ++++ src/Database/Adapter/SQL.php | 18 +++++++++++++-- src/Database/Adapter/SQLite.php | 5 ++++ src/Database/Database.php | 17 ++++++++++++++ .../e2e/Adapter/SharedTables/MariaDBTest.php | 4 +++- tests/e2e/Adapter/SharedTables/MySQLTest.php | 4 +++- 9 files changed, 82 insertions(+), 4 deletions(-) diff --git a/src/Database/Adapter.php b/src/Database/Adapter.php index 925c7eb72..8fc62aebb 100644 --- a/src/Database/Adapter.php +++ b/src/Database/Adapter.php @@ -30,6 +30,8 @@ abstract class Adapter protected int $inTransaction = 0; + protected bool $alterLocks = false; + /** * @var array */ @@ -1412,4 +1414,25 @@ abstract public function setSupportForAttributes(bool $support): bool; */ abstract public function getSupportForIntegerBooleans(): bool; + /** + * Does the adapter have support for ALTER TABLE locking modes? + * + * When enabled, adapters can specify lock behavior (e.g., LOCK=SHARED) + * during ALTER TABLE operations to control concurrent access. + * + * @return bool + */ + abstract public function getSupportForAlterLocks(): bool; + + /** + * @param bool $enable + * + * @return $this + */ + public function enableAlterLocks(bool $enable): self + { + $this->alterLocks = $enable; + + return $this; + } } diff --git a/src/Database/Adapter/MariaDB.php b/src/Database/Adapter/MariaDB.php index a6233d189..0cc86e6c5 100644 --- a/src/Database/Adapter/MariaDB.php +++ b/src/Database/Adapter/MariaDB.php @@ -2220,4 +2220,9 @@ public function getSupportForOptionalSpatialAttributeWithExistingRows(): bool { return true; } + + public function getSupportForAlterLocks(): bool + { + return true; + } } diff --git a/src/Database/Adapter/Mongo.php b/src/Database/Adapter/Mongo.php index 9fd57557f..32a0faee6 100644 --- a/src/Database/Adapter/Mongo.php +++ b/src/Database/Adapter/Mongo.php @@ -3211,4 +3211,9 @@ public function getTenantQuery(string $collection, string $alias = ''): string { return ''; } + + public function getSupportForAlterLocks(): bool + { + return false; + } } diff --git a/src/Database/Adapter/Pool.php b/src/Database/Adapter/Pool.php index 6adff2f27..e5d4b746f 100644 --- a/src/Database/Adapter/Pool.php +++ b/src/Database/Adapter/Pool.php @@ -619,4 +619,9 @@ public function getSupportForIntegerBooleans(): bool { return $this->delegate(__FUNCTION__, \func_get_args()); } + + public function getSupportForAlterLocks(): bool + { + return $this->delegate(__FUNCTION__, \func_get_args()); + } } diff --git a/src/Database/Adapter/SQL.php b/src/Database/Adapter/SQL.php index a7121d36d..7af02a4f7 100644 --- a/src/Database/Adapter/SQL.php +++ b/src/Database/Adapter/SQL.php @@ -248,7 +248,7 @@ public function createAttribute(string $collection, string $id, string $type, in { $id = $this->quote($this->filter($id)); $type = $this->getSQLType($type, $size, $signed, $array, $required); - $sql = "ALTER TABLE {$this->getSQLTable($collection)} ADD COLUMN {$id} {$type};"; + $sql = "ALTER TABLE {$this->getSQLTable($collection)} ADD COLUMN {$id} {$type} {$this->getLockType()};"; $sql = $this->trigger(Database::EVENT_ATTRIBUTE_CREATE, $sql); try { @@ -285,7 +285,7 @@ public function createAttributes(string $collection, array $attributes): bool $columns = \implode(', ADD COLUMN ', $parts); - $sql = "ALTER TABLE {$this->getSQLTable($collection)} ADD COLUMN {$columns};"; + $sql = "ALTER TABLE {$this->getSQLTable($collection)} ADD COLUMN {$columns} {$this->getLockType()};"; $sql = $this->trigger(Database::EVENT_ATTRIBUTE_CREATE, $sql); try { @@ -3515,4 +3515,18 @@ public function setSupportForAttributes(bool $support): bool { return true; } + + public function getSupportForAlterLocks(): bool + { + return false; + } + + public function getLockType(): string + { + if ($this->getSupportForAlterLocks() && $this->alterLocks) { + return ',LOCK=SHARED'; + } + + return ''; + } } diff --git a/src/Database/Adapter/SQLite.php b/src/Database/Adapter/SQLite.php index e50bd2068..1260ccca0 100644 --- a/src/Database/Adapter/SQLite.php +++ b/src/Database/Adapter/SQLite.php @@ -1866,4 +1866,9 @@ public function getUpsertStatement( return $stmt; } + + public function getSupportForAlterLocks(): bool + { + return false; + } } diff --git a/src/Database/Database.php b/src/Database/Database.php index e348b2df6..fcb421f15 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -1186,6 +1186,23 @@ public function getTenantPerDocument(): bool return $this->adapter->getTenantPerDocument(); } + /** + * Enable or disable LOCK=SHARED during ALTER TABLE operation + * + * Set lock mode when altering tables + * + * @param bool $enabled + * @return static + */ + public function enableLocks(bool $enabled): static + { + if ($this->adapter->getSupportForAlterLocks()) { + $this->adapter->enableAlterLocks($enabled); + } + + return $this; + } + public function getPreserveDates(): bool { return $this->preserveDates; diff --git a/tests/e2e/Adapter/SharedTables/MariaDBTest.php b/tests/e2e/Adapter/SharedTables/MariaDBTest.php index 346775691..6c5292b70 100644 --- a/tests/e2e/Adapter/SharedTables/MariaDBTest.php +++ b/tests/e2e/Adapter/SharedTables/MariaDBTest.php @@ -52,7 +52,9 @@ public static function getDatabase(bool $fresh = false): Database ->setDatabase('utopiaTests') ->setSharedTables(true) ->setTenant(999) - ->setNamespace(static::$namespace = ''); + ->setNamespace(static::$namespace = '') + ->enableLocks(true) + ; if ($database->exists()) { $database->delete(); diff --git a/tests/e2e/Adapter/SharedTables/MySQLTest.php b/tests/e2e/Adapter/SharedTables/MySQLTest.php index 7d72d0635..12b9e0132 100644 --- a/tests/e2e/Adapter/SharedTables/MySQLTest.php +++ b/tests/e2e/Adapter/SharedTables/MySQLTest.php @@ -54,7 +54,9 @@ public static function getDatabase(): Database ->setDatabase('utopiaTests') ->setSharedTables(true) ->setTenant(999) - ->setNamespace(static::$namespace = ''); + ->setNamespace(static::$namespace = '') + ->enableLocks(true) + ; if ($database->exists()) { $database->delete();