-
Notifications
You must be signed in to change notification settings - Fork 54
Fix ghost perms #813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix ghost perms #813
Changes from all commits
33ed565
b8542f6
40080e2
9523b2a
a375c36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,12 @@ class Pool extends Adapter | |
| */ | ||
| protected UtopiaPool $pool; | ||
|
|
||
| /** | ||
| * When a transaction is active, all delegate calls are routed through | ||
| * this pinned adapter to ensure they run on the same connection. | ||
| */ | ||
| protected ?Adapter $pinnedAdapter = null; | ||
|
|
||
| /** | ||
| * @param UtopiaPool<covariant Adapter> $pool The pool to use for connections. Must contain instances of Adapter. | ||
| */ | ||
|
|
@@ -36,6 +42,10 @@ public function __construct(UtopiaPool $pool) | |
| */ | ||
| public function delegate(string $method, array $args): mixed | ||
| { | ||
| if ($this->pinnedAdapter !== null) { | ||
| return $this->pinnedAdapter->{$method}(...$args); | ||
| } | ||
|
|
||
| return $this->pool->use(function (Adapter $adapter) use ($method, $args) { | ||
| // Run setters in case config changed since this connection was last used | ||
| $adapter->setDatabase($this->getDatabase()); | ||
|
|
@@ -92,6 +102,52 @@ public function rollbackTransaction(): bool | |
| return $this->delegate(__FUNCTION__, \func_get_args()); | ||
| } | ||
|
|
||
| /** | ||
| * Pin a single connection from the pool for the entire transaction lifecycle. | ||
| * This prevents startTransaction(), the callback, and commitTransaction() | ||
| * from running on different connections. | ||
| * | ||
| * @template T | ||
| * @param callable(): T $callback | ||
| * @return T | ||
| * @throws \Throwable | ||
| */ | ||
| public function withTransaction(callable $callback): mixed | ||
| { | ||
| // If already inside a transaction, reuse the pinned adapter | ||
| // so nested withTransaction calls use the same connection | ||
| if ($this->pinnedAdapter !== null) { | ||
| return $this->pinnedAdapter->withTransaction($callback); | ||
| } | ||
|
|
||
| return $this->pool->use(function (Adapter $adapter) use ($callback) { | ||
| $adapter->setDatabase($this->getDatabase()); | ||
| $adapter->setNamespace($this->getNamespace()); | ||
| $adapter->setSharedTables($this->getSharedTables()); | ||
| $adapter->setTenant($this->getTenant()); | ||
| $adapter->setAuthorization($this->authorization); | ||
|
|
||
| if ($this->getTimeout() > 0) { | ||
| $adapter->setTimeout($this->getTimeout()); | ||
| } | ||
| $adapter->resetDebug(); | ||
| foreach ($this->getDebug() as $key => $value) { | ||
| $adapter->setDebug($key, $value); | ||
| } | ||
| $adapter->resetMetadata(); | ||
| foreach ($this->getMetadata() as $key => $value) { | ||
| $adapter->setMetadata($key, $value); | ||
| } | ||
|
|
||
| $this->pinnedAdapter = $adapter; | ||
| try { | ||
| return $adapter->withTransaction($callback); | ||
| } finally { | ||
| $this->pinnedAdapter = null; | ||
| } | ||
| }); | ||
| } | ||
|
Comment on lines
+115
to
+149
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check how Pool adapter instances are created/shared in the codebase
echo "=== Pool instantiation patterns ==="
rg -n 'new Pool\(' --type php -g '!vendor/**' -g '!tests/**' | head -100
echo ""
echo "=== Pool::class bindings in DI containers ==="
rg -n 'Pool::class' --type php -g '!vendor/**' -g '!tests/**' | head -100
echo ""
echo "=== Check for singleton/shared container bindings ==="
rg -n -B2 -A2 '(singleton|share|shared).*[Pp]ool' --type php -g '!vendor/**' -g '!tests/**' | head -100Repository: utopia-php/database Length of output: 200 🏁 Script executed: #!/bin/bash
# Check for Pool usage patterns (imports, instanceof, etc.)
echo "=== Import/use Pool patterns ==="
rg -n '(use.*Pool|instanceof Pool)' --type php -g '!vendor/**' | head -100
echo ""
echo "=== Pool class definition and interface ==="
rg -n 'class Pool\|interface.*Pool' --type php -g '!vendor/**' | head -50
echo ""
echo "=== Check test files for Pool usage ==="
rg -n 'new Pool\|Pool::class' --type php tests/ 2>/dev/null | head -100
echo ""
echo "=== Check if Pool is created through a factory or builder ==="
rg -n -B3 -A3 'function.*[Pp]ool|Pool.*factory' --type php -g '!vendor/**' | head -150Repository: utopia-php/database Length of output: 2067 🏁 Script executed: #!/bin/bash
# Read the Pool test file to understand usage patterns
echo "=== Pool Test File ==="
cat -n tests/e2e/Adapter/PoolTest.php
echo ""
echo "=== Search for pool->use() patterns ==="
rg -n -B2 -A5 'pool->use\(' --type php -g '!vendor/**' | head -150
echo ""
echo "=== Check for Fiber/async documentation or comments ==="
rg -n -i '(fiber|coroutine|concurrent|async|swoole|thread)' --type php src/Database/Adapter/Pool.phpRepository: utopia-php/database Length of output: 11805 🏁 Script executed: #!/bin/bash
# Check for Database instantiation patterns in production code
echo "=== Database instantiation patterns ==="
rg -n 'new Database\(' --type php src/ | head -100
echo ""
echo "=== Check Pool class structure and properties ==="
head -100 src/Database/Adapter/Pool.php
echo ""
echo "=== Look for README or documentation ==="
find . -maxdepth 2 -name 'README*' -o -name '*.md' | head -10Repository: utopia-php/database Length of output: 3362 🏁 Script executed: #!/bin/bash
# Check README for Pool usage documentation
echo "=== README Pool usage section ==="
grep -n -i -A10 -B2 'pool' README.md | head -100
echo ""
echo "=== Check SPEC for Pool design ==="
grep -n -i -A10 'pool\|transaction\|concurrent\|coroutine' SPEC.md | head -150Repository: utopia-php/database Length of output: 132 🏁 Script executed: #!/bin/bash
# Read full README
echo "=== Full README.md ==="
cat README.md
echo ""
echo "=== Search for UtopiaPool usage documentation ==="
rg -n 'UtopiaPool\|Pools\\Pool' --type php -B5 -A5 src/ | head -200Repository: utopia-php/database Length of output: 26019 🏁 Script executed: #!/bin/bash
# Check if there are any concurrency-related comments or patterns
echo "=== Search for concurrency/thread safety patterns in Pool ==="
cat -n src/Database/Adapter/Pool.php | grep -i -A5 -B5 'thread\|concurrent\|coroutine\|fiber\|async\|race\|mutex\|lock'
echo ""
echo "=== Check Pool's usage of delegate() with pinnedAdapter ==="
cat -n src/Database/Adapter/Pool.php | head -150
echo ""
echo "=== Search for any container/DI configuration files ==="
find . -maxdepth 3 -name 'config*' -o -name 'container*' -o -name 'services*' | grep -v vendor | grep -v node_modules
echo ""
echo "=== Check if there are any examples or integration tests showing shared Pool usage ==="
find . -path ./vendor -prune -o -name '*example*' -o -name '*integration*' -type f -print 2>/dev/null | head -20Repository: utopia-php/database Length of output: 7023 Document
While Pool is dependency-injected (suggesting per-request instantiation), the codebase lacks documentation or configuration showing the intended ownership model. Add documentation clarifying whether Pool instances must be instantiated per-request/per-coroutine, or refactor to use context-aware storage (e.g., 🤖 Prompt for AI Agents |
||
|
|
||
| protected function quote(string $string): string | ||
| { | ||
| return $this->delegate(__FUNCTION__, \func_get_args()); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.