From 930df5ba4732da93bfe63c584f5eb397672ec248 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 18 Jan 2023 20:24:40 +0100 Subject: [PATCH 1/2] close pdo cursor after handling results --- src/Analyzer/QueryPlanAnalyzerMysql.php | 10 ++++- src/DbSchema/SchemaHasherMysql.php | 2 + src/QueryReflection/MysqliQueryReflector.php | 2 +- .../PdoMysqlQueryReflector.php | 32 +++++++++------ .../PdoPgSqlQueryReflector.php | 41 ++++++++++--------- 5 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/Analyzer/QueryPlanAnalyzerMysql.php b/src/Analyzer/QueryPlanAnalyzerMysql.php index 2dc7a8da2..e4dca6802 100644 --- a/src/Analyzer/QueryPlanAnalyzerMysql.php +++ b/src/Analyzer/QueryPlanAnalyzerMysql.php @@ -57,7 +57,10 @@ public function analyze(string $query): QueryPlanResult $stmt = $this->connection->query($simulatedQuery); // @phpstan-ignore-next-line - return $this->buildResult($simulatedQuery, $stmt); + $planResult = $this->buildResult($simulatedQuery, $stmt); + $stmt->closeCursor(); + + return $planResult; } finally { $this->connection->rollBack(); } @@ -67,7 +70,10 @@ public function analyze(string $query): QueryPlanResult try { $result = $this->connection->query($simulatedQuery); if ($result instanceof \mysqli_result) { - return $this->buildResult($simulatedQuery, $result); + $planResult = $this->buildResult($simulatedQuery, $result); + $result->close(); + + return $planResult; } } finally { $this->connection->rollback(); diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 6054a2a82..919915bac 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -68,6 +68,7 @@ public function hashDb(): string foreach ($stmt as $row) { $hash = $row['dbsignature'] ?? ''; } + $stmt->closeCursor(); } finally { $this->connection->rollBack(); } @@ -80,6 +81,7 @@ public function hashDb(): string $row = $result->fetch_assoc(); $hash = $row['dbsignature'] ?? ''; } + $result->close(); } finally { $this->connection->rollback(); } diff --git a/src/QueryReflection/MysqliQueryReflector.php b/src/QueryReflection/MysqliQueryReflector.php index 6eb93a900..7c1b3645a 100644 --- a/src/QueryReflection/MysqliQueryReflector.php +++ b/src/QueryReflection/MysqliQueryReflector.php @@ -160,7 +160,7 @@ private function simulateQuery(string $queryString) } $resultInfo = $result->fetch_fields(); - $result->free(); + $result->close(); return $this->cache[$queryString] = $resultInfo; } catch (mysqli_sql_exception $e) { diff --git a/src/QueryReflection/PdoMysqlQueryReflector.php b/src/QueryReflection/PdoMysqlQueryReflector.php index 98407fd87..8dc5c9aa4 100644 --- a/src/QueryReflection/PdoMysqlQueryReflector.php +++ b/src/QueryReflection/PdoMysqlQueryReflector.php @@ -82,6 +82,8 @@ protected function simulateQuery(string $queryString) ++$columnIndex; } + $stmt->closeCursor(); + return $this->cache[$queryString]; } @@ -107,20 +109,24 @@ protected function checkInformationSchema(string $tableName): Iterator ); } - $this->stmt->execute([$tableName]); - $result = $this->stmt->fetchAll(PDO::FETCH_ASSOC); - - foreach ($result as $row) { - $extra = $row['EXTRA']; - $columnType = $row['COLUMN_TYPE']; - $columnName = $row['COLUMN_NAME']; - - if (str_contains($extra, 'auto_increment')) { - yield $columnName => TypeMapper::FLAG_AUTO_INCREMENT; - } - if (str_contains($columnType, 'unsigned')) { - yield $columnName => TypeMapper::FLAG_UNSIGNED; + try { + $this->stmt->execute([$tableName]); + $result = $this->stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($result as $row) { + $extra = $row['EXTRA']; + $columnType = $row['COLUMN_TYPE']; + $columnName = $row['COLUMN_NAME']; + + if (str_contains($extra, 'auto_increment')) { + yield $columnName => TypeMapper::FLAG_AUTO_INCREMENT; + } + if (str_contains($columnType, 'unsigned')) { + yield $columnName => TypeMapper::FLAG_UNSIGNED; + } } + } finally { + $this->stmt->closeCursor(); } } } diff --git a/src/QueryReflection/PdoPgSqlQueryReflector.php b/src/QueryReflection/PdoPgSqlQueryReflector.php index f513135e2..e3d44ce54 100644 --- a/src/QueryReflection/PdoPgSqlQueryReflector.php +++ b/src/QueryReflection/PdoPgSqlQueryReflector.php @@ -56,12 +56,7 @@ protected function simulateQuery(string $queryString) } catch (PDOException $e) { return $this->cache[$queryString] = $e; } finally { - try { - $this->pdo->rollBack(); - } catch (PDOException $e) { - // not all drivers may support transactions - throw new \RuntimeException('Failed to rollback transaction', $e->getCode(), $e); - } + $this->pdo->rollBack(); } $this->cache[$queryString] = []; @@ -91,6 +86,8 @@ protected function simulateQuery(string $queryString) ++$columnIndex; } + $stmt->closeCursor(); + return $this->cache[$queryString]; } @@ -114,21 +111,25 @@ protected function checkInformationSchema(string $tableName): Iterator ); } - $this->stmt->execute([$tableName]); - $result = $this->stmt->fetchAll(PDO::FETCH_ASSOC); - - /** @var array{column_default?: string, column_name: string, is_nullable: string} $row */ - foreach ($result as $row) { - $default = $row['column_default'] ?? ''; - $columnName = $row['column_name']; - $isNullable = 'YES' === $row['is_nullable']; - - if (!$isNullable) { - yield $columnName => PgsqlTypeMapper::FLAG_NOT_NULL; - } - if (str_contains($default, 'nextval')) { - yield $columnName => PgsqlTypeMapper::FLAG_AUTO_INCREMENT; + try { + $this->stmt->execute([$tableName]); + $result = $this->stmt->fetchAll(PDO::FETCH_ASSOC); + + /** @var array{column_default?: string, column_name: string, is_nullable: string} $row */ + foreach ($result as $row) { + $default = $row['column_default'] ?? ''; + $columnName = $row['column_name']; + $isNullable = 'YES' === $row['is_nullable']; + + if (!$isNullable) { + yield $columnName => PgsqlTypeMapper::FLAG_NOT_NULL; + } + if (str_contains($default, 'nextval')) { + yield $columnName => PgsqlTypeMapper::FLAG_AUTO_INCREMENT; + } } + } finally { + $this->stmt->closeCursor(); } } } From 8134552e0bfb69528f3f5f2a0dfdec24331e2f96 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 17 Feb 2023 15:50:18 +0100 Subject: [PATCH 2/2] Update SchemaHasherMysql.php --- src/DbSchema/SchemaHasherMysql.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/DbSchema/SchemaHasherMysql.php b/src/DbSchema/SchemaHasherMysql.php index 919915bac..a71728d05 100644 --- a/src/DbSchema/SchemaHasherMysql.php +++ b/src/DbSchema/SchemaHasherMysql.php @@ -37,9 +37,16 @@ public function hashDb(): string // for a schema with 3.000 columns we need roughly // 70.000 group concat max length $maxConcatQuery = 'SET SESSION group_concat_max_len = 1000000'; - $this->connection->query($maxConcatQuery); + $result = $this->connection->query($maxConcatQuery); + if ($result) { + if ($this->connection instanceof PDO) { + $result->close(); + } else { + $result->closeCursor(); + } + } - $query = ' + $query = ' SELECT MD5( GROUP_CONCAT( @@ -80,8 +87,8 @@ public function hashDb(): string if ($result instanceof \mysqli_result) { $row = $result->fetch_assoc(); $hash = $row['dbsignature'] ?? ''; + $result->close(); } - $result->close(); } finally { $this->connection->rollback(); }