From 8c57b0aad91e1fc0d3c705705865fdaac6e01ab7 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 9 May 2018 11:49:16 +0200 Subject: [PATCH] drivers divided into Driver and Reflector (BC break) --- src/Dibi/Drivers/FirebirdDriver.php | 354 +---------------------- src/Dibi/Drivers/FirebirdReflector.php | 377 +++++++++++++++++++++++++ src/Dibi/Drivers/MySqlReflector.php | 1 - src/Dibi/Drivers/OdbcDriver.php | 69 +---- src/Dibi/Drivers/OdbcReflector.php | 92 ++++++ src/Dibi/Drivers/OracleDriver.php | 66 +---- src/Dibi/Drivers/OracleReflector.php | 89 ++++++ src/Dibi/Drivers/PostgreDriver.php | 231 +-------------- src/Dibi/Drivers/PostgreReflector.php | 259 +++++++++++++++++ src/Dibi/Drivers/SqliteReflector.php | 1 - src/Dibi/Drivers/SqlsrvReflector.php | 1 - 11 files changed, 825 insertions(+), 715 deletions(-) create mode 100644 src/Dibi/Drivers/FirebirdReflector.php create mode 100644 src/Dibi/Drivers/OdbcReflector.php create mode 100644 src/Dibi/Drivers/OracleReflector.php create mode 100644 src/Dibi/Drivers/PostgreReflector.php diff --git a/src/Dibi/Drivers/FirebirdDriver.php b/src/Dibi/Drivers/FirebirdDriver.php index 74d08a7a..08150caa 100644 --- a/src/Dibi/Drivers/FirebirdDriver.php +++ b/src/Dibi/Drivers/FirebirdDriver.php @@ -24,7 +24,7 @@ use Dibi\Helpers; * - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default. * - resource (resource) => existing connection resource */ -class FirebirdDriver implements Dibi\Driver, Dibi\Reflector +class FirebirdDriver implements Dibi\Driver { use Dibi\Strict; @@ -211,7 +211,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\Reflector */ public function getReflector(): Dibi\Reflector { - return $this; + return new FirebirdReflector($this); } @@ -298,354 +298,4 @@ class FirebirdDriver implements Dibi\Driver, Dibi\Reflector $sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . $limit : '') . ($offset > 0 ? ' SKIP ' . $offset : '') . ' * FROM (' . $sql . ')'; } } - - - /********************* Dibi\Reflector ****************d*g**/ - - - /** - * Returns list of tables. - */ - public function getTables(): array - { - $res = $this->query(" - SELECT TRIM(RDB\$RELATION_NAME), - CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END - FROM RDB\$RELATIONS - WHERE RDB\$SYSTEM_FLAG = 0;" - ); - $tables = []; - while ($row = $res->fetch(false)) { - $tables[] = [ - 'name' => $row[0], - 'view' => $row[1] === 'TRUE', - ]; - } - return $tables; - } - - - /** - * Returns metadata for all columns in a table. - */ - public function getColumns(string $table): array - { - $table = strtoupper($table); - $res = $this->query(" - SELECT TRIM(r.RDB\$FIELD_NAME) AS FIELD_NAME, - CASE f.RDB\$FIELD_TYPE - WHEN 261 THEN 'BLOB' - WHEN 14 THEN 'CHAR' - WHEN 40 THEN 'CSTRING' - WHEN 11 THEN 'D_FLOAT' - WHEN 27 THEN 'DOUBLE' - WHEN 10 THEN 'FLOAT' - WHEN 16 THEN 'INT64' - WHEN 8 THEN 'INTEGER' - WHEN 9 THEN 'QUAD' - WHEN 7 THEN 'SMALLINT' - WHEN 12 THEN 'DATE' - WHEN 13 THEN 'TIME' - WHEN 35 THEN 'TIMESTAMP' - WHEN 37 THEN 'VARCHAR' - ELSE 'UNKNOWN' - END AS FIELD_TYPE, - f.RDB\$FIELD_LENGTH AS FIELD_LENGTH, - r.RDB\$DEFAULT_VALUE AS DEFAULT_VALUE, - CASE r.RDB\$NULL_FLAG - WHEN 1 THEN 'FALSE' ELSE 'TRUE' - END AS NULLABLE - FROM RDB\$RELATION_FIELDS r - LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME - WHERE r.RDB\$RELATION_NAME = '$table' - ORDER BY r.RDB\$FIELD_POSITION;" - - ); - $columns = []; - while ($row = $res->fetch(true)) { - $key = $row['FIELD_NAME']; - $columns[$key] = [ - 'name' => $key, - 'table' => $table, - 'nativetype' => trim($row['FIELD_TYPE']), - 'size' => $row['FIELD_LENGTH'], - 'nullable' => $row['NULLABLE'] === 'TRUE', - 'default' => $row['DEFAULT_VALUE'], - 'autoincrement' => false, - ]; - } - return $columns; - } - - - /** - * Returns metadata for all indexes in a table (the constraints are included). - */ - public function getIndexes(string $table): array - { - $table = strtoupper($table); - $res = $this->query(" - SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME, - TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME, - i.RDB\$UNIQUE_FLAG AS UNIQUE_FLAG, - i.RDB\$FOREIGN_KEY AS FOREIGN_KEY, - TRIM(r.RDB\$CONSTRAINT_TYPE) AS CONSTRAINT_TYPE, - s.RDB\$FIELD_POSITION AS FIELD_POSITION - FROM RDB\$INDEX_SEGMENTS s - LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME - LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME - WHERE UPPER(i.RDB\$RELATION_NAME) = '$table' - ORDER BY s.RDB\$FIELD_POSITION" - ); - $indexes = []; - while ($row = $res->fetch(true)) { - $key = $row['INDEX_NAME']; - $indexes[$key]['name'] = $key; - $indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1; - $indexes[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY'; - $indexes[$key]['table'] = $table; - $indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME']; - } - return $indexes; - } - - - /** - * Returns metadata for all foreign keys in a table. - */ - public function getForeignKeys(string $table): array - { - $table = strtoupper($table); - $res = $this->query(" - SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME, - TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME, - FROM RDB\$INDEX_SEGMENTS s - LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME - WHERE UPPER(i.RDB\$RELATION_NAME) = '$table' - AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY' - ORDER BY s.RDB\$FIELD_POSITION" - ); - $keys = []; - while ($row = $res->fetch(true)) { - $key = $row['INDEX_NAME']; - $keys[$key] = [ - 'name' => $key, - 'column' => $row['FIELD_NAME'], - 'table' => $table, - ]; - } - return $keys; - } - - - /** - * Returns list of indices in given table (the constraints are not listed). - */ - public function getIndices(string $table): array - { - $res = $this->query(" - SELECT TRIM(RDB\$INDEX_NAME) - FROM RDB\$INDICES - WHERE RDB\$RELATION_NAME = UPPER('$table') - AND RDB\$UNIQUE_FLAG IS NULL - AND RDB\$FOREIGN_KEY IS NULL;" - ); - $indices = []; - while ($row = $res->fetch(false)) { - $indices[] = $row[0]; - } - return $indices; - } - - - /** - * Returns list of constraints in given table. - */ - public function getConstraints(string $table): array - { - $res = $this->query(" - SELECT TRIM(RDB\$INDEX_NAME) - FROM RDB\$INDICES - WHERE RDB\$RELATION_NAME = UPPER('$table') - AND ( - RDB\$UNIQUE_FLAG IS NOT NULL - OR RDB\$FOREIGN_KEY IS NOT NULL - );" - ); - $constraints = []; - while ($row = $res->fetch(false)) { - $constraints[] = $row[0]; - } - return $constraints; - } - - - /** - * Returns metadata for all triggers in a table or database. - * (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table) - */ - public function getTriggersMeta(string $table = null): array - { - $res = $this->query(" - SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME, - TRIM(RDB\$RELATION_NAME) AS TABLE_NAME, - CASE RDB\$TRIGGER_TYPE - WHEN 1 THEN 'BEFORE' - WHEN 2 THEN 'AFTER' - WHEN 3 THEN 'BEFORE' - WHEN 4 THEN 'AFTER' - WHEN 5 THEN 'BEFORE' - WHEN 6 THEN 'AFTER' - END AS TRIGGER_TYPE, - CASE RDB\$TRIGGER_TYPE - WHEN 1 THEN 'INSERT' - WHEN 2 THEN 'INSERT' - WHEN 3 THEN 'UPDATE' - WHEN 4 THEN 'UPDATE' - WHEN 5 THEN 'DELETE' - WHEN 6 THEN 'DELETE' - END AS TRIGGER_EVENT, - CASE RDB\$TRIGGER_INACTIVE - WHEN 1 THEN 'FALSE' ELSE 'TRUE' - END AS TRIGGER_ENABLED - FROM RDB\$TRIGGERS - WHERE RDB\$SYSTEM_FLAG = 0" - . ($table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');") - ); - $triggers = []; - while ($row = $res->fetch(true)) { - $triggers[$row['TRIGGER_NAME']] = [ - 'name' => $row['TRIGGER_NAME'], - 'table' => $row['TABLE_NAME'], - 'type' => trim($row['TRIGGER_TYPE']), - 'event' => trim($row['TRIGGER_EVENT']), - 'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE', - ]; - } - return $triggers; - } - - - /** - * Returns list of triggers for given table. - * (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table) - */ - public function getTriggers(string $table = null): array - { - $q = 'SELECT TRIM(RDB$TRIGGER_NAME) - FROM RDB$TRIGGERS - WHERE RDB$SYSTEM_FLAG = 0'; - $q .= $table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')"; - - $res = $this->query($q); - $triggers = []; - while ($row = $res->fetch(false)) { - $triggers[] = $row[0]; - } - return $triggers; - } - - - /** - * Returns metadata from stored procedures and their input and output parameters. - */ - public function getProceduresMeta(): array - { - $res = $this->query(" - SELECT - TRIM(p.RDB\$PARAMETER_NAME) AS PARAMETER_NAME, - TRIM(p.RDB\$PROCEDURE_NAME) AS PROCEDURE_NAME, - CASE p.RDB\$PARAMETER_TYPE - WHEN 0 THEN 'INPUT' - WHEN 1 THEN 'OUTPUT' - ELSE 'UNKNOWN' - END AS PARAMETER_TYPE, - CASE f.RDB\$FIELD_TYPE - WHEN 261 THEN 'BLOB' - WHEN 14 THEN 'CHAR' - WHEN 40 THEN 'CSTRING' - WHEN 11 THEN 'D_FLOAT' - WHEN 27 THEN 'DOUBLE' - WHEN 10 THEN 'FLOAT' - WHEN 16 THEN 'INT64' - WHEN 8 THEN 'INTEGER' - WHEN 9 THEN 'QUAD' - WHEN 7 THEN 'SMALLINT' - WHEN 12 THEN 'DATE' - WHEN 13 THEN 'TIME' - WHEN 35 THEN 'TIMESTAMP' - WHEN 37 THEN 'VARCHAR' - ELSE 'UNKNOWN' - END AS FIELD_TYPE, - f.RDB\$FIELD_LENGTH AS FIELD_LENGTH, - p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER - FROM RDB\$PROCEDURE_PARAMETERS p - LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE - ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;" - ); - $procedures = []; - while ($row = $res->fetch(true)) { - $key = $row['PROCEDURE_NAME']; - $io = trim($row['PARAMETER_TYPE']); - $num = $row['PARAMETER_NUMBER']; - $procedures[$key]['name'] = $row['PROCEDURE_NAME']; - $procedures[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME']; - $procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']); - $procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH']; - } - return $procedures; - } - - - /** - * Returns list of stored procedures. - */ - public function getProcedures(): array - { - $res = $this->query(' - SELECT TRIM(RDB$PROCEDURE_NAME) - FROM RDB$PROCEDURES;' - ); - $procedures = []; - while ($row = $res->fetch(false)) { - $procedures[] = $row[0]; - } - return $procedures; - } - - - /** - * Returns list of generators. - */ - public function getGenerators(): array - { - $res = $this->query(' - SELECT TRIM(RDB$GENERATOR_NAME) - FROM RDB$GENERATORS - WHERE RDB$SYSTEM_FLAG = 0;' - ); - $generators = []; - while ($row = $res->fetch(false)) { - $generators[] = $row[0]; - } - return $generators; - } - - - /** - * Returns list of user defined functions (UDF). - */ - public function getFunctions(): array - { - $res = $this->query(' - SELECT TRIM(RDB$FUNCTION_NAME) - FROM RDB$FUNCTIONS - WHERE RDB$SYSTEM_FLAG = 0;' - ); - $functions = []; - while ($row = $res->fetch(false)) { - $functions[] = $row[0]; - } - return $functions; - } } diff --git a/src/Dibi/Drivers/FirebirdReflector.php b/src/Dibi/Drivers/FirebirdReflector.php new file mode 100644 index 00000000..277d8cf3 --- /dev/null +++ b/src/Dibi/Drivers/FirebirdReflector.php @@ -0,0 +1,377 @@ +driver = $driver; + } + + + /** + * Returns list of tables. + */ + public function getTables(): array + { + $res = $this->driver->query(" + SELECT TRIM(RDB\$RELATION_NAME), + CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END + FROM RDB\$RELATIONS + WHERE RDB\$SYSTEM_FLAG = 0;" + ); + $tables = []; + while ($row = $res->fetch(false)) { + $tables[] = [ + 'name' => $row[0], + 'view' => $row[1] === 'TRUE', + ]; + } + return $tables; + } + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns(string $table): array + { + $table = strtoupper($table); + $res = $this->driver->query(" + SELECT TRIM(r.RDB\$FIELD_NAME) AS FIELD_NAME, + CASE f.RDB\$FIELD_TYPE + WHEN 261 THEN 'BLOB' + WHEN 14 THEN 'CHAR' + WHEN 40 THEN 'CSTRING' + WHEN 11 THEN 'D_FLOAT' + WHEN 27 THEN 'DOUBLE' + WHEN 10 THEN 'FLOAT' + WHEN 16 THEN 'INT64' + WHEN 8 THEN 'INTEGER' + WHEN 9 THEN 'QUAD' + WHEN 7 THEN 'SMALLINT' + WHEN 12 THEN 'DATE' + WHEN 13 THEN 'TIME' + WHEN 35 THEN 'TIMESTAMP' + WHEN 37 THEN 'VARCHAR' + ELSE 'UNKNOWN' + END AS FIELD_TYPE, + f.RDB\$FIELD_LENGTH AS FIELD_LENGTH, + r.RDB\$DEFAULT_VALUE AS DEFAULT_VALUE, + CASE r.RDB\$NULL_FLAG + WHEN 1 THEN 'FALSE' ELSE 'TRUE' + END AS NULLABLE + FROM RDB\$RELATION_FIELDS r + LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME + WHERE r.RDB\$RELATION_NAME = '$table' + ORDER BY r.RDB\$FIELD_POSITION;" + + ); + $columns = []; + while ($row = $res->fetch(true)) { + $key = $row['FIELD_NAME']; + $columns[$key] = [ + 'name' => $key, + 'table' => $table, + 'nativetype' => trim($row['FIELD_TYPE']), + 'size' => $row['FIELD_LENGTH'], + 'nullable' => $row['NULLABLE'] === 'TRUE', + 'default' => $row['DEFAULT_VALUE'], + 'autoincrement' => false, + ]; + } + return $columns; + } + + + /** + * Returns metadata for all indexes in a table (the constraints are included). + */ + public function getIndexes(string $table): array + { + $table = strtoupper($table); + $res = $this->driver->query(" + SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME, + TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME, + i.RDB\$UNIQUE_FLAG AS UNIQUE_FLAG, + i.RDB\$FOREIGN_KEY AS FOREIGN_KEY, + TRIM(r.RDB\$CONSTRAINT_TYPE) AS CONSTRAINT_TYPE, + s.RDB\$FIELD_POSITION AS FIELD_POSITION + FROM RDB\$INDEX_SEGMENTS s + LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME + LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME + WHERE UPPER(i.RDB\$RELATION_NAME) = '$table' + ORDER BY s.RDB\$FIELD_POSITION" + ); + $indexes = []; + while ($row = $res->fetch(true)) { + $key = $row['INDEX_NAME']; + $indexes[$key]['name'] = $key; + $indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1; + $indexes[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY'; + $indexes[$key]['table'] = $table; + $indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME']; + } + return $indexes; + } + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys(string $table): array + { + $table = strtoupper($table); + $res = $this->driver->query(" + SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME, + TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME, + FROM RDB\$INDEX_SEGMENTS s + LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME + WHERE UPPER(i.RDB\$RELATION_NAME) = '$table' + AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY' + ORDER BY s.RDB\$FIELD_POSITION" + ); + $keys = []; + while ($row = $res->fetch(true)) { + $key = $row['INDEX_NAME']; + $keys[$key] = [ + 'name' => $key, + 'column' => $row['FIELD_NAME'], + 'table' => $table, + ]; + } + return $keys; + } + + + /** + * Returns list of indices in given table (the constraints are not listed). + */ + public function getIndices(string $table): array + { + $res = $this->driver->query(" + SELECT TRIM(RDB\$INDEX_NAME) + FROM RDB\$INDICES + WHERE RDB\$RELATION_NAME = UPPER('$table') + AND RDB\$UNIQUE_FLAG IS NULL + AND RDB\$FOREIGN_KEY IS NULL;" + ); + $indices = []; + while ($row = $res->fetch(false)) { + $indices[] = $row[0]; + } + return $indices; + } + + + /** + * Returns list of constraints in given table. + */ + public function getConstraints(string $table): array + { + $res = $this->driver->query(" + SELECT TRIM(RDB\$INDEX_NAME) + FROM RDB\$INDICES + WHERE RDB\$RELATION_NAME = UPPER('$table') + AND ( + RDB\$UNIQUE_FLAG IS NOT NULL + OR RDB\$FOREIGN_KEY IS NOT NULL + );" + ); + $constraints = []; + while ($row = $res->fetch(false)) { + $constraints[] = $row[0]; + } + return $constraints; + } + + + /** + * Returns metadata for all triggers in a table or database. + * (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table) + */ + public function getTriggersMeta(string $table = null): array + { + $res = $this->driver->query(" + SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME, + TRIM(RDB\$RELATION_NAME) AS TABLE_NAME, + CASE RDB\$TRIGGER_TYPE + WHEN 1 THEN 'BEFORE' + WHEN 2 THEN 'AFTER' + WHEN 3 THEN 'BEFORE' + WHEN 4 THEN 'AFTER' + WHEN 5 THEN 'BEFORE' + WHEN 6 THEN 'AFTER' + END AS TRIGGER_TYPE, + CASE RDB\$TRIGGER_TYPE + WHEN 1 THEN 'INSERT' + WHEN 2 THEN 'INSERT' + WHEN 3 THEN 'UPDATE' + WHEN 4 THEN 'UPDATE' + WHEN 5 THEN 'DELETE' + WHEN 6 THEN 'DELETE' + END AS TRIGGER_EVENT, + CASE RDB\$TRIGGER_INACTIVE + WHEN 1 THEN 'FALSE' ELSE 'TRUE' + END AS TRIGGER_ENABLED + FROM RDB\$TRIGGERS + WHERE RDB\$SYSTEM_FLAG = 0" + . ($table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');") + ); + $triggers = []; + while ($row = $res->fetch(true)) { + $triggers[$row['TRIGGER_NAME']] = [ + 'name' => $row['TRIGGER_NAME'], + 'table' => $row['TABLE_NAME'], + 'type' => trim($row['TRIGGER_TYPE']), + 'event' => trim($row['TRIGGER_EVENT']), + 'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE', + ]; + } + return $triggers; + } + + + /** + * Returns list of triggers for given table. + * (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table) + */ + public function getTriggers(string $table = null): array + { + $q = 'SELECT TRIM(RDB$TRIGGER_NAME) + FROM RDB$TRIGGERS + WHERE RDB$SYSTEM_FLAG = 0'; + $q .= $table === null ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')"; + + $res = $this->driver->query($q); + $triggers = []; + while ($row = $res->fetch(false)) { + $triggers[] = $row[0]; + } + return $triggers; + } + + + /** + * Returns metadata from stored procedures and their input and output parameters. + */ + public function getProceduresMeta(): array + { + $res = $this->driver->query(" + SELECT + TRIM(p.RDB\$PARAMETER_NAME) AS PARAMETER_NAME, + TRIM(p.RDB\$PROCEDURE_NAME) AS PROCEDURE_NAME, + CASE p.RDB\$PARAMETER_TYPE + WHEN 0 THEN 'INPUT' + WHEN 1 THEN 'OUTPUT' + ELSE 'UNKNOWN' + END AS PARAMETER_TYPE, + CASE f.RDB\$FIELD_TYPE + WHEN 261 THEN 'BLOB' + WHEN 14 THEN 'CHAR' + WHEN 40 THEN 'CSTRING' + WHEN 11 THEN 'D_FLOAT' + WHEN 27 THEN 'DOUBLE' + WHEN 10 THEN 'FLOAT' + WHEN 16 THEN 'INT64' + WHEN 8 THEN 'INTEGER' + WHEN 9 THEN 'QUAD' + WHEN 7 THEN 'SMALLINT' + WHEN 12 THEN 'DATE' + WHEN 13 THEN 'TIME' + WHEN 35 THEN 'TIMESTAMP' + WHEN 37 THEN 'VARCHAR' + ELSE 'UNKNOWN' + END AS FIELD_TYPE, + f.RDB\$FIELD_LENGTH AS FIELD_LENGTH, + p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER + FROM RDB\$PROCEDURE_PARAMETERS p + LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE + ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;" + ); + $procedures = []; + while ($row = $res->fetch(true)) { + $key = $row['PROCEDURE_NAME']; + $io = trim($row['PARAMETER_TYPE']); + $num = $row['PARAMETER_NUMBER']; + $procedures[$key]['name'] = $row['PROCEDURE_NAME']; + $procedures[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME']; + $procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']); + $procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH']; + } + return $procedures; + } + + + /** + * Returns list of stored procedures. + */ + public function getProcedures(): array + { + $res = $this->driver->query(' + SELECT TRIM(RDB$PROCEDURE_NAME) + FROM RDB$PROCEDURES;' + ); + $procedures = []; + while ($row = $res->fetch(false)) { + $procedures[] = $row[0]; + } + return $procedures; + } + + + /** + * Returns list of generators. + */ + public function getGenerators(): array + { + $res = $this->driver->query(' + SELECT TRIM(RDB$GENERATOR_NAME) + FROM RDB$GENERATORS + WHERE RDB$SYSTEM_FLAG = 0;' + ); + $generators = []; + while ($row = $res->fetch(false)) { + $generators[] = $row[0]; + } + return $generators; + } + + + /** + * Returns list of user defined functions (UDF). + */ + public function getFunctions(): array + { + $res = $this->driver->query(' + SELECT TRIM(RDB$FUNCTION_NAME) + FROM RDB$FUNCTIONS + WHERE RDB$SYSTEM_FLAG = 0;' + ); + $functions = []; + while ($row = $res->fetch(false)) { + $functions[] = $row[0]; + } + return $functions; + } +} diff --git a/src/Dibi/Drivers/MySqlReflector.php b/src/Dibi/Drivers/MySqlReflector.php index 7f82e1ce..1ec94173 100644 --- a/src/Dibi/Drivers/MySqlReflector.php +++ b/src/Dibi/Drivers/MySqlReflector.php @@ -14,7 +14,6 @@ use Dibi; /** * The dibi reflector for MySQL databases. - * @internal */ class MySqlReflector implements Dibi\Reflector { diff --git a/src/Dibi/Drivers/OdbcDriver.php b/src/Dibi/Drivers/OdbcDriver.php index 67eab197..3bc5c969 100644 --- a/src/Dibi/Drivers/OdbcDriver.php +++ b/src/Dibi/Drivers/OdbcDriver.php @@ -22,7 +22,7 @@ use Dibi; * - persistent (bool) => try to find a persistent link? * - resource (resource) => existing connection resource */ -class OdbcDriver implements Dibi\Driver, Dibi\Reflector +class OdbcDriver implements Dibi\Driver { use Dibi\Strict; @@ -182,7 +182,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\Reflector */ public function getReflector(): Dibi\Reflector { - return $this; + return new OdbcReflector($this); } @@ -275,69 +275,4 @@ class OdbcDriver implements Dibi\Driver, Dibi\Reflector $sql = 'SELECT TOP ' . $limit . ' * FROM (' . $sql . ') t'; } } - - - /********************* Dibi\Reflector ****************d*g**/ - - - /** - * Returns list of tables. - */ - public function getTables(): array - { - $res = odbc_tables($this->connection); - $tables = []; - while ($row = odbc_fetch_array($res)) { - if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') { - $tables[] = [ - 'name' => $row['TABLE_NAME'], - 'view' => $row['TABLE_TYPE'] === 'VIEW', - ]; - } - } - odbc_free_result($res); - return $tables; - } - - - /** - * Returns metadata for all columns in a table. - */ - public function getColumns(string $table): array - { - $res = odbc_columns($this->connection); - $columns = []; - while ($row = odbc_fetch_array($res)) { - if ($row['TABLE_NAME'] === $table) { - $columns[] = [ - 'name' => $row['COLUMN_NAME'], - 'table' => $table, - 'nativetype' => $row['TYPE_NAME'], - 'size' => $row['COLUMN_SIZE'], - 'nullable' => (bool) $row['NULLABLE'], - 'default' => $row['COLUMN_DEF'], - ]; - } - } - odbc_free_result($res); - return $columns; - } - - - /** - * Returns metadata for all indexes in a table. - */ - public function getIndexes(string $table): array - { - throw new Dibi\NotImplementedException; - } - - - /** - * Returns metadata for all foreign keys in a table. - */ - public function getForeignKeys(string $table): array - { - throw new Dibi\NotImplementedException; - } } diff --git a/src/Dibi/Drivers/OdbcReflector.php b/src/Dibi/Drivers/OdbcReflector.php new file mode 100644 index 00000000..07cd79e7 --- /dev/null +++ b/src/Dibi/Drivers/OdbcReflector.php @@ -0,0 +1,92 @@ +driver = $driver; + } + + + /** + * Returns list of tables. + */ + public function getTables(): array + { + $res = odbc_tables($this->driver->getResource()); + $tables = []; + while ($row = odbc_fetch_array($res)) { + if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') { + $tables[] = [ + 'name' => $row['TABLE_NAME'], + 'view' => $row['TABLE_TYPE'] === 'VIEW', + ]; + } + } + odbc_free_result($res); + return $tables; + } + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns(string $table): array + { + $res = odbc_columns($this->driver->getResource()); + $columns = []; + while ($row = odbc_fetch_array($res)) { + if ($row['TABLE_NAME'] === $table) { + $columns[] = [ + 'name' => $row['COLUMN_NAME'], + 'table' => $table, + 'nativetype' => $row['TYPE_NAME'], + 'size' => $row['COLUMN_SIZE'], + 'nullable' => (bool) $row['NULLABLE'], + 'default' => $row['COLUMN_DEF'], + ]; + } + } + odbc_free_result($res); + return $columns; + } + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes(string $table): array + { + throw new Dibi\NotImplementedException; + } + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys(string $table): array + { + throw new Dibi\NotImplementedException; + } +} diff --git a/src/Dibi/Drivers/OracleDriver.php b/src/Dibi/Drivers/OracleDriver.php index 0c827ce2..42de5bbd 100644 --- a/src/Dibi/Drivers/OracleDriver.php +++ b/src/Dibi/Drivers/OracleDriver.php @@ -25,7 +25,7 @@ use Dibi; * - resource (resource) => existing connection resource * - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect */ -class OracleDriver implements Dibi\Driver, Dibi\Reflector +class OracleDriver implements Dibi\Driver { use Dibi\Strict; @@ -208,7 +208,7 @@ class OracleDriver implements Dibi\Driver, Dibi\Reflector */ public function getReflector(): Dibi\Reflector { - return $this; + return new OracleReflector($this); } @@ -310,66 +310,4 @@ class OracleDriver implements Dibi\Driver, Dibi\Reflector $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . $limit; } } - - - /********************* Dibi\Reflector ****************d*g**/ - - - /** - * Returns list of tables. - */ - public function getTables(): array - { - $res = $this->query('SELECT * FROM cat'); - $tables = []; - while ($row = $res->fetch(false)) { - if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { - $tables[] = [ - 'name' => $row[0], - 'view' => $row[1] === 'VIEW', - ]; - } - } - return $tables; - } - - - /** - * Returns metadata for all columns in a table. - */ - public function getColumns(string $table): array - { - $res = $this->query('SELECT * FROM "ALL_TAB_COLUMNS" WHERE "TABLE_NAME" = ' . $this->escapeText($table)); - $columns = []; - while ($row = $res->fetch(true)) { - $columns[] = [ - 'table' => $row['TABLE_NAME'], - 'name' => $row['COLUMN_NAME'], - 'nativetype' => $row['DATA_TYPE'], - 'size' => $row['DATA_LENGTH'] ?? null, - 'nullable' => $row['NULLABLE'] === 'Y', - 'default' => $row['DATA_DEFAULT'], - 'vendor' => $row, - ]; - } - return $columns; - } - - - /** - * Returns metadata for all indexes in a table. - */ - public function getIndexes(string $table): array - { - throw new Dibi\NotImplementedException; - } - - - /** - * Returns metadata for all foreign keys in a table. - */ - public function getForeignKeys(string $table): array - { - throw new Dibi\NotImplementedException; - } } diff --git a/src/Dibi/Drivers/OracleReflector.php b/src/Dibi/Drivers/OracleReflector.php new file mode 100644 index 00000000..96b3b240 --- /dev/null +++ b/src/Dibi/Drivers/OracleReflector.php @@ -0,0 +1,89 @@ +driver = $driver; + } + + + /** + * Returns list of tables. + */ + public function getTables(): array + { + $res = $this->driver->query('SELECT * FROM cat'); + $tables = []; + while ($row = $res->fetch(false)) { + if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { + $tables[] = [ + 'name' => $row[0], + 'view' => $row[1] === 'VIEW', + ]; + } + } + return $tables; + } + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns(string $table): array + { + $res = $this->driver->query('SELECT * FROM "ALL_TAB_COLUMNS" WHERE "TABLE_NAME" = ' . $this->driver->escapeText($table)); + $columns = []; + while ($row = $res->fetch(true)) { + $columns[] = [ + 'table' => $row['TABLE_NAME'], + 'name' => $row['COLUMN_NAME'], + 'nativetype' => $row['DATA_TYPE'], + 'size' => $row['DATA_LENGTH'] ?? null, + 'nullable' => $row['NULLABLE'] === 'Y', + 'default' => $row['DATA_DEFAULT'], + 'vendor' => $row, + ]; + } + return $columns; + } + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes(string $table): array + { + throw new Dibi\NotImplementedException; + } + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys(string $table): array + { + throw new Dibi\NotImplementedException; + } +} diff --git a/src/Dibi/Drivers/PostgreDriver.php b/src/Dibi/Drivers/PostgreDriver.php index 2b9aa8c2..93e3734b 100644 --- a/src/Dibi/Drivers/PostgreDriver.php +++ b/src/Dibi/Drivers/PostgreDriver.php @@ -24,7 +24,7 @@ use Dibi\Helpers; * - persistent (bool) => try to find a persistent link? * - resource (resource) => existing connection resource */ -class PostgreDriver implements Dibi\Driver, Dibi\Reflector +class PostgreDriver implements Dibi\Driver { use Dibi\Strict; @@ -249,7 +249,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\Reflector */ public function getReflector(): Dibi\Reflector { - return $this; + return new PostgreReflector($this, pg_parameter_status($this->connection, 'server_version')); } @@ -351,231 +351,4 @@ class PostgreDriver implements Dibi\Driver, Dibi\Reflector $sql .= ' OFFSET ' . $offset; } } - - - /********************* Dibi\Reflector ****************d*g**/ - - - /** - * Returns list of tables. - */ - public function getTables(): array - { - $version = pg_parameter_status($this->getResource(), 'server_version'); - if ($version < 7.4) { - throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.'); - } - - $query = " - SELECT - table_name AS name, - CASE table_type - WHEN 'VIEW' THEN 1 - ELSE 0 - END AS view - FROM - information_schema.tables - WHERE - table_schema = ANY (current_schemas(false))"; - - if ($version >= 9.3) { - $query .= ' - UNION ALL - SELECT - matviewname, 1 - FROM - pg_matviews - WHERE - schemaname = ANY (current_schemas(false))'; - } - - $res = $this->query($query); - $tables = pg_fetch_all($res->getResultResource()); - return $tables ? $tables : []; - } - - - /** - * Returns metadata for all columns in a table. - */ - public function getColumns(string $table): array - { - $_table = $this->escapeText($this->escapeIdentifier($table)); - $res = $this->query(" - SELECT indkey - FROM pg_class - LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary - WHERE pg_class.oid = $_table::regclass - "); - $primary = (int) pg_fetch_object($res->getResultResource())->indkey; - - $res = $this->query(" - SELECT * - FROM information_schema.columns c - JOIN pg_class ON pg_class.relname = c.table_name - JOIN pg_namespace nsp ON nsp.oid = pg_class.relnamespace AND nsp.nspname = c.table_schema - WHERE pg_class.oid = $_table::regclass - ORDER BY c.ordinal_position - "); - - if (!$res->getRowCount()) { - $res = $this->query(" - SELECT - a.attname AS column_name, - pg_type.typname AS udt_name, - a.attlen AS numeric_precision, - a.atttypmod-4 AS character_maximum_length, - NOT a.attnotnull AS is_nullable, - a.attnum AS ordinal_position, - adef.adsrc AS column_default - FROM - pg_attribute a - JOIN pg_type ON a.atttypid = pg_type.oid - JOIN pg_class cls ON a.attrelid = cls.oid - LEFT JOIN pg_attrdef adef ON adef.adnum = a.attnum AND adef.adrelid = a.attrelid - WHERE - cls.relkind IN ('r', 'v', 'mv') - AND a.attrelid = $_table::regclass - AND a.attnum > 0 - AND NOT a.attisdropped - ORDER BY ordinal_position - "); - } - - $columns = []; - while ($row = $res->fetch(true)) { - $size = (int) max($row['character_maximum_length'], $row['numeric_precision']); - $columns[] = [ - 'name' => $row['column_name'], - 'table' => $table, - 'nativetype' => strtoupper($row['udt_name']), - 'size' => $size > 0 ? $size : null, - 'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't', - 'default' => $row['column_default'], - 'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval', - 'vendor' => $row, - ]; - } - return $columns; - } - - - /** - * Returns metadata for all indexes in a table. - */ - public function getIndexes(string $table): array - { - $_table = $this->escapeText($this->escapeIdentifier($table)); - $res = $this->query(" - SELECT - a.attnum AS ordinal_position, - a.attname AS column_name - FROM - pg_attribute a - JOIN pg_class cls ON a.attrelid = cls.oid - WHERE - a.attrelid = $_table::regclass - AND a.attnum > 0 - AND NOT a.attisdropped - ORDER BY ordinal_position - "); - - $columns = []; - while ($row = $res->fetch(true)) { - $columns[$row['ordinal_position']] = $row['column_name']; - } - - $res = $this->query(" - SELECT pg_class2.relname, indisunique, indisprimary, indkey - FROM pg_class - LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid - INNER JOIN pg_class as pg_class2 on pg_class2.oid = pg_index.indexrelid - WHERE pg_class.oid = $_table::regclass - "); - - $indexes = []; - while ($row = $res->fetch(true)) { - $indexes[$row['relname']]['name'] = $row['relname']; - $indexes[$row['relname']]['unique'] = $row['indisunique'] === 't'; - $indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't'; - foreach (explode(' ', $row['indkey']) as $index) { - $indexes[$row['relname']]['columns'][] = $columns[$index]; - } - } - return array_values($indexes); - } - - - /** - * Returns metadata for all foreign keys in a table. - */ - public function getForeignKeys(string $table): array - { - $_table = $this->escapeText($this->escapeIdentifier($table)); - - $res = $this->query(" - SELECT - c.conname AS name, - lt.attname AS local, - c.confrelid::regclass AS table, - ft.attname AS foreign, - - CASE c.confupdtype - WHEN 'a' THEN 'NO ACTION' - WHEN 'r' THEN 'RESTRICT' - WHEN 'c' THEN 'CASCADE' - WHEN 'n' THEN 'SET NULL' - WHEN 'd' THEN 'SET DEFAULT' - ELSE 'UNKNOWN' - END AS \"onUpdate\", - - CASE c.confdeltype - WHEN 'a' THEN 'NO ACTION' - WHEN 'r' THEN 'RESTRICT' - WHEN 'c' THEN 'CASCADE' - WHEN 'n' THEN 'SET NULL' - WHEN 'd' THEN 'SET DEFAULT' - ELSE 'UNKNOWN' - END AS \"onDelete\", - - c.conkey, - lt.attnum AS lnum, - c.confkey, - ft.attnum AS fnum - FROM - pg_constraint c - JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey) - JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey) - WHERE - c.contype = 'f' - AND - c.conrelid = $_table::regclass - "); - - $fKeys = $references = []; - while ($row = $res->fetch(true)) { - if (!isset($fKeys[$row['name']])) { - $fKeys[$row['name']] = [ - 'name' => $row['name'], - 'table' => $row['table'], - 'local' => [], - 'foreign' => [], - 'onUpdate' => $row['onUpdate'], - 'onDelete' => $row['onDelete'], - ]; - - $l = explode(',', trim($row['conkey'], '{}')); - $f = explode(',', trim($row['confkey'], '{}')); - - $references[$row['name']] = array_combine($l, $f); - } - - if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) { - $fKeys[$row['name']]['local'][] = $row['local']; - $fKeys[$row['name']]['foreign'][] = $row['foreign']; - } - } - - return $fKeys; - } } diff --git a/src/Dibi/Drivers/PostgreReflector.php b/src/Dibi/Drivers/PostgreReflector.php new file mode 100644 index 00000000..a6bcb7ea --- /dev/null +++ b/src/Dibi/Drivers/PostgreReflector.php @@ -0,0 +1,259 @@ +driver = $driver; + $this->version = $version; + } + + + /** + * Returns list of tables. + */ + public function getTables(): array + { + $query = " + SELECT + table_name AS name, + CASE table_type + WHEN 'VIEW' THEN 1 + ELSE 0 + END AS view + FROM + information_schema.tables + WHERE + table_schema = ANY (current_schemas(false))"; + + if ($this->version >= 9.3) { + $query .= ' + UNION ALL + SELECT + matviewname, 1 + FROM + pg_matviews + WHERE + schemaname = ANY (current_schemas(false))'; + } + + $res = $this->driver->query($query); + $tables = []; + while ($row = $res->fetch(true)) { + $tables[] = $row; + } + return $tables; + } + + + /** + * Returns metadata for all columns in a table. + */ + public function getColumns(string $table): array + { + $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table)); + $res = $this->driver->query(" + SELECT indkey + FROM pg_class + LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary + WHERE pg_class.oid = $_table::regclass + "); + $primary = (int) $res->fetch(true)['indkey']; + + $res = $this->driver->query(" + SELECT * + FROM information_schema.columns c + JOIN pg_class ON pg_class.relname = c.table_name + JOIN pg_namespace nsp ON nsp.oid = pg_class.relnamespace AND nsp.nspname = c.table_schema + WHERE pg_class.oid = $_table::regclass + ORDER BY c.ordinal_position + "); + + if (!$res->getRowCount()) { + $res = $this->driver->query(" + SELECT + a.attname AS column_name, + pg_type.typname AS udt_name, + a.attlen AS numeric_precision, + a.atttypmod-4 AS character_maximum_length, + NOT a.attnotnull AS is_nullable, + a.attnum AS ordinal_position, + adef.adsrc AS column_default + FROM + pg_attribute a + JOIN pg_type ON a.atttypid = pg_type.oid + JOIN pg_class cls ON a.attrelid = cls.oid + LEFT JOIN pg_attrdef adef ON adef.adnum = a.attnum AND adef.adrelid = a.attrelid + WHERE + cls.relkind IN ('r', 'v', 'mv') + AND a.attrelid = $_table::regclass + AND a.attnum > 0 + AND NOT a.attisdropped + ORDER BY ordinal_position + "); + } + + $columns = []; + while ($row = $res->fetch(true)) { + $size = (int) max($row['character_maximum_length'], $row['numeric_precision']); + $columns[] = [ + 'name' => $row['column_name'], + 'table' => $table, + 'nativetype' => strtoupper($row['udt_name']), + 'size' => $size > 0 ? $size : null, + 'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't' || $row['is_nullable'] === true, + 'default' => $row['column_default'], + 'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval', + 'vendor' => $row, + ]; + } + return $columns; + } + + + /** + * Returns metadata for all indexes in a table. + */ + public function getIndexes(string $table): array + { + $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table)); + $res = $this->driver->query(" + SELECT + a.attnum AS ordinal_position, + a.attname AS column_name + FROM + pg_attribute a + JOIN pg_class cls ON a.attrelid = cls.oid + WHERE + a.attrelid = $_table::regclass + AND a.attnum > 0 + AND NOT a.attisdropped + ORDER BY ordinal_position + "); + + $columns = []; + while ($row = $res->fetch(true)) { + $columns[$row['ordinal_position']] = $row['column_name']; + } + + $res = $this->driver->query(" + SELECT pg_class2.relname, indisunique, indisprimary, indkey + FROM pg_class + LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid + INNER JOIN pg_class as pg_class2 on pg_class2.oid = pg_index.indexrelid + WHERE pg_class.oid = $_table::regclass + "); + + $indexes = []; + while ($row = $res->fetch(true)) { + $indexes[$row['relname']]['name'] = $row['relname']; + $indexes[$row['relname']]['unique'] = $row['indisunique'] === 't' || $row['indisunique'] === true; + $indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't' || $row['indisprimary'] === true; + foreach (explode(' ', $row['indkey']) as $index) { + $indexes[$row['relname']]['columns'][] = $columns[$index]; + } + } + return array_values($indexes); + } + + + /** + * Returns metadata for all foreign keys in a table. + */ + public function getForeignKeys(string $table): array + { + $_table = $this->driver->escapeText($this->driver->escapeIdentifier($table)); + + $res = $this->driver->query(" + SELECT + c.conname AS name, + lt.attname AS local, + c.confrelid::regclass AS table, + ft.attname AS foreign, + + CASE c.confupdtype + WHEN 'a' THEN 'NO ACTION' + WHEN 'r' THEN 'RESTRICT' + WHEN 'c' THEN 'CASCADE' + WHEN 'n' THEN 'SET NULL' + WHEN 'd' THEN 'SET DEFAULT' + ELSE 'UNKNOWN' + END AS \"onUpdate\", + + CASE c.confdeltype + WHEN 'a' THEN 'NO ACTION' + WHEN 'r' THEN 'RESTRICT' + WHEN 'c' THEN 'CASCADE' + WHEN 'n' THEN 'SET NULL' + WHEN 'd' THEN 'SET DEFAULT' + ELSE 'UNKNOWN' + END AS \"onDelete\", + + c.conkey, + lt.attnum AS lnum, + c.confkey, + ft.attnum AS fnum + FROM + pg_constraint c + JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey) + JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey) + WHERE + c.contype = 'f' + AND + c.conrelid = $_table::regclass + "); + + $fKeys = $references = []; + while ($row = $res->fetch(true)) { + if (!isset($fKeys[$row['name']])) { + $fKeys[$row['name']] = [ + 'name' => $row['name'], + 'table' => $row['table'], + 'local' => [], + 'foreign' => [], + 'onUpdate' => $row['onUpdate'], + 'onDelete' => $row['onDelete'], + ]; + + $l = explode(',', trim($row['conkey'], '{}')); + $f = explode(',', trim($row['confkey'], '{}')); + + $references[$row['name']] = array_combine($l, $f); + } + + if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) { + $fKeys[$row['name']]['local'][] = $row['local']; + $fKeys[$row['name']]['foreign'][] = $row['foreign']; + } + } + + return $fKeys; + } +} diff --git a/src/Dibi/Drivers/SqliteReflector.php b/src/Dibi/Drivers/SqliteReflector.php index c8d2bda3..b990f7c6 100644 --- a/src/Dibi/Drivers/SqliteReflector.php +++ b/src/Dibi/Drivers/SqliteReflector.php @@ -14,7 +14,6 @@ use Dibi; /** * The dibi reflector for SQLite database. - * @internal */ class SqliteReflector implements Dibi\Reflector { diff --git a/src/Dibi/Drivers/SqlsrvReflector.php b/src/Dibi/Drivers/SqlsrvReflector.php index 381e4c10..57af22d9 100644 --- a/src/Dibi/Drivers/SqlsrvReflector.php +++ b/src/Dibi/Drivers/SqlsrvReflector.php @@ -14,7 +14,6 @@ use Dibi; /** * The dibi reflector for Microsoft SQL Server and SQL Azure databases. - * @internal */ class SqlsrvReflector implements Dibi\Reflector {