├── .gitignore
├── tests
├── expected
│ ├── xml
│ │ └── transactions.xml
│ ├── mysql
│ │ ├── strings.json
│ │ ├── view_description.php
│ │ ├── database_description.php
│ │ └── database_description_clean_defaults.php
│ ├── sqlite
│ │ ├── strings.json
│ │ ├── view_description.php
│ │ ├── database_description.php
│ │ └── database_description_clean_defaults.php
│ └── postgresql
│ │ ├── strings.json
│ │ ├── employees_description.php
│ │ ├── view_description.php
│ │ └── database_description_clean_defaults.php
├── config
│ ├── github.xml
│ └── sample.xml
├── databases
│ ├── sqlite.sql
│ ├── mysql.sql
│ └── postgresql.sql
├── lib
│ └── DriverLoader.php
└── cases
│ └── DriverTest.php
├── composer.json
├── CHANGELOG.md
├── src
├── drivers
│ ├── SqliteDriver.php
│ ├── PostgresqlDriver.php
│ └── MysqlDriver.php
├── DriverFactory.php
├── exceptions
│ ├── DatabaseDriverException.php
│ ├── ConnectionException.php
│ └── TableNotFoundException.php
├── NullConnection.php
├── descriptors
│ ├── MysqlDescriptor.php
│ ├── InformationSchemaDescriptor.php
│ ├── PostgresqlDescriptor.php
│ └── SqliteDescriptor.php
├── DbContext.php
├── Driver.php
└── Descriptor.php
├── LICENSE
├── .scrutinizer.yml
├── .github
└── workflows
│ └── tests.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | nbproject
2 | vendor
3 | tests/config/*.xml
4 |
--------------------------------------------------------------------------------
/tests/expected/xml/transactions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/expected/mysql/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "quoted_string": "'string'",
3 | "quoted_identifier" : "`identifier`",
4 | "quoted_query_identifiers" : "SELECT `some`, `identifiers` FROM `some`.`table`"
5 | }
6 |
--------------------------------------------------------------------------------
/tests/expected/sqlite/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "quoted_string": "'string'",
3 | "quoted_identifier" : "\"identifier\"",
4 | "quoted_query_identifiers" : "SELECT \"some\", \"identifiers\" FROM \"some\".\"table\""
5 | }
6 |
--------------------------------------------------------------------------------
/tests/expected/postgresql/strings.json:
--------------------------------------------------------------------------------
1 | {
2 | "quoted_string": "'string'",
3 | "quoted_identifier" : "\"identifier\"",
4 | "quoted_query_identifiers" : "SELECT \"some\", \"identifiers\" FROM \"some\".\"table\""
5 | }
6 |
--------------------------------------------------------------------------------
/tests/config/github.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | ../cases
9 |
10 |
11 |
12 |
13 | ../../src
14 |
15 |
16 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ntentan/atiaa",
3 | "description": "A slim wrapper around PDO to provide some extra utilities",
4 | "license": "MIT",
5 | "authors": [
6 | {
7 | "name": "James Ekow Abaka Ainooson",
8 | "email": "jainooson@gmail.com"
9 | }
10 | ],
11 | "require": {
12 | "ntentan/utils": "0.*",
13 | "ext-pdo": "*"
14 | },
15 | "require-dev" : {
16 | "phpunit/phpunit" : "11.*",
17 | "ext-json": "*"
18 | },
19 | "autoload" : {
20 | "psr-4" : {
21 | "ntentan\\atiaa\\" : "src/",
22 | "ntentan\\atiaa\\tests\\" : "tests/"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | v0.9.1 - 2025-09-07
2 | ===================
3 | - Fixes a bug to ensure connections are only initiated when needed.
4 |
5 | v0.9.0 - 2025-04-05
6 | ===================
7 | - Added a new way to access the driver for transaction management through the database context.
8 | - Cleaned up the documentation and type hints.
9 |
10 | v0.8.0 - 2025-01-20
11 | ===================
12 | Added
13 | -----
14 | - Can now pass PDO attributes to the connection.
15 | - Some type hints to bring the library in line with modern PHP features.
16 | - Migrated tests from TravisCI to Github workflow.
17 |
18 | v0.7.2 - 2018-12-02
19 | ===================
20 | - First release to have a CHANGELOG
21 |
22 | Removed
23 | -------
24 | - Boolean type is not defined in a standard way across database drivers, and it is being temporarily disabled.
25 |
--------------------------------------------------------------------------------
/src/drivers/SqliteDriver.php:
--------------------------------------------------------------------------------
1 | defaultSchema = 'main';
14 | parent::__construct($config);
15 | }
16 |
17 | #[\Override]
18 | public function connect(): void
19 | {
20 | parent::connect();
21 | if (!$this->isConnected()) {
22 | // Connection is still in progress and an Exception has not been thrown.
23 | $this->query('PRAGMA foreign_keys=ON');
24 | }
25 | }
26 |
27 | protected function getDriverName()
28 | {
29 | return 'sqlite';
30 | }
31 |
32 | public function quoteIdentifier($identifier)
33 | {
34 | return "\"$identifier\"";
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/drivers/PostgresqlDriver.php:
--------------------------------------------------------------------------------
1 | query('SELECT LASTVAL() as last');
27 | return $lastval[0]['last'];
28 | }
29 |
30 | #[\Override]
31 | public function connect(): void
32 | {
33 | parent::connect();
34 | if (isset($this->config['schema']) && !$this->isConnected()) {
35 | $this->query("SET search_path TO {$this->config['schema']}");
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/config/sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ../cases
18 |
19 |
20 |
21 |
22 | ../../src
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/DriverFactory.php:
--------------------------------------------------------------------------------
1 | config = $dbConfig;
17 | }
18 |
19 | /**
20 | * Set or replace the configuration found in the factory.
21 | */
22 | public function setConfig(array $config): void
23 | {
24 | $this->config = $config;
25 | }
26 |
27 | /**
28 | * Return the configuration currently stored in the factory.
29 | */
30 | public function getConfig(): array
31 | {
32 | return $this->config;
33 | }
34 |
35 | /**
36 | * Create a new driver based on the configuration in the factory.
37 | */
38 | public function createDriver(): Driver
39 | {
40 | $classname = '\ntentan\atiaa\drivers\\'.Text::ucamelize($this->config['driver']).'Driver';
41 | return new $classname($this->config);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 James Ekow Abaka Ainooson
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/tests/databases/sqlite.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `departments` (`id` INTEGER CONSTRAINT `department_id_pk` PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL);
2 | CREATE TABLE `roles` (`id` INTEGER CONSTRAINT `role_id_pk` PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL);
3 | CREATE TABLE `users` (
4 | `id` INTEGER NOT NULL CONSTRAINT `user_id_pk` PRIMARY KEY AUTOINCREMENT,
5 | `username` TEXT(255) NOT NULL,
6 | `password` TEXT NOT NULL,
7 | `role_id` INTEGER NOT NULL CONSTRAINT `role_id_fk` REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
8 | `firstname` TEXT NOT NULL,
9 | `lastname` TEXT NOT NULL,
10 | `othernames` TEXT DEFAULT 'None',
11 | `status` INTEGER DEFAULT '2',
12 | `email` TEXT,
13 | `phone` TEXT,
14 | `office` INTEGER NOT NULL CONSTRAINT `office_fk` REFERENCES `departments` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
15 | `last_login_time` TEXT,
16 | `is_admin` INTEGER,
17 | CONSTRAINT `username_uk` UNIQUE (username)
18 | );
19 |
20 | CREATE INDEX user_email_idx ON users(email);
21 |
22 | CREATE VIEW `users_view` AS SELECT
23 | `users`.`id`, `username`, `password`, `firstname`, `lastname`, `othernames`,
24 | `email`, `roles`.`name` as `role`
25 | FROM `users` JOIN `roles` ON `role_id` = `roles`.`id`;
26 |
--------------------------------------------------------------------------------
/src/exceptions/DatabaseDriverException.php:
--------------------------------------------------------------------------------
1 | defaultSchema) {
47 | return $this->config['dbname'];
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/lib/DriverLoader.php:
--------------------------------------------------------------------------------
1 | getenv('ATIAA_DRIVER'),
40 | 'host' => getenv('ATIAA_HOST'),
41 | 'user' => getenv('ATIAA_USER'),
42 | 'password' => getenv('ATIAA_PASSWORD'),
43 | 'file' => getenv('ATIAA_FILE'),
44 | 'dbname' => getenv('ATIAA_DBNAME'),
45 | 'options' => [\PDO::ATTR_TIMEOUT => 5]
46 | ]);
47 | $driver = $factory->createDriver();
48 | $driver->connect();
49 |
50 | return $driver;
51 | }
52 |
53 | public function getDriverName()
54 | {
55 | return getenv('ATIAA_DRIVER');
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/expected/postgresql/employees_description.php:
--------------------------------------------------------------------------------
1 | [
29 | 'schema' => 'hr',
30 | 'name' => 'employees',
31 | 'columns' => [
32 | 'date_of_birth' => [
33 | 'name' => 'date_of_birth',
34 | 'type' => 'date',
35 | 'nulls' => true,
36 | 'default' => null,
37 | 'length' => null,
38 | ],
39 | 'firstname' => [
40 | 'name' => 'firstname',
41 | 'type' => 'character varying',
42 | 'nulls' => false,
43 | 'default' => null,
44 | 'length' => 255,
45 | ],
46 | 'id' => [
47 | 'name' => 'id',
48 | 'type' => 'integer',
49 | 'nulls' => false,
50 | 'default' => null,
51 | 'length' => null,
52 | ],
53 | 'lastname' => [
54 | 'name' => 'lastname',
55 | 'type' => 'character varying',
56 | 'nulls' => true,
57 | 'default' => null,
58 | 'length' => 255,
59 | ],
60 | ],
61 | 'primary_key' => [
62 | 'employees_pkey' => [
63 | 'columns' => [
64 | 0 => 'id',
65 | ],
66 | ],
67 | ],
68 | 'unique_keys' => [
69 | ],
70 | 'foreign_keys' => [
71 | ],
72 | 'indices' => [
73 | ],
74 | 'auto_increment' => true,
75 | ],
76 | ];
77 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push]
3 | jobs:
4 | Integration:
5 | runs-on: ubuntu-latest
6 | services:
7 | server:
8 | image: ${{ matrix.db.services.image }}
9 | env: ${{ matrix.db.services.env }}
10 | ports:
11 | - ${{ matrix.db.services.ports }}
12 | strategy:
13 | matrix:
14 | db:
15 | - env:
16 | ATIAA_DRIVER: sqlite
17 | ATIAA_FILE: sqlite.db
18 | ATIAA_PDO_DSN: sqlite:sqlite.db
19 | ATIAA_SKIP_DB: yes
20 | initialize: sqlite3 sqlite.db < tests/databases/sqlite.sql
21 | services:
22 | image: 'hello-world'
23 | env:
24 | GREET: Hello
25 | ports: '80:80'
26 |
27 | - env:
28 | ATIAA_DRIVER: mysql
29 | ATIAA_HOST: 127.0.0.1
30 | ATIAA_USER: root
31 | ATIAA_DBNAME: atiaa_test
32 | ATIAA_PDO_DSN: mysql:host=127.0.0.1;dbname=atiaa_test;user=root
33 | initialize: |
34 | sleep 10
35 | echo "create database atiaa_test;" | mysql -h 127.0.0.1 --user root
36 | mysql -h 127.0.0.1 --user root atiaa_test < tests/databases/mysql.sql
37 | services:
38 | image: mysql:latest
39 | env:
40 | MYSQL_ALLOW_EMPTY_PASSWORD: yes
41 | ports: '3306:3306'
42 |
43 | - env:
44 | ATIAA_DRIVER: postgresql
45 | ATIAA_HOST: 127.0.0.1
46 | ATIAA_USER: postgres
47 | ATIAA_DBNAME: atiaa_test
48 | ATIAA_PDO_DSN: pgsql:host=127.0.0.1;dbname=atiaa_test;user=postgres
49 | initialize: |
50 | sleep 10
51 | psql -c 'create database atiaa_test;' --host 127.0.0.1 --user postgres
52 | psql -f tests/databases/postgresql.sql -U postgres -d atiaa_test --host 127.0.0.1
53 | services:
54 | image: postgres:latest
55 | env:
56 | POSTGRES_HOST_AUTH_METHOD: trust
57 | ports: '5432:5432'
58 | steps:
59 | - name: Check out repository code
60 | uses: actions/checkout@v2
61 | with:
62 | fetch-depth: 2
63 | - name: Install PHP
64 | uses: shivammathur/setup-php@v2
65 | with:
66 | tools: composer
67 | - name: Running composer
68 | run: composer install
69 | - name: Initializing the database
70 | run: ${{ matrix.db.initialize }}
71 | - name: Running php unit tests
72 | env: ${{ matrix.db.env }}
73 | run: vendor/bin/phpunit --coverage-clover coverage.clover -c tests/config/github.xml
74 | - name: Downloading Ocular Tool
75 | run: composer global require scrutinizer/ocular dev-master
76 | - name: Uploading Code Metrics
77 | run: php ~/.composer/vendor/bin/ocular code-coverage:upload --format=php-clover coverage.clover
--------------------------------------------------------------------------------
/src/descriptors/MysqlDescriptor.php:
--------------------------------------------------------------------------------
1 | driver->query(
9 | sprintf(
10 | "SELECT
11 | kcu.constraint_name as name,
12 | kcu.table_schema as `schema`,
13 | kcu.table_name as `table`,
14 | kcu.column_name as `column`,
15 | kcu.referenced_table_name AS foreign_table,
16 | kcu.referenced_table_schema AS foreign_schema,
17 | kcu.referenced_column_name AS foreign_column,
18 | rc.update_rule as on_update,
19 | rc.delete_rule as on_delete
20 | FROM
21 | information_schema.table_constraints AS tc
22 | JOIN information_schema.key_column_usage AS kcu
23 | ON tc.constraint_name = kcu.constraint_name and tc.table_schema = kcu.table_schema
24 | JOIN information_schema.referential_constraints AS rc
25 | ON rc.constraint_name = tc.constraint_name and rc.constraint_schema = tc.table_schema
26 | WHERE constraint_type = 'FOREIGN KEY'
27 | AND tc.table_name='%s' AND tc.table_schema='%s' order by kcu.constraint_name, kcu.column_name",
28 | $table['name'],
29 | $table['schema']
30 | )
31 | );
32 | }
33 |
34 | protected function getIndices(&$table)
35 | {
36 | return $this->driver->query(
37 | sprintf("SELECT table_name, column_name as `column`,index_name as `name` FROM information_schema.STATISTICS
38 | WHERE INDEX_NAME not in (SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE)
39 | AND table_name = '%s' and table_schema = '%s' order by index_name, column_name", $table['name'], $table['schema'])
40 | );
41 | }
42 |
43 | protected function getSchemata()
44 | {
45 | $defaultSchema = $this->driver->getDefaultSchema();
46 | if ($defaultSchema == '') {
47 | $schemata = $this->driver->query(
48 | "select schema_name as name from information_schema.schemata
49 | where schema_name <> 'information_schema' order by schema_name"
50 | );
51 | } else {
52 | $schemata = [
53 | [
54 | 'name' => $defaultSchema,
55 | ],
56 | ];
57 | }
58 |
59 | return $schemata;
60 | }
61 |
62 | protected function hasAutoIncrementingKey(&$table)
63 | {
64 | $auto = false;
65 | $found = $this->driver->query(
66 | sprintf(
67 | "select column_name as name
68 | from information_schema.columns
69 | where table_name = '%s' and table_schema='%s' and extra = 'auto_increment'",
70 | $table['name'],
71 | $table['schema']
72 | )
73 | );
74 |
75 | if (count($found) > 0) {
76 | $auto = true;
77 | }
78 |
79 | return $auto;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/tests/expected/sqlite/view_description.php:
--------------------------------------------------------------------------------
1 | [
29 | 'name' => 'users_view',
30 | 'schema' => '',
31 | 'columns' => [
32 | 'id' => [
33 | 'name' => 'id',
34 | 'type' => 'INTEGER',
35 | 'nulls' => true,
36 | 'default' => null,
37 | 'length' => null,
38 | ],
39 | 'username' => [
40 | 'name' => 'username',
41 | 'type' => 'TEXT',
42 | 'nulls' => true,
43 | 'default' => null,
44 | 'length' => '255',
45 | ],
46 | 'password' => [
47 | 'name' => 'password',
48 | 'type' => 'TEXT',
49 | 'nulls' => true,
50 | 'default' => null,
51 | 'length' => null,
52 | ],
53 | 'firstname' => [
54 | 'name' => 'firstname',
55 | 'type' => 'TEXT',
56 | 'nulls' => true,
57 | 'default' => null,
58 | 'length' => null,
59 | ],
60 | 'lastname' => [
61 | 'name' => 'lastname',
62 | 'type' => 'TEXT',
63 | 'nulls' => true,
64 | 'default' => null,
65 | 'length' => null,
66 | ],
67 | 'othernames' => [
68 | 'name' => 'othernames',
69 | 'type' => 'TEXT',
70 | 'nulls' => true,
71 | 'default' => null,
72 | 'length' => null,
73 | ],
74 | 'email' => [
75 | 'name' => 'email',
76 | 'type' => 'TEXT',
77 | 'nulls' => true,
78 | 'default' => null,
79 | 'length' => null,
80 | ],
81 | 'role' => [
82 | 'name' => 'role',
83 | 'type' => 'TEXT',
84 | 'nulls' => true,
85 | 'default' => null,
86 | 'length' => null,
87 | ],
88 | ],
89 | 'primary_key' => [
90 | ],
91 | 'unique_keys' => [
92 | ],
93 | 'foreign_keys' => [
94 | ],
95 | 'indices' => [
96 | ],
97 | 'auto_increment' => false,
98 | ],
99 | ];
100 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Atiaa PDO Wrapper
2 | =================
3 |
4 | [](https://github.com/ntentan/atiaa/actions/workflows/tests.yml)
5 | [](https://scrutinizer-ci.com/g/ntentan/atiaa/?branch=main)
6 | [](https://scrutinizer-ci.com/g/ntentan/atiaa/?branch=main)
7 | [](https://packagist.org/packages/ntentan/atiaa)
8 | [](https://packagist.org/packages/ntentan/atiaa)
9 |
10 | Atiaa is a thin wrapper around PHP's PDO database abstraction layer. The main
11 | purpose of atiaa is to provide utility classes that other packages in the
12 | ntentan framework need (which are not available in PDO).
13 |
14 | Currently atiaa provides the following features:
15 | - Wrappers arround the PDO query method which prepare the query and execute in
16 | one stretch. These methods then return all the results as a simple
17 | PHP associative array.
18 | - Methods which describe the schema of the database represented by the connection.
19 | - A platform independent approach for quoting database literals in queries.
20 |
21 | Currently atiaa works only with MySQL, PostgreSQL and SQLite databases.
22 | Support for other platforms is planned for later releases.
23 |
24 | Installation
25 | ------------
26 | The best way to install atiaa is to use composer. To install atiaa add
27 | `ntentan/atiaa` to your composer dependencies.
28 |
29 | Example
30 | -------
31 | The following example tries to summarise the entirety of atiaa.
32 |
33 | ````php
34 | 'mysql',
40 | 'user' => 'root',
41 | 'password' => 'rootpassy',
42 | 'host' => 'localhost',
43 | 'dbname' => 'somedb'
44 | )
45 | );
46 | $atiaa = $factory->createDriver();
47 |
48 | // Perform some queries
49 | $data = $atiaa->query('SELECT * FROM some_table');
50 | $data2 = $atiaa->query(
51 | 'SELECT * FROM some_other_table WHERE id = ? and an_item = ?',
52 | array(2, 'something')
53 | );
54 |
55 | // Get the description of the database
56 | $description = $atiaa->describe();
57 | var_dump($description);
58 |
59 | // Perform a query while quoting the literals.
60 | $data3 = $atiaa->quoteQuery('SELECT "First Name" from "Users Table" ');
61 | ````
62 |
63 | License
64 | -------
65 | Copyright (c) 2014 James Ekow Abaka Ainooson
66 |
67 | Permission is hereby granted, free of charge, to any person obtaining
68 | a copy of this software and associated documentation files (the
69 | "Software"), to deal in the Software without restriction, including
70 | without limitation the rights to use, copy, modify, merge, publish,
71 | distribute, sublicense, and/or sell copies of the Software, and to
72 | permit persons to whom the Software is furnished to do so, subject to
73 | the following conditions:
74 |
75 | The above copyright notice and this permission notice shall be
76 | included in all copies or substantial portions of the Software.
77 |
78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
81 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
82 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
83 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
84 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
85 |
--------------------------------------------------------------------------------
/tests/expected/mysql/view_description.php:
--------------------------------------------------------------------------------
1 | [
29 | 'schema' => '',
30 | 'name' => 'users_view',
31 | 'columns' => [
32 | 'email' => [
33 | 'name' => 'email',
34 | 'type' => 'varchar',
35 | 'nulls' => false,
36 | 'default' => null,
37 | 'length' => '255',
38 | ],
39 | 'firstname' => [
40 | 'name' => 'firstname',
41 | 'type' => 'varchar',
42 | 'nulls' => false,
43 | 'default' => null,
44 | 'length' => '255',
45 | ],
46 | 'id' => [
47 | 'name' => 'id',
48 | 'type' => 'int',
49 | 'nulls' => false,
50 | 'default' => '0',
51 | 'length' => null,
52 | ],
53 | 'lastname' => [
54 | 'name' => 'lastname',
55 | 'type' => 'varchar',
56 | 'nulls' => false,
57 | 'default' => null,
58 | 'length' => '255',
59 | ],
60 | 'othernames' => [
61 | 'name' => 'othernames',
62 | 'type' => 'varchar',
63 | 'nulls' => true,
64 | 'default' => 'None',
65 | 'length' => 255,
66 | ],
67 | 'password' => [
68 | 'name' => 'password',
69 | 'type' => 'varchar',
70 | 'nulls' => false,
71 | 'default' => null,
72 | 'length' => '255',
73 | ],
74 | 'role' => [
75 | 'name' => 'role',
76 | 'type' => 'varchar',
77 | 'nulls' => false,
78 | 'default' => null,
79 | 'length' => '255',
80 | ],
81 | 'username' => [
82 | 'name' => 'username',
83 | 'type' => 'varchar',
84 | 'nulls' => false,
85 | 'default' => null,
86 | 'length' => '255',
87 | ],
88 | ],
89 | 'primary_key' => [
90 | ],
91 | 'unique_keys' => [
92 | ],
93 | 'foreign_keys' => [
94 | ],
95 | 'indices' => [
96 | ],
97 | 'auto_increment' => false,
98 | ],
99 | ];
100 |
--------------------------------------------------------------------------------
/tests/expected/postgresql/view_description.php:
--------------------------------------------------------------------------------
1 | [
29 | 'schema' => '',
30 | 'name' => 'users_view',
31 | 'columns' => [
32 | 'email' => [
33 | 'name' => 'email',
34 | 'type' => 'character varying',
35 | 'nulls' => true,
36 | 'default' => null,
37 | 'length' => 255,
38 | ],
39 | 'firstname' => [
40 | 'name' => 'firstname',
41 | 'type' => 'character varying',
42 | 'nulls' => true,
43 | 'default' => null,
44 | 'length' => 255,
45 | ],
46 | 'id' => [
47 | 'name' => 'id',
48 | 'type' => 'integer',
49 | 'nulls' => true,
50 | 'default' => null,
51 | 'length' => null,
52 | ],
53 | 'lastname' => [
54 | 'name' => 'lastname',
55 | 'type' => 'character varying',
56 | 'nulls' => true,
57 | 'default' => null,
58 | 'length' => 255,
59 | ],
60 | 'othernames' => [
61 | 'name' => 'othernames',
62 | 'type' => 'character varying',
63 | 'nulls' => true,
64 | 'default' => null,
65 | 'length' => 255,
66 | ],
67 | 'password' => [
68 | 'name' => 'password',
69 | 'type' => 'character varying',
70 | 'nulls' => true,
71 | 'default' => null,
72 | 'length' => 255,
73 | ],
74 | 'role' => [
75 | 'name' => 'role',
76 | 'type' => 'character varying',
77 | 'nulls' => true,
78 | 'default' => null,
79 | 'length' => 255,
80 | ],
81 | 'username' => [
82 | 'name' => 'username',
83 | 'type' => 'character varying',
84 | 'nulls' => true,
85 | 'default' => null,
86 | 'length' => 255,
87 | ],
88 | ],
89 | 'primary_key' => [
90 | ],
91 | 'unique_keys' => [
92 | ],
93 | 'foreign_keys' => [
94 | ],
95 | 'indices' => [
96 | ],
97 | 'auto_increment' => false,
98 | ],
99 | ];
100 |
--------------------------------------------------------------------------------
/src/descriptors/InformationSchemaDescriptor.php:
--------------------------------------------------------------------------------
1 | driver->quotedQuery(
15 | 'select
16 | "column_name" as "name",
17 | "data_type" as "type",
18 | "is_nullable" as "nulls",
19 | "column_default" as "default",
20 | "character_maximum_length" as "length"
21 | from "information_schema"."columns"
22 | where "table_name" = ? and "table_schema"=?
23 | order by "column_name"',
24 | [
25 | $table['name'],
26 | $table['schema'],
27 | ]
28 | );
29 | }
30 |
31 | #[\Override]
32 | protected function getTables($schema, $tables, $includeViews)
33 | {
34 | if ($includeViews) {
35 | $condition = '(table_type = ? or table_type = ?)';
36 | $bind = ['BASE TABLE', 'VIEW'];
37 | } else {
38 | $condition = 'table_type = ?';
39 | $bind = ['BASE TABLE'];
40 | }
41 |
42 | if (count($tables) > 0) {
43 | return $this->driver->quotedQuery(
44 | 'select "table_schema" as "schema", "table_name" as "name"
45 | from "information_schema"."tables"
46 | where '.$condition.' and table_schema = ?
47 | and table_name in (?'.str_repeat(', ?', count($tables) - 1).')
48 | order by "table_name"',
49 | array_merge($bind, [$schema], $tables)
50 | );
51 | } else {
52 | return $this->driver->quotedQuery(
53 | 'select "table_schema" as "schema", "table_name" as "name"
54 | from "information_schema"."tables"
55 | where '.$condition.' and table_schema = ? order by "table_name"',
56 | array_merge($bind, [$schema])
57 | );
58 | }
59 | }
60 |
61 | #[\Override]
62 | protected function getPrimaryKey(&$table)
63 | {
64 | return $this->getConstraints($table, 'PRIMARY KEY');
65 | }
66 |
67 | #[\Override]
68 | protected function getUniqueKeys(&$table)
69 | {
70 | return $this->getConstraints($table, 'UNIQUE');
71 | }
72 |
73 | /**
74 | * @param string $type
75 | */
76 | private function getConstraints($table, $type)
77 | {
78 | return $this->driver->quotedQuery(
79 | 'select "column_name" as "column", "pk"."constraint_name" as "name"
80 | from "information_schema"."table_constraints" "pk"
81 | join "information_schema"."key_column_usage" "c" on
82 | "c"."table_name" = "pk"."table_name" and
83 | "c"."constraint_name" = "pk"."constraint_name" and
84 | "c"."constraint_schema" = "pk"."table_schema"
85 | where "pk"."table_name" = ? and pk.table_schema= ?
86 | and constraint_type = ? order by "pk"."constraint_name", "column_name"',
87 | [$table['name'], $table['schema'], $type]
88 | );
89 | }
90 |
91 | #[\Override]
92 | protected function getViews(&$schema)
93 | {
94 | return $this->driver->quotedQuery(
95 | 'select "table_schema" as "schema", "table_name" as "name", "view_definition" as "definition"
96 | from "information_schema"."views"
97 | where "table_schema" = ? order by "table_name"',
98 | [$schema]
99 | );
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/DbContext.php:
--------------------------------------------------------------------------------
1 | driver = $driver;
50 | self::$instance = $this;
51 | }
52 |
53 | /**
54 | * Create a new database context.
55 | */
56 | public static function initialize(DriverFactory $driverFactory): DbContext
57 | {
58 | self::$instance = new self($driverFactory->createDriver());
59 | return self::$instance;
60 | }
61 |
62 | /**
63 | * Get the current singleton instance of the context.
64 | *
65 | * @throws \Exception
66 | */
67 | public static function getInstance(): self
68 | {
69 | if (!self::$instance) {
70 | throw new \Exception('The database context has not been initialized');
71 | }
72 |
73 | return self::$instance;
74 | }
75 |
76 | /**
77 | * Get the Driver instance wrapped in the context.
78 | * @throws exceptions\ConnectionException
79 | */
80 | public function getDriver(): Driver
81 | {
82 | return $this->driver;
83 | }
84 |
85 | /**
86 | * Run a query on the database driver.
87 | *
88 | * @throws exceptions\ConnectionException
89 | * @throws exceptions\DatabaseDriverException
90 | */
91 | public function query(string $query, array $bindData = []): array
92 | {
93 | return $this->driver->query($query, $bindData);
94 | }
95 |
96 | /**
97 | * Destroy the context.
98 | *
99 | * @throws exceptions\ConnectionException
100 | */
101 | public static function destroy(): void
102 | {
103 | self::$instance->driver->connect();
104 | self::$instance->driver->disconnect();
105 | }
106 |
107 | public static function beginTransaction(): void
108 | {
109 | self::$instance->driver->connect();
110 | self::$instance->driver->beginTransaction();
111 | }
112 |
113 | public static function commitTransaction(): void
114 | {
115 | self::$instance->driver->connect();
116 | self::$instance->driver->commit();
117 | }
118 |
119 | public static function rollbackTransaction(): void
120 | {
121 | self::$instance->driver->connect();
122 | self::$instance->driver->rollback();
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/tests/databases/mysql.sql:
--------------------------------------------------------------------------------
1 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
2 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
3 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
4 | /*!40101 SET NAMES utf8 */;
5 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
6 | /*!40103 SET TIME_ZONE='+00:00' */;
7 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
8 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
9 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
10 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
11 |
12 | DROP TABLE IF EXISTS `departments`;
13 | /*!40101 SET @saved_cs_client = @@character_set_client */;
14 | /*!40101 SET character_set_client = utf8 */;
15 | CREATE TABLE `departments` (
16 | `id` int(11) NOT NULL AUTO_INCREMENT,
17 | `name` varchar(255) NOT NULL,
18 | PRIMARY KEY (`id`)
19 | ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
20 | /*!40101 SET character_set_client = @saved_cs_client */;
21 |
22 | LOCK TABLES `departments` WRITE;
23 | /*!40000 ALTER TABLE `departments` DISABLE KEYS */;
24 | /*!40000 ALTER TABLE `departments` ENABLE KEYS */;
25 | UNLOCK TABLES;
26 |
27 | DROP TABLE IF EXISTS `roles`;
28 | /*!40101 SET @saved_cs_client = @@character_set_client */;
29 | /*!40101 SET character_set_client = utf8 */;
30 | CREATE TABLE `roles` (
31 | `id` int(11) NOT NULL AUTO_INCREMENT,
32 | `name` varchar(255) NOT NULL,
33 | PRIMARY KEY (`id`),
34 | UNIQUE KEY `name` (`name`)
35 | ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
36 | /*!40101 SET character_set_client = @saved_cs_client */;
37 |
38 | LOCK TABLES `roles` WRITE;
39 | /*!40000 ALTER TABLE `roles` DISABLE KEYS */;
40 | /*!40000 ALTER TABLE `roles` ENABLE KEYS */;
41 | UNLOCK TABLES;
42 |
43 | DROP TABLE IF EXISTS `users`;
44 | /*!40101 SET @saved_cs_client = @@character_set_client */;
45 | /*!40101 SET character_set_client = utf8 */;
46 | CREATE TABLE `users` (
47 | `id` int(11) NOT NULL AUTO_INCREMENT,
48 | `username` varchar(255) NOT NULL,
49 | `password` varchar(255) NOT NULL,
50 | `role_id` int(11) NOT NULL,
51 | `firstname` varchar(255) NOT NULL,
52 | `lastname` varchar(255) NOT NULL,
53 | `othernames` varchar(255) DEFAULT 'None',
54 | `status` int(11) NOT NULL DEFAULT '2',
55 | `email` varchar(255) NOT NULL,
56 | `phone` varchar(64) DEFAULT NULL,
57 | `office` int(11) DEFAULT NULL,
58 | `last_login_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
59 | `is_admin` tinyint(1) DEFAULT NULL,
60 | PRIMARY KEY (`id`),
61 | KEY `user_role_fk` (`role_id`),
62 | CONSTRAINT `user_role_fk` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`)
63 | ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
64 | /*!40101 SET character_set_client = @saved_cs_client */;
65 |
66 | LOCK TABLES `users` WRITE;
67 | /*!40000 ALTER TABLE `users` DISABLE KEYS */;
68 | /*!40000 ALTER TABLE `users` ENABLE KEYS */;
69 | UNLOCK TABLES;
70 |
71 | DROP TABLE IF EXISTS `users_view`;
72 | /*!50001 DROP VIEW IF EXISTS `users_view`*/;
73 | SET @saved_cs_client = @@character_set_client;
74 | SET character_set_client = utf8;
75 | /*!50001 CREATE TABLE `users_view` (
76 | `id` tinyint NOT NULL,
77 | `username` tinyint NOT NULL,
78 | `password` tinyint NOT NULL,
79 | `firstname` tinyint NOT NULL,
80 | `lastname` tinyint NOT NULL,
81 | `othernames` tinyint NOT NULL,
82 | `email` tinyint NOT NULL,
83 | `role` tinyint NOT NULL
84 | ) ENGINE=MyISAM */;
85 | SET character_set_client = @saved_cs_client;
86 |
87 | /*!50001 DROP TABLE IF EXISTS `users_view`*/;
88 | /*!50001 DROP VIEW IF EXISTS `users_view`*/;
89 | /*!50001 SET @saved_cs_client = @@character_set_client */;
90 | /*!50001 SET @saved_cs_results = @@character_set_results */;
91 | /*!50001 SET @saved_col_connection = @@collation_connection */;
92 | /*!50001 SET character_set_client = utf8 */;
93 | /*!50001 SET character_set_results = utf8 */;
94 | /*!50001 SET collation_connection = utf8_general_ci */;
95 | /*!50001 CREATE ALGORITHM=UNDEFINED */
96 | /*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */
97 | /*!50001 VIEW `users_view` AS select `users`.`id` AS `id`,`users`.`username` AS `username`,`users`.`password` AS `password`,`users`.`firstname` AS `firstname`,`users`.`lastname` AS `lastname`,`users`.`othernames` AS `othernames`,`users`.`email` AS `email`,`roles`.`name` AS `role` from (`users` join `roles` on((`users`.`role_id` = `roles`.`id`))) */;
98 | /*!50001 SET character_set_client = @saved_cs_client */;
99 | /*!50001 SET character_set_results = @saved_cs_results */;
100 | /*!50001 SET collation_connection = @saved_col_connection */;
101 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
102 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
103 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
104 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
105 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
106 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
107 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
108 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
109 |
--------------------------------------------------------------------------------
/tests/databases/postgresql.sql:
--------------------------------------------------------------------------------
1 | SET statement_timeout = 0;
2 | SET lock_timeout = 0;
3 | SET client_encoding = 'UTF8';
4 | SET standard_conforming_strings = on;
5 | SET check_function_bodies = false;
6 | SET client_min_messages = warning;
7 |
8 | CREATE SCHEMA crm;
9 |
10 | CREATE SCHEMA hr;
11 |
12 | CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
13 |
14 | SET search_path = crm, pg_catalog;
15 | SET default_tablespace = '';
16 | SET default_with_oids = false;
17 |
18 | CREATE TABLE customers (
19 | id integer NOT NULL,
20 | name character varying(255) NOT NULL,
21 | employee_id integer NOT NULL
22 | );
23 |
24 | CREATE SEQUENCE customers_id_seq
25 | START WITH 1
26 | INCREMENT BY 1
27 | NO MINVALUE
28 | NO MAXVALUE
29 | CACHE 1;
30 |
31 |
32 | ALTER SEQUENCE customers_id_seq OWNED BY customers.id;
33 | SET search_path = hr, pg_catalog;
34 |
35 | CREATE TABLE categories (
36 | id integer NOT NULL,
37 | name character varying(255) NOT NULL
38 | );
39 |
40 | CREATE SEQUENCE departments_id_seq
41 | START WITH 1
42 | INCREMENT BY 1
43 | NO MINVALUE
44 | NO MAXVALUE
45 | CACHE 1;
46 |
47 |
48 | ALTER SEQUENCE departments_id_seq OWNED BY categories.id;
49 |
50 | CREATE TABLE employees (
51 | id integer NOT NULL,
52 | firstname character varying(255) NOT NULL,
53 | lastname character varying(255),
54 | date_of_birth date
55 | );
56 |
57 | CREATE SEQUENCE employees_id_seq
58 | START WITH 1
59 | INCREMENT BY 1
60 | NO MINVALUE
61 | NO MAXVALUE
62 | CACHE 1;
63 |
64 |
65 | ALTER SEQUENCE employees_id_seq OWNED BY employees.id;
66 | SET search_path = public, pg_catalog;
67 |
68 | CREATE TABLE departments (
69 | id integer NOT NULL,
70 | name character varying(255) NOT NULL
71 | );
72 |
73 | CREATE SEQUENCE departments_id_seq
74 | START WITH 6
75 | INCREMENT BY 1
76 | NO MINVALUE
77 | NO MAXVALUE
78 | CACHE 1;
79 |
80 |
81 | ALTER SEQUENCE departments_id_seq OWNED BY departments.id;
82 |
83 | CREATE TABLE roles (
84 | id integer NOT NULL,
85 | name character varying(255) NOT NULL
86 | );
87 |
88 | CREATE SEQUENCE roles_id_seq
89 | START WITH 5
90 | INCREMENT BY 1
91 | NO MINVALUE
92 | NO MAXVALUE
93 | CACHE 1;
94 |
95 |
96 | ALTER SEQUENCE roles_id_seq OWNED BY roles.id;
97 |
98 | CREATE TABLE users (
99 | id integer NOT NULL,
100 | username character varying(255) NOT NULL,
101 | password character varying(255) NOT NULL,
102 | role_id integer NOT NULL,
103 | firstname character varying(255) NOT NULL,
104 | lastname character varying(255) NOT NULL,
105 | othernames character varying(255) DEFAULT 'None'::character varying,
106 | status integer NOT NULL DEFAULT '2',
107 | email character varying(255) NOT NULL,
108 | phone character varying(64) DEFAULT NULL::character varying,
109 | office integer,
110 | last_login_time timestamp without time zone,
111 | is_admin boolean
112 | );
113 |
114 | CREATE SEQUENCE users_id_seq
115 | START WITH 8
116 | INCREMENT BY 1
117 | NO MINVALUE
118 | NO MAXVALUE
119 | CACHE 1;
120 |
121 |
122 | ALTER SEQUENCE users_id_seq OWNED BY users.id;
123 |
124 | CREATE VIEW users_view AS
125 | SELECT users.id,
126 | users.username,
127 | users.password,
128 | users.firstname,
129 | users.lastname,
130 | users.othernames,
131 | users.email,
132 | roles.name AS role
133 | FROM (users
134 | JOIN roles ON ((users.role_id = roles.id)));
135 | SET search_path = crm, pg_catalog;
136 |
137 | ALTER TABLE ONLY customers ALTER COLUMN id SET DEFAULT nextval('customers_id_seq'::regclass);
138 | SET search_path = hr, pg_catalog;
139 |
140 | ALTER TABLE ONLY categories ALTER COLUMN id SET DEFAULT nextval('departments_id_seq'::regclass);
141 |
142 | ALTER TABLE ONLY employees ALTER COLUMN id SET DEFAULT nextval('employees_id_seq'::regclass);
143 | SET search_path = public, pg_catalog;
144 |
145 | ALTER TABLE ONLY departments ALTER COLUMN id SET DEFAULT nextval('departments_id_seq'::regclass);
146 |
147 | ALTER TABLE ONLY roles ALTER COLUMN id SET DEFAULT nextval('roles_id_seq'::regclass);
148 |
149 | ALTER TABLE ONLY users ALTER COLUMN id SET DEFAULT nextval('users_id_seq'::regclass);
150 | SET search_path = crm, pg_catalog;
151 |
152 | ALTER TABLE ONLY customers
153 | ADD CONSTRAINT customers_pkey PRIMARY KEY (id);
154 | SET search_path = hr, pg_catalog;
155 |
156 | ALTER TABLE ONLY categories
157 | ADD CONSTRAINT departments_pkey PRIMARY KEY (id);
158 |
159 | ALTER TABLE ONLY employees
160 | ADD CONSTRAINT employees_pkey PRIMARY KEY (id);
161 | SET search_path = public, pg_catalog;
162 |
163 | ALTER TABLE ONLY departments
164 | ADD CONSTRAINT departments_pkey PRIMARY KEY (id);
165 |
166 | ALTER TABLE ONLY roles
167 | ADD CONSTRAINT roles_pkey PRIMARY KEY (id);
168 |
169 | ALTER TABLE ONLY users
170 | ADD CONSTRAINT users_pkey PRIMARY KEY (id);
171 | SET search_path = crm, pg_catalog;
172 |
173 | ALTER TABLE ONLY customers
174 | ADD CONSTRAINT customers_employee_id_fkey FOREIGN KEY (employee_id) REFERENCES hr.employees(id);
175 | SET search_path = public, pg_catalog;
176 |
177 | ALTER TABLE ONLY users
178 | ADD CONSTRAINT users_office_fkey FOREIGN KEY (office) REFERENCES departments(id);
179 |
180 | ALTER TABLE ONLY users
181 | ADD CONSTRAINT users_role_id_fkey FOREIGN KEY (role_id) REFERENCES roles(id);
182 |
183 |
184 |
--------------------------------------------------------------------------------
/src/descriptors/PostgresqlDescriptor.php:
--------------------------------------------------------------------------------
1 | .*)(::)(?[a-zA-Z0-9\s]*)$/", $defaultValue, $matches)) {
11 | $value = $matches['value'];
12 |
13 | // If numeric
14 | if (is_numeric($value)) {
15 | return $value;
16 |
17 | // If its a string
18 | } elseif (preg_match("/'(?.*)'/", $value, $matches)) {
19 | return $matches['string'];
20 |
21 | // null for anything else
22 | } else {
23 | return;
24 | }
25 |
26 | // Return the nextval as atiaa uses them to detect auto keys
27 | } elseif (preg_match("/nextval\(.*/", $defaultValue)) {
28 | return $defaultValue;
29 |
30 | // Return numeric default values
31 | } elseif (is_numeric($defaultValue)) {
32 | return $defaultValue;
33 |
34 | // Return null for anything else
35 | } else {
36 | return;
37 | }
38 | }
39 |
40 | /**
41 | * @note Query sourced from http://stackoverflow.com/questions/2204058/show-which-columns-an-index-is-on-in-postgresql
42 | *
43 | * @param type $table
44 | *
45 | * @return type
46 | */
47 | protected function getIndices(&$table)
48 | {
49 | return $this->driver->query(
50 | sprintf(
51 | "select
52 | t.relname as table_name,
53 | i.relname as name,
54 | a.attname as column
55 | from
56 | pg_class t,
57 | pg_class i,
58 | pg_index ix,
59 | pg_attribute a,
60 | pg_namespace n
61 | where
62 | t.oid = ix.indrelid
63 | and i.oid = ix.indexrelid
64 | and a.attrelid = t.oid
65 | and a.attnum = ANY(ix.indkey)
66 | and t.relkind = 'r'
67 | and t.relname = '%s'
68 | and n.nspname = '%s'
69 | and i.relnamespace = n.oid
70 | AND indisunique != 't'
71 | AND indisprimary != 't'
72 | order by i.relname, a.attname",
73 | $table['name'],
74 | $table['schema']
75 | )
76 | );
77 | }
78 |
79 | /**
80 | * @note Query sourced from http://stackoverflow.com/questions/1152260/postgres-sql-to-list-table-foreign-keys
81 | *
82 | * @param type $table
83 | */
84 | protected function getForeignKeys(&$table)
85 | {
86 | return $this->driver->query(
87 | "SELECT distinct
88 | kcu.constraint_name as name,
89 | kcu.table_schema as schema,
90 | kcu.table_name as table,
91 | kcu.column_name as column,
92 | ccu.table_name AS foreign_table,
93 | ccu.table_schema AS foreign_schema,
94 | ccu.column_name AS foreign_column,
95 | rc.update_rule as on_update,
96 | rc.delete_rule as on_delete
97 |
98 | FROM
99 | information_schema.table_constraints AS tc
100 | JOIN information_schema.key_column_usage AS kcu
101 | ON tc.constraint_name = kcu.constraint_name and tc.table_schema = kcu.table_schema and tc.table_name = kcu.table_name
102 | JOIN information_schema.constraint_column_usage AS ccu
103 | ON ccu.constraint_name = tc.constraint_name and ccu.constraint_schema = tc.table_schema
104 | JOIN information_schema.referential_constraints AS rc
105 | ON rc.constraint_name = tc.constraint_name and rc.constraint_schema = tc.table_schema
106 | WHERE constraint_type = 'FOREIGN KEY'
107 | AND tc.table_name=:name AND tc.table_schema=:schema
108 | AND kcu.table_name=:name AND kcu.table_schema=:schema
109 | order by kcu.constraint_name, kcu.column_name",
110 | //$table['name'], $table['schema']
111 | ['name'=>$table['name'], 'schema'=>$table['schema']]
112 | );
113 | }
114 |
115 | public function getSchemata()
116 | {
117 | return $this->driver->query(
118 | "select schema_name as name from information_schema.schemata
119 | where schema_name not like 'pg_temp%' and
120 | schema_name not like 'pg_toast%' and
121 | schema_name not in ('pg_catalog', 'information_schema')
122 | order by schema_name"
123 | );
124 | }
125 |
126 | protected function hasAutoIncrementingKey(&$table)
127 | {
128 | $auto = false;
129 | $primaryKey = reset($table['primary_key']);
130 | if (is_array($primaryKey)) {
131 | if (count($primaryKey['columns']) == 1 && substr_count($table['columns'][$primaryKey['columns'][0]]['default'], 'nextval')) {
132 | $table['columns'][$primaryKey['columns'][0]]['default'] = null;
133 | $auto = true;
134 | }
135 | }
136 |
137 | return $auto;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/descriptors/SqliteDescriptor.php:
--------------------------------------------------------------------------------
1 | driver->query("PRAGMA table_info({$table['name']})");
9 | foreach ($pragmaColumns as $column) {
10 | preg_match("/(?[a-zA-Z]*)(\((?[0-9]+)\))*/", $column['type'], $matches);
11 | $columns[] = [
12 | 'name' => $column['name'],
13 | 'type' => $matches['type'],
14 | 'nulls' => $column['notnull'] == '0',
15 | 'default' => $column['dflt_value'],
16 | 'length' => isset($matches['length']) ? $matches['length'] : null,
17 | ];
18 | }
19 |
20 | return $columns;
21 | }
22 |
23 | protected function cleanDefaultValue($default)
24 | {
25 | if (preg_match("/(')?(?.*)/", $default, $matches)) {
26 | return substr($matches['value'], 0, strlen($matches['value']) - 1);
27 | } else {
28 | return;
29 | }
30 | }
31 |
32 | protected function getForeignKeys(&$table)
33 | {
34 | $foreignKeys = [];
35 | $pragmaColumns = $this->driver->query("pragma foreign_key_list({$table['name']})");
36 | foreach ($pragmaColumns as $i => $foreignKey) {
37 | $foreignKeys[] = [
38 | 'name' => "{$table['name']}_{$foreignKey['table']}_{$i}_fk",
39 | 'schema' => $table['schema'],
40 | 'table' => $table['name'],
41 | 'column' => $foreignKey['from'],
42 | 'foreign_table' => $foreignKey['table'],
43 | 'foreign_schema' => 'main',
44 | 'foreign_column' => $foreignKey['to'],
45 | 'on_update' => $foreignKey['on_update'],
46 | 'on_delete' => $foreignKey['on_delete'],
47 | ];
48 | }
49 |
50 | return $foreignKeys;
51 | }
52 |
53 | private function extractIndexDetails($details, $index, &$indexDetails)
54 | {
55 | foreach ($details as $detail) {
56 | if ($detail['name'] != '') {
57 | $indexDetails[] = [
58 | 'column' => $detail['name'],
59 | 'name' => $index['name'],
60 | 'schema' => $index['schema'],
61 | ];
62 | }
63 | }
64 | }
65 |
66 | private function getIndexDetails($table, $unique)
67 | {
68 | $indices = $this->driver->query("pragma index_list({$table['name']})");
69 | $indexDetails = [];
70 |
71 | foreach ($indices as $index) {
72 | if ($index['unique'] == $unique) {
73 | $index['schema'] = $table['schema'];
74 | $detail = $this->driver->query("pragma index_info({$index['name']})");
75 | $this->extractIndexDetails($detail, $index, $indexDetails);
76 | }
77 | }
78 |
79 | return $indexDetails;
80 | }
81 |
82 | protected function getIndices(&$table)
83 | {
84 | return $this->getIndexDetails($table, '0');
85 | }
86 |
87 | protected function getPrimaryKey(&$table)
88 | {
89 | $keyColumns = [];
90 | $pragmaColumns = $this->driver->query("PRAGMA table_info({$table['name']})");
91 | foreach ($pragmaColumns as $column) {
92 | if ($column['pk'] > 0) {
93 | $keyColumns[] = [
94 | 'order' => $column['pk'],
95 | 'column' => $column['name'],
96 | 'name' => "{$table['name']}_pk",
97 | ];
98 | }
99 | }
100 |
101 | usort($keyColumns, function ($a, $b) {
102 | return $a['order'] - $b['order'];
103 | });
104 |
105 | return $keyColumns;
106 | }
107 |
108 | protected function getSchemata()
109 | {
110 | return [['name' => 'main']];
111 | }
112 |
113 | protected function getTables($schema, $tables, $includeViews)
114 | {
115 | if ($includeViews) {
116 | $condition = '(type = ? or type = ?)';
117 | $bind = ['table', 'view'];
118 | } else {
119 | $condition = 'type = ?';
120 | $bind = ['table'];
121 | }
122 |
123 | if (count($tables) > 0) {
124 | return $this->driver->quotedQuery(
125 | 'select name as "name", \'main\' as "schema" from sqlite_master
126 | where '.$condition.' and name not in (\'sqlite_master\', \'sqlite_sequence\') and name in (?'.str_repeat(', ?', count($tables) - 1).')
127 | order by name',
128 | array_merge($bind, $tables)
129 | );
130 | } else {
131 | return $this->driver->quotedQuery(
132 | 'select name as "name", \'main\' as "schema" from sqlite_master
133 | where name not in (\'sqlite_master\', \'sqlite_sequence\') and '.$condition,
134 | array_merge($bind)
135 | );
136 | }
137 | }
138 |
139 | protected function getUniqueKeys(&$table)
140 | {
141 | return $this->getIndexDetails($table, '1');
142 | }
143 |
144 | protected function getViews(&$schema)
145 | {
146 | return $this->driver->query("select 'main' as schema, name, sql as definition from sqlite_master where type = 'view'");
147 | }
148 |
149 | protected function hasAutoIncrementingKey(&$table)
150 | {
151 | $sql = $this->driver->query('select sql from sqlite_master where name = ?', [$table['name']]);
152 | if (preg_match('/AUTOINCREMENT/', $sql[0]['sql'])) {
153 | return true;
154 | } else {
155 | return false;
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/tests/cases/DriverTest.php:
--------------------------------------------------------------------------------
1 | getDriverName()).'Descriptor';
44 | $descriptor = new $descriptorClass($driver);
45 |
46 | return $descriptor;
47 | }
48 |
49 | public function setUp(): void
50 | {
51 | // Preserve the original dbname just in case it changes in any test
52 | $this->dbName = getenv('ATIAA_DBNAME');
53 | }
54 |
55 | public function tearDown(): void
56 | {
57 | putenv("ATIAA_DBNAME={$this->dbName}");
58 | }
59 |
60 | public function testFunctions()
61 | {
62 | $driverName = $this->getDriverName();
63 | $driver = $this->getDriver();
64 |
65 | $strings = json_decode(file_get_contents("tests/expected/$driverName/strings.json"), true);
66 | $this->assertEquals($strings['quoted_string'], $driver->quote('string'));
67 | $this->assertEquals($strings['quoted_identifier'], $driver->quoteIdentifier('identifier'));
68 | $this->assertEquals($strings['quoted_query_identifiers'], $driver->quoteQueryIdentifiers('SELECT "some", "identifiers" FROM "some"."table"'));
69 | $pdo = $driver->getPDO();
70 | $this->assertInstanceOf('PDO', $pdo);
71 | }
72 |
73 | public function testFullDescription()
74 | {
75 | $driver = $this->getDriver();
76 | $type = $this->getDriverName();
77 |
78 | $testDbDescription = $driver->describe();
79 | $views = $testDbDescription['views'];
80 |
81 | // Unset the views since they vary from each other with respect to database drivers.
82 | unset($testDbDescription['views']);
83 | unset($testDbDescription['schemata'][$driver->getDefaultSchema()]['views']);
84 |
85 | require "tests/expected/{$type}/database_description.php";
86 | $this->assertEquals($databaseDescription, $testDbDescription);
87 | $this->assertArrayHasKey('users_view', $views);
88 | $this->assertArrayHasKey('definition', $views['users_view']);
89 | $this->assertEquals('users_view', $views['users_view']['name']);
90 | }
91 |
92 | public function testCleanDefaultDescription()
93 | {
94 | $driver = $this->getDriver();
95 | $type = $this->getDriverName();
96 | $driver->setCleanDefaults(true);
97 |
98 | $testDbDescription = $driver->describe();
99 | $views = $testDbDescription['views'];
100 | unset($testDbDescription['views']);
101 | unset($testDbDescription['schemata'][$driver->getDefaultSchema()]['views']);
102 | require "tests/expected/{$type}/database_description_clean_defaults.php";
103 | $this->assertEquals($databaseDescription, $testDbDescription);
104 | $this->assertArrayHasKey('users_view', $views);
105 | $this->assertArrayHasKey('definition', $views['users_view']);
106 | $this->assertEquals('users_view', $views['users_view']['name']);
107 | }
108 |
109 | public function testViewDescriptionAsTable()
110 | {
111 | $driver = $this->getDriver();
112 | $type = $this->getDriverName();
113 |
114 | $viewDbDescription = $driver->describeTable('users_view');
115 | require "tests/expected/{$type}/view_description.php";
116 | $this->assertEquals($viewDescription, $viewDbDescription);
117 | }
118 |
119 | public function testStringSchema()
120 | {
121 | if (!$this->hasSchemata()) {
122 | $this->markTestSkipped();
123 |
124 | return;
125 | }
126 |
127 | $driver = $this->getDriver();
128 | $type = $this->getDriverName();
129 |
130 | $employeesDbDescription = $driver->describeTable('hr.employees');
131 | require "tests/expected/{$type}/employees_description.php";
132 | $this->assertEquals($employeesDescription, $employeesDbDescription);
133 | }
134 |
135 | public function testTableNotFoundException()
136 | {
137 | $this->expectException(TableNotFoundException::class);
138 | $driver = $this->getDriver();
139 | $driver->describeTable('unknown_table');
140 | }
141 |
142 | public function testTableNotFoundExceptionAgain()
143 | {
144 | $this->expectException(TableNotFoundException::class);
145 | $driver = $this->getDriver($this);
146 | $this->getDescriptor($driver)->describeTables($driver->getDefaultSchema(), ['users', 'unknown_table']);
147 | }
148 |
149 | public function testFaultyQueryException()
150 | {
151 | $this->expectException(DatabaseDriverException::class);
152 | $driver = $this->getDriver($this);
153 | $driver->query('SPELECT * FROM dummy');
154 | }
155 |
156 | private function hasSchemata()
157 | {
158 | return strtolower(getenv('ATIAA_HAS_SCHEMAS')) === 'yes';
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/Driver.php:
--------------------------------------------------------------------------------
1 | 'mysql',
56 | * 'user' => 'root',
57 | * 'password' => 'rootpassy',
58 | * 'host' => 'localhost',
59 | * 'dbname' => 'somedb'
60 | * )
61 | * );
62 | *
63 | * var_dump($driver->query("SELECT * FROM some_table");
64 | * var_dump($driver->describe());
65 | * ````
66 | */
67 | public function __construct(array $config)
68 | {
69 | $this->config = $config;
70 | $this->defaultSchema = $this->config['schema'] ?? $this->defaultSchema ?? null;
71 | }
72 |
73 | public function connect(): void
74 | {
75 | if ($this->connected) {
76 | return;
77 | }
78 | $username = $this->config['user'] ?? null;
79 | $password = $this->config['password'] ?? null;
80 |
81 | unset($this->config['schema']);
82 |
83 | try {
84 | $options = $this->config['options'] ?? [];
85 |
86 | $options[PDO::ATTR_ERRMODE] = $options[PDO::ATTR_ERRMODE] ?? PDO::ERRMODE_EXCEPTION;
87 | $options[PDO::ATTR_EMULATE_PREPARES] = $options[PDO::ATTR_EMULATE_PREPARES] ?? false;
88 | $options[PDO::ATTR_STRINGIFY_FETCHES] = $options[PDO::ATTR_STRINGIFY_FETCHES] ?? false;
89 |
90 | $this->pdo = new \PDO(
91 | $this->getDriverName().':'.$this->expand($this->config), $username, $password, $options
92 | );
93 | $this->connected = true;
94 | } catch (\PDOException $e) {
95 | throw new ConnectionException("PDO failed to connect: {$e->getMessage()}");
96 | }
97 | }
98 |
99 | public function __destruct()
100 | {
101 | $this->disconnect();
102 | }
103 |
104 | /**
105 | * Close a connection to the database server.
106 | */
107 | public function disconnect(): void
108 | {
109 | $this->pdo = new NullConnection();
110 | $this->connected = false;
111 | }
112 |
113 | /**
114 | * Get the default schema of the current connection.
115 | *
116 | * @return string
117 | */
118 | public function getDefaultSchema(): string
119 | {
120 | return $this->defaultSchema;
121 | }
122 |
123 | /**
124 | * Use the PDO driver to quote a string.
125 | *
126 | * @throws ConnectionException
127 | */
128 | public function quote(string $string): string
129 | {
130 | return $this->getPDO()->quote($string);
131 | }
132 |
133 |
134 | private function fetchRows(\PDOStatement $statement): array
135 | {
136 | try {
137 | $rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
138 | return $rows;
139 | } catch (\PDOException $e) {
140 | return [];
141 | }
142 | }
143 |
144 | private function prepareQuery($query, $bindData): \PDOStatement
145 | {
146 | $statement = $this->pdo->prepare($query);
147 | foreach ($bindData as $key => $value) {
148 | switch (gettype($value)) {
149 | case 'integer':
150 | case 'boolean': // casts to boolean seems unstable
151 | $type = \PDO::PARAM_INT;
152 | break;
153 | default:
154 | $type = \PDO::PARAM_STR;
155 | break;
156 | }
157 | // Bind values while adjusting numerical indices to start from 1
158 | $statement->bindValue(is_numeric($key) ? $key + 1 : $key, $value, $type);
159 | }
160 |
161 | return $statement;
162 | }
163 |
164 | /**
165 | * Pepare and execute a query, while binding data at the same time. Prevents the writing of repetitive prepare and
166 | * execute statements. This method returns an array which contains the results of the query that was executed. For
167 | * queries which do not return any results a null is returned.
168 | *
169 | * @throws DatabaseDriverException
170 | */
171 | public function query(string $query, array $bindData = []): array
172 | {
173 | $this->connect();
174 | try {
175 | if (empty($bindData)) {
176 | $statement = $this->pdo->query($query);
177 | } else {
178 | $statement = $this->prepareQuery($query, $bindData);
179 | $statement->execute();
180 | $statement->errorCode();
181 | }
182 | } catch (\PDOException $e) {
183 | $boundData = json_encode($bindData);
184 |
185 | throw new DatabaseDriverException("{$e->getMessage()} [$query] [BOUND DATA:$boundData]");
186 | }
187 | $rows = $this->fetchRows($statement);
188 | $statement->closeCursor();
189 |
190 | return $rows;
191 | }
192 |
193 | /**
194 | * Runs a query but ensures that all identifiers are properly quoted by calling the Driver::quoteQueryIdentifiers
195 | * method on the query before executing it.
196 | *
197 | * @throws DatabaseDriverException
198 | */
199 | public function quotedQuery(string $query, array $bindData = []): array
200 | {
201 | return $this->query($this->quoteQueryIdentifiers($query), $bindData);
202 | }
203 |
204 | /**
205 | * Expands the configuration array into a format that can easily be passed to PDO.
206 | */
207 | private function expand(array $params): string
208 | {
209 | unset($params['driver']);
210 | if (isset($params['file'])) {
211 | if ($params['file'] != '') {
212 | return $params['file'];
213 | }
214 | }
215 |
216 | $equated = [];
217 | foreach ($params as $key => $value) {
218 | if ($value == '' || $key == 'options') {
219 | continue;
220 | } else {
221 | $equated[] = "$key=$value";
222 | }
223 | }
224 |
225 | return implode(';', $equated);
226 | }
227 |
228 | /**
229 | * This method provides a system independent way of quoting identifiers in queries. By default all identifiers can
230 | * be quoted with double quotes ("). When a query quoted with double quotes is passed through this method the output
231 | * generated has the double quotes replaced with the quoting character of the target database platform.
232 | */
233 | public function quoteQueryIdentifiers(string $query): string
234 | {
235 | return preg_replace_callback(
236 | '/\"([a-zA-Z\_ ]*)\"/',
237 | function ($matches) {
238 | return $this->quoteIdentifier($matches[1]);
239 | },
240 | $query
241 | );
242 | }
243 |
244 | /**
245 | * Returns an array description of the schema represented by the connection.
246 | * The description returns contains information about `tables`, `columns`, `keys`,
247 | * `constraints`, `views` and `indices`.
248 | */
249 | public function describe(): array
250 | {
251 | return $this->getDescriptor()->describe();
252 | }
253 |
254 | /**
255 | * Returns the description of a database table as an associative array.
256 | */
257 | public function describeTable(string $table): array
258 | {
259 | $table = explode('.', $table);
260 | if (count($table) > 1) {
261 | $schema = $table[0];
262 | $table = $table[1];
263 | } else {
264 | $schema = $this->getDefaultSchema();
265 | $table = $table[0];
266 | }
267 |
268 | return $this->getDescriptor()->describeTables($schema, [$table], true);
269 | }
270 |
271 | /**
272 | * A wrapper around PDO's beginTransaction method which uses a static reference counter to implement nested
273 | * transactions.
274 | */
275 | public function beginTransaction()
276 | {
277 | if (self::$transactionCount++ === 0) {
278 | $this->pdo->beginTransaction();
279 | }
280 | }
281 |
282 | /**
283 | * A wrapper around PDO's commit transaction method which uses a static reference counter to implement nested
284 | * transactions.
285 | */
286 | public function commit()
287 | {
288 | if (--self::$transactionCount === 0) {
289 | $this->pdo->commit();
290 | }
291 | }
292 |
293 | /**
294 | * A wrapper around PDO's rollback transaction methd which rolls back all activities performed since the first call
295 | * to begin transaction. Unfortunately, transactions cannot be rolled back in a nested fashion.
296 | */
297 | public function rollback()
298 | {
299 | if (self::$transactionCount) {
300 | $this->pdo->rollBack();
301 | self::$transactionCount = 0;
302 | }
303 | }
304 |
305 | /**
306 | * Return the underlying PDO object.
307 | */
308 | public function getPDO(): PDO
309 | {
310 | return $this->pdo;
311 | }
312 |
313 | /**
314 | * Returns an instance of a descriptor for a given driver.
315 | */
316 | private function getDescriptor(): Descriptor
317 | {
318 | if (!isset($this->descriptor)) {
319 | $descriptorClass = '\\ntentan\\atiaa\\descriptors\\'.ucfirst($this->config['driver']).'Descriptor';
320 | $this->descriptor = new $descriptorClass($this);
321 | }
322 |
323 | return $this->descriptor;
324 | }
325 |
326 | /**
327 | * A wrapper around PDO's lastInsertId() method.
328 | */
329 | public function getLastInsertId(): mixed
330 | {
331 | return $this->pdo->lastInsertId();
332 | }
333 |
334 | /**
335 | * Specify the default schema to use in cases where a schema is not provided as part of the table reference.
336 | */
337 | public function setDefaultSchema(string $defaultSchema)
338 | {
339 | $this->defaultSchema = $defaultSchema;
340 | }
341 |
342 | abstract protected function getDriverName();
343 |
344 | abstract public function quoteIdentifier($identifier);
345 |
346 | public function setCleanDefaults(bool $cleanDefaults): void
347 | {
348 | $this->getDescriptor()->setCleanDefaults($cleanDefaults);
349 | }
350 |
351 | public function isConnected(): bool
352 | {
353 | return $this->connected;
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/src/Descriptor.php:
--------------------------------------------------------------------------------
1 | driver = $driver;
25 | }
26 |
27 | /**
28 | * Returns a list of schemata available on the database.
29 | *
30 | * @return array
31 | */
32 | abstract protected function getSchemata();
33 |
34 | /**
35 | * Retrieve the names of all the tables in a given schema.
36 | * The array returned must be a list of structured arrays which have `name`
37 | * and `schema` as keys. The `name` key should represent the name of the table and
38 | * the `schema` key should represent the name of the schema (which is the same
39 | * as the schema which was passed to the function).
40 | *
41 | * @param string $schema The name of the schema whose tables should be
42 | * describled.
43 | * @param array An array contianing names of specific tables
44 | * who's descriptions should be retrieved.
45 | *
46 | * @return array
47 | */
48 | abstract protected function getTables($schema, $requestedTables, $includeViews);
49 |
50 | /**
51 | * Retrieve descriptions of all the columns available in a given table as an array.
52 | * The array returned must contain structured arrays with the following keys.
53 | *
54 | * name
55 | * : The name of the column.
56 | *
57 | * type
58 | * : The system specific datatype of the column.
59 | *
60 | * nulls
61 | * : A boolean which is true for columsn which can contain null values
62 | * and false for columns which can't.
63 | *
64 | * default
65 | * : The default value of the column. In cases where there is no default
66 | * this column is set to null.
67 | *
68 | * length
69 | * : The maximum character lenght of the column.
70 | *
71 | * @param array $table An array which contains the name of the table as it's
72 | * `name` key and the schema of the table as it's `schema` key.
73 | *
74 | * @return array>
75 | */
76 | abstract protected function getColumns(&$table);
77 |
78 | /**
79 | * Retrieve the descriptions of all the views of a given schema in a array.
80 | * The array returned must contain structured arrays with the following keys.
81 | *
82 | * name
83 | * : The name of the view.
84 | *
85 | * schema
86 | * : The schema to which the view belongs.
87 | *
88 | * definition
89 | * : The SQL query which represents the definition of the view.
90 | *
91 | * @param string $schema The name of the database schema
92 | *
93 | * @return array>
94 | */
95 | abstract protected function getViews(&$schema);
96 |
97 | /**
98 | * Retrieve the description of a primary key on a given table.
99 | * The description returned must be an array which contains structured
100 | * arrays with the following keys.
101 | *
102 | * column
103 | * : The name of a column which is part of the primary key
104 | *
105 | * name
106 | * : The name of the primary key constraint (must be the same throughout
107 | * all the items returned).
108 | *
109 | * For primary keys with multiple columns, the array returned would contain
110 | * one entry for each column.
111 | *
112 | * @param array $table An array which contains the name of the table as it's
113 | * `name` key and the schema of the table as it's `schema` key.
114 | *
115 | * @return array>
116 | */
117 | abstract protected function getPrimaryKey(&$table);
118 |
119 | /**
120 | * Retrieve the description of unique keys on a given table.
121 | * The description returned must be an array which contains structured
122 | * arrays with the following keys.
123 | *
124 | * column
125 | * : The name of a column which is part of a unique key
126 | *
127 | * name
128 | * : The name of the unique key constraint.
129 | *
130 | * For unique keys with multiple columns, the value of the `name` key must
131 | * be the same for only the columns in the key.
132 | *
133 | * @param array $table An array which contains the name of the table as it's
134 | * `name` key and the schema of the table as it's `schema` key.
135 | *
136 | * @return array>
137 | */
138 | abstract protected function getUniqueKeys(&$table);
139 |
140 | /**
141 | * Retrieve the description of foreign keys on a given table.
142 | * The description returned must be an array which contains structured
143 | * arrays with the following keys.
144 | *
145 | * name
146 | * : The name of the foreign key constraint.
147 | *
148 | * table
149 | * : The name of the database table (should be same as passed to the function)
150 | *
151 | * schema
152 | * : The schema of the database table (should be same as passed to the
153 | * function)
154 | *
155 | * column
156 | * : The foreign key column on the table.
157 | *
158 | * foreign_table
159 | * : The name of the database table to be referenced.
160 | *
161 | * foreign_schema
162 | * : The schema which contains the database table to be referenced.
163 | *
164 | * foreign_column:
165 | * : The column to be refereced on the foreign table.
166 | *
167 | * For foreign keys with multiple columns, the value of the `name` key must
168 | * be the same for only the columns in the key.
169 | *
170 | * @param array $table An array which contains the name of the table as it's
171 | * `name` key and the schema of the table as it's `schema` key.
172 | *
173 | * @return array>
174 | */
175 | abstract protected function getForeignKeys(&$table);
176 |
177 | /**
178 | * Retrieve the description of indices on a given table.
179 | * The description returned must be an array which contains structured
180 | * arrays with the following keys.
181 | *
182 | * column
183 | * : The name of a column which is part of an index
184 | *
185 | * name
186 | * : The name of the index.
187 | *
188 | * For unique keys with multiple columns, the value of the `name` key must
189 | * be the same for only the columns in the key.
190 | *
191 | * @param array $table An array which contains the name of the table as it's
192 | * `name` key and the schema of the table as it's `schema` key.
193 | *
194 | * @return array>
195 | */
196 | abstract protected function getIndices(&$table);
197 |
198 | /**
199 | * Returns a boolean value which tells whether a table has an auto incrementing
200 | * key or not.
201 | *
202 | * @return bool
203 | */
204 | abstract protected function hasAutoIncrementingKey(&$table);
205 |
206 | /**
207 | * Returns the description of the database as an array.
208 | *
209 | * @return array
210 | */
211 | public function describe()
212 | {
213 | $defaultSchema = $this->driver->getDefaultSchema();
214 | $description = [
215 | 'schemata' => [],
216 | ];
217 |
218 | $schemata = $this->getSchemata();
219 |
220 | foreach ($schemata as $schema) {
221 | $description['schemata'][$schema['name']]['name'] = $schema['name'];
222 | $description['schemata'][$schema['name']]['tables'] = $this->describeTables($schema['name']);
223 | $description['schemata'][$schema['name']]['views'] = $this->describeViews($schema['name']);
224 |
225 | if ($schema['name'] == $defaultSchema) {
226 | $description['tables'] = $description['schemata'][$schema['name']]['tables']; //$this->describeTables($defaultSchema);
227 | $description['views'] = $description['schemata'][$schema['name']]['views']; //$this->describeViews($defaultSchema);
228 | }
229 | }
230 |
231 | return $description;
232 | }
233 |
234 | public function setCleanDefaults($cleanDefaults)
235 | {
236 | $this->cleanDefaults = $cleanDefaults;
237 | }
238 |
239 | /**
240 | * Throws exceptions for which are found in the list of requested tables
241 | * but not found in the list of found tables.
242 | *
243 | * @param array $tables
244 | * @param array $requestedTables
245 | *
246 | * @throws exceptions\TableNotFoundException
247 | */
248 | private function throwTableExceptions($tables, $requestedTables)
249 | {
250 | $foundTables = [];
251 | foreach ($tables as $table) {
252 | $foundTables[] = $table['name'];
253 | }
254 |
255 | foreach ($requestedTables as $requestedTable) {
256 | if (array_search($requestedTable, $foundTables) === false) {
257 | throw new exceptions\TableNotFoundException(
258 | $requestedTable
259 | ? "$requestedTable not found on target database."
260 | : 'Please specify a table name.'
261 | );
262 | }
263 | }
264 | }
265 |
266 | public function describeTables($schema, $requestedTables = [], $includeViews = false)
267 | {
268 | $description = [];
269 | $tables = $this->getTables($schema, $requestedTables, $includeViews);
270 |
271 | if (count($requestedTables) > 0 && count($tables) < count($requestedTables)) {
272 | $this->throwTableExceptions($tables, $requestedTables);
273 | }
274 |
275 | foreach ($tables as $table) {
276 | $table['columns'] = $this->describeColumns($table);
277 | $table['primary_key'] = $this->describePrimaryKey($table);
278 | $table['unique_keys'] = $this->describeUniqueKeys($table);
279 | $table['foreign_keys'] = $this->describeForeignKeys($table);
280 | $table['indices'] = $this->describeIndices($table);
281 | $table['auto_increment'] = $this->hasAutoIncrementingKey($table);
282 | $table['schema'] = $this->fixSchema($table['schema']);
283 |
284 | $description[$table['name']] = $table;
285 | }
286 |
287 | return $description;
288 | }
289 |
290 | private function describeColumns($table)
291 | {
292 | $columns = [];
293 | $columnDetails = $this->getColumns($table);
294 | foreach ($columnDetails as $column) {
295 | $columns[$column['name']] = $column;
296 | $columns[$column['name']]['nulls'] = $columns[$column['name']]['nulls'] == 'YES' ? true : false;
297 |
298 | if ($this->cleanDefaults) {
299 | $columns[$column['name']]['default'] = $this->cleanDefaultValue($column['default']);
300 | }
301 | }
302 |
303 | return $columns;
304 | }
305 |
306 | private function describeViews($schema)
307 | {
308 | $description = [];
309 | $views = $this->getViews($schema);
310 | foreach ($views as $view) {
311 | $description[$view['name']] = [
312 | 'name' => $view['name'],
313 | 'schema' => $view['schema'],
314 | 'definition' => $view['definition'],
315 | ];
316 | }
317 |
318 | return $description;
319 | }
320 |
321 | private function describePrimaryKey($table)
322 | {
323 | return $this->describeKey($this->getPrimaryKey($table));
324 | }
325 |
326 | private function describeUniqueKeys($table)
327 | {
328 | return $this->describeKey($this->getUniqueKeys($table));
329 | }
330 |
331 | private function describeForeignKeys($table)
332 | {
333 | return $this->describeKey($this->getForeignKeys($table));
334 | }
335 |
336 | private function describeIndices($table)
337 | {
338 | return $this->describeKey($this->getIndices($table));
339 | }
340 |
341 | private function describeKey($constraintColumns)
342 | {
343 | $constraints = [];
344 | foreach ($constraintColumns as $column) {
345 | $name = $column['name'];
346 | unset($column['name']);
347 | foreach ($column as $key => $value) {
348 | if ($key == 'column' || $key == 'foreign_column') {
349 | $constraints[$name]["{$key}s"][] = $value;
350 | } else {
351 | if ($key === 'schema' || $key === 'foreign_schema') {
352 | $value = $this->fixSchema($value);
353 | }
354 | $constraints[$name][$key] = $value;
355 | }
356 | }
357 | }
358 |
359 | return $constraints;
360 | }
361 |
362 | private function fixSchema($schema)
363 | {
364 | $defaultSchema = $this->driver->getDefaultSchema();
365 | if ($schema == false || $schema == $defaultSchema) {
366 | return '';
367 | } else {
368 | return $schema;
369 | }
370 | }
371 |
372 | protected function cleanDefaultValue($defaultValue)
373 | {
374 | return $defaultValue;
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/tests/expected/mysql/database_description.php:
--------------------------------------------------------------------------------
1 | [
29 | 'atiaa_test' => [
30 | 'name' => 'atiaa_test',
31 | 'tables' => [
32 | 'departments' => [
33 | 'schema' => '',
34 | 'name' => 'departments',
35 | 'columns' => [
36 | 'id' => [
37 | 'name' => 'id',
38 | 'type' => 'int',
39 | 'nulls' => false,
40 | 'default' => null,
41 | 'length' => null,
42 | ],
43 | 'name' => [
44 | 'name' => 'name',
45 | 'type' => 'varchar',
46 | 'nulls' => false,
47 | 'default' => null,
48 | 'length' => '255',
49 | ],
50 | ],
51 | 'primary_key' => [
52 | 'PRIMARY' => [
53 | 'columns' => [
54 | 0 => 'id',
55 | ],
56 | ],
57 | ],
58 | 'unique_keys' => [
59 | ],
60 | 'foreign_keys' => [
61 | ],
62 | 'indices' => [
63 | ],
64 | 'auto_increment' => true,
65 | ],
66 | 'roles' => [
67 | 'schema' => '',
68 | 'name' => 'roles',
69 | 'columns' => [
70 | 'id' => [
71 | 'name' => 'id',
72 | 'type' => 'int',
73 | 'nulls' => false,
74 | 'default' => null,
75 | 'length' => null,
76 | ],
77 | 'name' => [
78 | 'name' => 'name',
79 | 'type' => 'varchar',
80 | 'nulls' => false,
81 | 'default' => null,
82 | 'length' => '255',
83 | ],
84 | ],
85 | 'primary_key' => [
86 | 'PRIMARY' => [
87 | 'columns' => [
88 | 0 => 'id',
89 | ],
90 | ],
91 | ],
92 | 'unique_keys' => [
93 | 'name' => [
94 | 'columns' => [
95 | 0 => 'name',
96 | ],
97 | ],
98 | ],
99 | 'foreign_keys' => [
100 | ],
101 | 'indices' => [
102 | ],
103 | 'auto_increment' => true,
104 | ],
105 | 'users' => [
106 | 'schema' => '',
107 | 'name' => 'users',
108 | 'columns' => [
109 | 'email' => [
110 | 'name' => 'email',
111 | 'type' => 'varchar',
112 | 'nulls' => false,
113 | 'default' => null,
114 | 'length' => '255',
115 | ],
116 | 'firstname' => [
117 | 'name' => 'firstname',
118 | 'type' => 'varchar',
119 | 'nulls' => false,
120 | 'default' => null,
121 | 'length' => '255',
122 | ],
123 | 'id' => [
124 | 'name' => 'id',
125 | 'type' => 'int',
126 | 'nulls' => false,
127 | 'default' => null,
128 | 'length' => null,
129 | ],
130 | 'is_admin' => [
131 | 'name' => 'is_admin',
132 | 'type' => 'tinyint',
133 | 'nulls' => true,
134 | 'default' => null,
135 | 'length' => null,
136 | ],
137 | 'lastname' => [
138 | 'name' => 'lastname',
139 | 'type' => 'varchar',
140 | 'nulls' => false,
141 | 'default' => null,
142 | 'length' => '255',
143 | ],
144 | 'last_login_time' => [
145 | 'name' => 'last_login_time',
146 | 'type' => 'timestamp',
147 | 'nulls' => true,
148 | 'default' => null,
149 | 'length' => null,
150 | ],
151 | 'office' => [
152 | 'name' => 'office',
153 | 'type' => 'int',
154 | 'nulls' => true,
155 | 'default' => null,
156 | 'length' => null,
157 | ],
158 | 'othernames' => [
159 | 'name' => 'othernames',
160 | 'type' => 'varchar',
161 | 'nulls' => true,
162 | 'default' => 'None',
163 | 'length' => 255,
164 | ],
165 | 'password' => [
166 | 'name' => 'password',
167 | 'type' => 'varchar',
168 | 'nulls' => false,
169 | 'default' => null,
170 | 'length' => '255',
171 | ],
172 | 'phone' => [
173 | 'name' => 'phone',
174 | 'type' => 'varchar',
175 | 'nulls' => true,
176 | 'default' => null,
177 | 'length' => '64',
178 | ],
179 | 'role_id' => [
180 | 'name' => 'role_id',
181 | 'type' => 'int',
182 | 'nulls' => false,
183 | 'default' => null,
184 | 'length' => null,
185 | ],
186 | 'status' => [
187 | 'name' => 'status',
188 | 'type' => 'int',
189 | 'nulls' => false,
190 | 'default' => '2',
191 | 'length' => null,
192 | ],
193 | 'username' => [
194 | 'name' => 'username',
195 | 'type' => 'varchar',
196 | 'nulls' => false,
197 | 'default' => null,
198 | 'length' => '255',
199 | ],
200 | ],
201 | 'primary_key' => [
202 | 'PRIMARY' => [
203 | 'columns' => [
204 | 0 => 'id',
205 | ],
206 | ],
207 | ],
208 | 'unique_keys' => [
209 | ],
210 | 'foreign_keys' => [
211 | 'user_role_fk' => [
212 | 'schema' => '',
213 | 'table' => 'users',
214 | 'columns' => [
215 | 0 => 'role_id',
216 | ],
217 | 'foreign_table' => 'roles',
218 | 'foreign_schema' => '',
219 | 'foreign_columns' => [
220 | 0 => 'id',
221 | ],
222 | 'on_update' => 'NO ACTION',
223 | 'on_delete' => 'NO ACTION',
224 | ],
225 | ],
226 | 'indices' => [
227 | ],
228 | 'auto_increment' => true,
229 | ]
230 | ]
231 | ]
232 | ],
233 | 'tables' => [
234 | 'departments' => [
235 | 'schema' => '',
236 | 'name' => 'departments',
237 | 'columns' => [
238 | 'id' => [
239 | 'name' => 'id',
240 | 'type' => 'int',
241 | 'nulls' => false,
242 | 'default' => null,
243 | 'length' => null,
244 | ],
245 | 'name' => [
246 | 'name' => 'name',
247 | 'type' => 'varchar',
248 | 'nulls' => false,
249 | 'default' => null,
250 | 'length' => '255',
251 | ],
252 | ],
253 | 'primary_key' => [
254 | 'PRIMARY' => [
255 | 'columns' => [
256 | 0 => 'id',
257 | ],
258 | ],
259 | ],
260 | 'unique_keys' => [
261 | ],
262 | 'foreign_keys' => [
263 | ],
264 | 'indices' => [
265 | ],
266 | 'auto_increment' => true,
267 | ],
268 | 'roles' => [
269 | 'schema' => '',
270 | 'name' => 'roles',
271 | 'columns' => [
272 | 'id' => [
273 | 'name' => 'id',
274 | 'type' => 'int',
275 | 'nulls' => false,
276 | 'default' => null,
277 | 'length' => null,
278 | ],
279 | 'name' => [
280 | 'name' => 'name',
281 | 'type' => 'varchar',
282 | 'nulls' => false,
283 | 'default' => null,
284 | 'length' => '255',
285 | ],
286 | ],
287 | 'primary_key' => [
288 | 'PRIMARY' => [
289 | 'columns' => [
290 | 0 => 'id',
291 | ],
292 | ],
293 | ],
294 | 'unique_keys' => [
295 | 'name' => [
296 | 'columns' => [
297 | 0 => 'name',
298 | ],
299 | ],
300 | ],
301 | 'foreign_keys' => [
302 | ],
303 | 'indices' => [
304 | ],
305 | 'auto_increment' => true,
306 | ],
307 | 'users' => [
308 | 'schema' => '',
309 | 'name' => 'users',
310 | 'columns' => [
311 | 'email' => [
312 | 'name' => 'email',
313 | 'type' => 'varchar',
314 | 'nulls' => false,
315 | 'default' => null,
316 | 'length' => '255',
317 | ],
318 | 'firstname' => [
319 | 'name' => 'firstname',
320 | 'type' => 'varchar',
321 | 'nulls' => false,
322 | 'default' => null,
323 | 'length' => '255',
324 | ],
325 | 'id' => [
326 | 'name' => 'id',
327 | 'type' => 'int',
328 | 'nulls' => false,
329 | 'default' => null,
330 | 'length' => null,
331 | ],
332 | 'is_admin' => [
333 | 'name' => 'is_admin',
334 | 'type' => 'tinyint',
335 | 'nulls' => true,
336 | 'default' => null,
337 | 'length' => null,
338 | ],
339 | 'lastname' => [
340 | 'name' => 'lastname',
341 | 'type' => 'varchar',
342 | 'nulls' => false,
343 | 'default' => null,
344 | 'length' => '255',
345 | ],
346 | 'last_login_time' => [
347 | 'name' => 'last_login_time',
348 | 'type' => 'timestamp',
349 | 'nulls' => true,
350 | 'default' => null,
351 | 'length' => null,
352 | ],
353 | 'office' => [
354 | 'name' => 'office',
355 | 'type' => 'int',
356 | 'nulls' => true,
357 | 'default' => null,
358 | 'length' => null,
359 | ],
360 | 'othernames' => [
361 | 'name' => 'othernames',
362 | 'type' => 'varchar',
363 | 'nulls' => true,
364 | 'default' => 'None',
365 | 'length' => 255,
366 | ],
367 | 'password' => [
368 | 'name' => 'password',
369 | 'type' => 'varchar',
370 | 'nulls' => false,
371 | 'default' => null,
372 | 'length' => '255',
373 | ],
374 | 'phone' => [
375 | 'name' => 'phone',
376 | 'type' => 'varchar',
377 | 'nulls' => true,
378 | 'default' => null,
379 | 'length' => '64',
380 | ],
381 | 'role_id' => [
382 | 'name' => 'role_id',
383 | 'type' => 'int',
384 | 'nulls' => false,
385 | 'default' => null,
386 | 'length' => null,
387 | ],
388 | 'status' => [
389 | 'name' => 'status',
390 | 'type' => 'int',
391 | 'nulls' => false,
392 | 'default' => '2',
393 | 'length' => null,
394 | ],
395 | 'username' => [
396 | 'name' => 'username',
397 | 'type' => 'varchar',
398 | 'nulls' => false,
399 | 'default' => null,
400 | 'length' => '255',
401 | ],
402 | ],
403 | 'primary_key' => [
404 | 'PRIMARY' => [
405 | 'columns' => [
406 | 0 => 'id',
407 | ],
408 | ],
409 | ],
410 | 'unique_keys' => [
411 | ],
412 | 'foreign_keys' => [
413 | 'user_role_fk' => [
414 | 'schema' => '',
415 | 'table' => 'users',
416 | 'columns' => [
417 | 0 => 'role_id',
418 | ],
419 | 'foreign_table' => 'roles',
420 | 'foreign_schema' => '',
421 | 'foreign_columns' => [
422 | 0 => 'id',
423 | ],
424 | 'on_update' => 'NO ACTION',
425 | 'on_delete' => 'NO ACTION',
426 | ],
427 | ],
428 | 'indices' => [
429 | ],
430 | 'auto_increment' => true,
431 | ],
432 | ],
433 | ];
434 |
--------------------------------------------------------------------------------
/tests/expected/mysql/database_description_clean_defaults.php:
--------------------------------------------------------------------------------
1 | [
29 | 'atiaa_test' => [
30 | 'name' => 'atiaa_test',
31 | 'tables' => [
32 | 'departments' => [
33 | 'schema' => '',
34 | 'name' => 'departments',
35 | 'columns' => [
36 | 'id' => [
37 | 'name' => 'id',
38 | 'type' => 'int',
39 | 'nulls' => false,
40 | 'default' => null,
41 | 'length' => null,
42 | ],
43 | 'name' => [
44 | 'name' => 'name',
45 | 'type' => 'varchar',
46 | 'nulls' => false,
47 | 'default' => null,
48 | 'length' => '255',
49 | ],
50 | ],
51 | 'primary_key' => [
52 | 'PRIMARY' => [
53 | 'columns' => [
54 | 0 => 'id',
55 | ],
56 | ],
57 | ],
58 | 'unique_keys' => [
59 | ],
60 | 'foreign_keys' => [
61 | ],
62 | 'indices' => [
63 | ],
64 | 'auto_increment' => true,
65 | ],
66 | 'roles' => [
67 | 'schema' => '',
68 | 'name' => 'roles',
69 | 'columns' => [
70 | 'id' => [
71 | 'name' => 'id',
72 | 'type' => 'int',
73 | 'nulls' => false,
74 | 'default' => null,
75 | 'length' => null,
76 | ],
77 | 'name' => [
78 | 'name' => 'name',
79 | 'type' => 'varchar',
80 | 'nulls' => false,
81 | 'default' => null,
82 | 'length' => '255',
83 | ],
84 | ],
85 | 'primary_key' => [
86 | 'PRIMARY' => [
87 | 'columns' => [
88 | 0 => 'id',
89 | ],
90 | ],
91 | ],
92 | 'unique_keys' => [
93 | 'name' => [
94 | 'columns' => [
95 | 0 => 'name',
96 | ],
97 | ],
98 | ],
99 | 'foreign_keys' => [
100 | ],
101 | 'indices' => [
102 | ],
103 | 'auto_increment' => true,
104 | ],
105 | 'users' => [
106 | 'schema' => '',
107 | 'name' => 'users',
108 | 'columns' => [
109 | 'email' => [
110 | 'name' => 'email',
111 | 'type' => 'varchar',
112 | 'nulls' => false,
113 | 'default' => null,
114 | 'length' => '255',
115 | ],
116 | 'firstname' => [
117 | 'name' => 'firstname',
118 | 'type' => 'varchar',
119 | 'nulls' => false,
120 | 'default' => null,
121 | 'length' => '255',
122 | ],
123 | 'id' => [
124 | 'name' => 'id',
125 | 'type' => 'int',
126 | 'nulls' => false,
127 | 'default' => null,
128 | 'length' => null,
129 | ],
130 | 'is_admin' => [
131 | 'name' => 'is_admin',
132 | 'type' => 'tinyint',
133 | 'nulls' => true,
134 | 'default' => null,
135 | 'length' => null,
136 | ],
137 | 'lastname' => [
138 | 'name' => 'lastname',
139 | 'type' => 'varchar',
140 | 'nulls' => false,
141 | 'default' => null,
142 | 'length' => '255',
143 | ],
144 | 'last_login_time' => [
145 | 'name' => 'last_login_time',
146 | 'type' => 'timestamp',
147 | 'nulls' => true,
148 | 'default' => null,
149 | 'length' => null,
150 | ],
151 | 'office' => [
152 | 'name' => 'office',
153 | 'type' => 'int',
154 | 'nulls' => true,
155 | 'default' => null,
156 | 'length' => null,
157 | ],
158 | 'othernames' => [
159 | 'name' => 'othernames',
160 | 'type' => 'varchar',
161 | 'nulls' => true,
162 | 'default' => 'None',
163 | 'length' => 255,
164 | ],
165 | 'password' => [
166 | 'name' => 'password',
167 | 'type' => 'varchar',
168 | 'nulls' => false,
169 | 'default' => null,
170 | 'length' => '255',
171 | ],
172 | 'phone' => [
173 | 'name' => 'phone',
174 | 'type' => 'varchar',
175 | 'nulls' => true,
176 | 'default' => null,
177 | 'length' => '64',
178 | ],
179 | 'role_id' => [
180 | 'name' => 'role_id',
181 | 'type' => 'int',
182 | 'nulls' => false,
183 | 'default' => null,
184 | 'length' => null,
185 | ],
186 | 'status' => [
187 | 'name' => 'status',
188 | 'type' => 'int',
189 | 'nulls' => false,
190 | 'default' => '2',
191 | 'length' => null,
192 | ],
193 | 'username' => [
194 | 'name' => 'username',
195 | 'type' => 'varchar',
196 | 'nulls' => false,
197 | 'default' => null,
198 | 'length' => '255',
199 | ],
200 | ],
201 | 'primary_key' => [
202 | 'PRIMARY' => [
203 | 'columns' => [
204 | 0 => 'id',
205 | ],
206 | ],
207 | ],
208 | 'unique_keys' => [
209 | ],
210 | 'foreign_keys' => [
211 | 'user_role_fk' => [
212 | 'schema' => '',
213 | 'table' => 'users',
214 | 'columns' => [
215 | 0 => 'role_id',
216 | ],
217 | 'foreign_table' => 'roles',
218 | 'foreign_schema' => '',
219 | 'foreign_columns' => [
220 | 0 => 'id',
221 | ],
222 | 'on_update' => 'NO ACTION',
223 | 'on_delete' => 'NO ACTION',
224 | ],
225 | ],
226 | 'indices' => [
227 | ],
228 | 'auto_increment' => true,
229 | ]
230 | ]
231 | ]
232 | ],
233 | 'tables' => [
234 | 'departments' => [
235 | 'schema' => '',
236 | 'name' => 'departments',
237 | 'columns' => [
238 | 'id' => [
239 | 'name' => 'id',
240 | 'type' => 'int',
241 | 'nulls' => false,
242 | 'default' => null,
243 | 'length' => null,
244 | ],
245 | 'name' => [
246 | 'name' => 'name',
247 | 'type' => 'varchar',
248 | 'nulls' => false,
249 | 'default' => null,
250 | 'length' => '255',
251 | ],
252 | ],
253 | 'primary_key' => [
254 | 'PRIMARY' => [
255 | 'columns' => [
256 | 0 => 'id',
257 | ],
258 | ],
259 | ],
260 | 'unique_keys' => [
261 | ],
262 | 'foreign_keys' => [
263 | ],
264 | 'indices' => [
265 | ],
266 | 'auto_increment' => true,
267 | ],
268 | 'roles' => [
269 | 'schema' => '',
270 | 'name' => 'roles',
271 | 'columns' => [
272 | 'id' => [
273 | 'name' => 'id',
274 | 'type' => 'int',
275 | 'nulls' => false,
276 | 'default' => null,
277 | 'length' => null,
278 | ],
279 | 'name' => [
280 | 'name' => 'name',
281 | 'type' => 'varchar',
282 | 'nulls' => false,
283 | 'default' => null,
284 | 'length' => '255',
285 | ],
286 | ],
287 | 'primary_key' => [
288 | 'PRIMARY' => [
289 | 'columns' => [
290 | 0 => 'id',
291 | ],
292 | ],
293 | ],
294 | 'unique_keys' => [
295 | 'name' => [
296 | 'columns' => [
297 | 0 => 'name',
298 | ],
299 | ],
300 | ],
301 | 'foreign_keys' => [
302 | ],
303 | 'indices' => [
304 | ],
305 | 'auto_increment' => true,
306 | ],
307 | 'users' => [
308 | 'schema' => '',
309 | 'name' => 'users',
310 | 'columns' => [
311 | 'email' => [
312 | 'name' => 'email',
313 | 'type' => 'varchar',
314 | 'nulls' => false,
315 | 'default' => null,
316 | 'length' => '255',
317 | ],
318 | 'firstname' => [
319 | 'name' => 'firstname',
320 | 'type' => 'varchar',
321 | 'nulls' => false,
322 | 'default' => null,
323 | 'length' => '255',
324 | ],
325 | 'id' => [
326 | 'name' => 'id',
327 | 'type' => 'int',
328 | 'nulls' => false,
329 | 'default' => null,
330 | 'length' => null,
331 | ],
332 | 'is_admin' => [
333 | 'name' => 'is_admin',
334 | 'type' => 'tinyint',
335 | 'nulls' => true,
336 | 'default' => null,
337 | 'length' => null,
338 | ],
339 | 'lastname' => [
340 | 'name' => 'lastname',
341 | 'type' => 'varchar',
342 | 'nulls' => false,
343 | 'default' => null,
344 | 'length' => '255',
345 | ],
346 | 'last_login_time' => [
347 | 'name' => 'last_login_time',
348 | 'type' => 'timestamp',
349 | 'nulls' => true,
350 | 'default' => null,
351 | 'length' => null,
352 | ],
353 | 'office' => [
354 | 'name' => 'office',
355 | 'type' => 'int',
356 | 'nulls' => true,
357 | 'default' => null,
358 | 'length' => null,
359 | ],
360 | 'othernames' => [
361 | 'name' => 'othernames',
362 | 'type' => 'varchar',
363 | 'nulls' => true,
364 | 'default' => 'None',
365 | 'length' => 255,
366 | ],
367 | 'password' => [
368 | 'name' => 'password',
369 | 'type' => 'varchar',
370 | 'nulls' => false,
371 | 'default' => null,
372 | 'length' => '255',
373 | ],
374 | 'phone' => [
375 | 'name' => 'phone',
376 | 'type' => 'varchar',
377 | 'nulls' => true,
378 | 'default' => null,
379 | 'length' => '64',
380 | ],
381 | 'role_id' => [
382 | 'name' => 'role_id',
383 | 'type' => 'int',
384 | 'nulls' => false,
385 | 'default' => null,
386 | 'length' => null,
387 | ],
388 | 'status' => [
389 | 'name' => 'status',
390 | 'type' => 'int',
391 | 'nulls' => false,
392 | 'default' => '2',
393 | 'length' => null,
394 | ],
395 | 'username' => [
396 | 'name' => 'username',
397 | 'type' => 'varchar',
398 | 'nulls' => false,
399 | 'default' => null,
400 | 'length' => '255',
401 | ],
402 | ],
403 | 'primary_key' => [
404 | 'PRIMARY' => [
405 | 'columns' => [
406 | 0 => 'id',
407 | ],
408 | ],
409 | ],
410 | 'unique_keys' => [
411 | ],
412 | 'foreign_keys' => [
413 | 'user_role_fk' => [
414 | 'schema' => '',
415 | 'table' => 'users',
416 | 'columns' => [
417 | 0 => 'role_id',
418 | ],
419 | 'foreign_table' => 'roles',
420 | 'foreign_schema' => '',
421 | 'foreign_columns' => [
422 | 0 => 'id',
423 | ],
424 | 'on_update' => 'NO ACTION',
425 | 'on_delete' => 'NO ACTION',
426 | ],
427 | ],
428 | 'indices' => [
429 | ],
430 | 'auto_increment' => true,
431 | ],
432 | ],
433 | ];
434 |
--------------------------------------------------------------------------------
/tests/expected/sqlite/database_description.php:
--------------------------------------------------------------------------------
1 | [
29 | 'main' => [
30 | 'name' => 'main',
31 | 'tables' => [
32 | 'departments' => [
33 | 'name' => 'departments',
34 | 'schema' => '',
35 | 'columns' => [
36 | 'id' => [
37 | 'name' => 'id',
38 | 'type' => 'INTEGER',
39 | 'nulls' => true,
40 | 'default' => null,
41 | 'length' => null,
42 | ],
43 | 'name' => [
44 | 'name' => 'name',
45 | 'type' => 'TEXT',
46 | 'nulls' => false,
47 | 'default' => null,
48 | 'length' => null,
49 | ],
50 | ],
51 | 'primary_key' => [
52 | 'departments_pk' => [
53 | 'order' => '1',
54 | 'columns' => [
55 | 0 => 'id',
56 | ],
57 | ],
58 | ],
59 | 'unique_keys' => [
60 | ],
61 | 'foreign_keys' => [
62 | ],
63 | 'indices' => [
64 | ],
65 | 'auto_increment' => true,
66 | ],
67 | 'roles' => [
68 | 'name' => 'roles',
69 | 'schema' => '',
70 | 'columns' => [
71 | 'id' => [
72 | 'name' => 'id',
73 | 'type' => 'INTEGER',
74 | 'nulls' => true,
75 | 'default' => null,
76 | 'length' => null,
77 | ],
78 | 'name' => [
79 | 'name' => 'name',
80 | 'type' => 'TEXT',
81 | 'nulls' => false,
82 | 'default' => null,
83 | 'length' => null,
84 | ],
85 | ],
86 | 'primary_key' => [
87 | 'roles_pk' => [
88 | 'order' => '1',
89 | 'columns' => [
90 | 0 => 'id',
91 | ],
92 | ],
93 | ],
94 | 'unique_keys' => [
95 | ],
96 | 'foreign_keys' => [
97 | ],
98 | 'indices' => [
99 | ],
100 | 'auto_increment' => true,
101 | ],
102 | 'users' => [
103 | 'name' => 'users',
104 | 'schema' => '',
105 | 'columns' => [
106 | 'id' => [
107 | 'name' => 'id',
108 | 'type' => 'INTEGER',
109 | 'nulls' => false,
110 | 'default' => null,
111 | 'length' => null,
112 | ],
113 | 'username' => [
114 | 'name' => 'username',
115 | 'type' => 'TEXT',
116 | 'nulls' => false,
117 | 'default' => null,
118 | 'length' => '255',
119 | ],
120 | 'password' => [
121 | 'name' => 'password',
122 | 'type' => 'TEXT',
123 | 'nulls' => false,
124 | 'default' => null,
125 | 'length' => null,
126 | ],
127 | 'role_id' => [
128 | 'name' => 'role_id',
129 | 'type' => 'INTEGER',
130 | 'nulls' => false,
131 | 'default' => null,
132 | 'length' => null,
133 | ],
134 | 'firstname' => [
135 | 'name' => 'firstname',
136 | 'type' => 'TEXT',
137 | 'nulls' => false,
138 | 'default' => null,
139 | 'length' => null,
140 | ],
141 | 'lastname' => [
142 | 'name' => 'lastname',
143 | 'type' => 'TEXT',
144 | 'nulls' => false,
145 | 'default' => null,
146 | 'length' => null,
147 | ],
148 | 'othernames' => [
149 | 'name' => 'othernames',
150 | 'type' => 'TEXT',
151 | 'nulls' => true,
152 | 'default' => '\'None\'',
153 | 'length' => null,
154 | ],
155 | 'status' => [
156 | 'name' => 'status',
157 | 'type' => 'INTEGER',
158 | 'nulls' => true,
159 | 'default' => '\'2\'',
160 | 'length' => null,
161 | ],
162 | 'email' => [
163 | 'name' => 'email',
164 | 'type' => 'TEXT',
165 | 'nulls' => true,
166 | 'default' => null,
167 | 'length' => null,
168 | ],
169 | 'phone' => [
170 | 'name' => 'phone',
171 | 'type' => 'TEXT',
172 | 'nulls' => true,
173 | 'default' => null,
174 | 'length' => null,
175 | ],
176 | 'office' => [
177 | 'name' => 'office',
178 | 'type' => 'INTEGER',
179 | 'nulls' => false,
180 | 'default' => null,
181 | 'length' => null,
182 | ],
183 | 'last_login_time' => [
184 | 'name' => 'last_login_time',
185 | 'type' => 'TEXT',
186 | 'nulls' => true,
187 | 'default' => null,
188 | 'length' => null,
189 | ],
190 | 'is_admin' => [
191 | 'name' => 'is_admin',
192 | 'type' => 'INTEGER',
193 | 'nulls' => true,
194 | 'default' => null,
195 | 'length' => null,
196 | ],
197 | ],
198 | 'primary_key' => [
199 | 'users_pk' => [
200 | 'order' => '1',
201 | 'columns' => [
202 | 0 => 'id',
203 | ],
204 | ],
205 | ],
206 | 'unique_keys' => [
207 | 'sqlite_autoindex_users_1' => [
208 | 'columns' => [
209 | 0 => 'username',
210 | ],
211 | 'schema' => '',
212 | ],
213 | ],
214 | 'foreign_keys' => [
215 | 'users_departments_0_fk' => [
216 | 'schema' => '',
217 | 'table' => 'users',
218 | 'columns' => [
219 | 0 => 'office',
220 | ],
221 | 'foreign_table' => 'departments',
222 | 'foreign_schema' => '',
223 | 'foreign_columns' => [
224 | 0 => 'id',
225 | ],
226 | 'on_update' => 'CASCADE',
227 | 'on_delete' => 'CASCADE',
228 | ],
229 | 'users_roles_1_fk' => [
230 | 'schema' => '',
231 | 'table' => 'users',
232 | 'columns' => [
233 | 0 => 'role_id',
234 | ],
235 | 'foreign_table' => 'roles',
236 | 'foreign_schema' => '',
237 | 'foreign_columns' => [
238 | 0 => 'id',
239 | ],
240 | 'on_update' => 'CASCADE',
241 | 'on_delete' => 'CASCADE',
242 | ],
243 | ],
244 | 'indices' => [
245 | 'user_email_idx' => [
246 | 'columns' => [
247 | 0 => 'email',
248 | ],
249 | 'schema' => '',
250 | ],
251 | ],
252 | 'auto_increment' => true,
253 | ],
254 | ]
255 | ]
256 | ],
257 | 'tables' => [
258 | 'departments' => [
259 | 'name' => 'departments',
260 | 'schema' => '',
261 | 'columns' => [
262 | 'id' => [
263 | 'name' => 'id',
264 | 'type' => 'INTEGER',
265 | 'nulls' => true,
266 | 'default' => null,
267 | 'length' => null,
268 | ],
269 | 'name' => [
270 | 'name' => 'name',
271 | 'type' => 'TEXT',
272 | 'nulls' => false,
273 | 'default' => null,
274 | 'length' => null,
275 | ],
276 | ],
277 | 'primary_key' => [
278 | 'departments_pk' => [
279 | 'order' => '1',
280 | 'columns' => [
281 | 0 => 'id',
282 | ],
283 | ],
284 | ],
285 | 'unique_keys' => [
286 | ],
287 | 'foreign_keys' => [
288 | ],
289 | 'indices' => [
290 | ],
291 | 'auto_increment' => true,
292 | ],
293 | 'roles' => [
294 | 'name' => 'roles',
295 | 'schema' => '',
296 | 'columns' => [
297 | 'id' => [
298 | 'name' => 'id',
299 | 'type' => 'INTEGER',
300 | 'nulls' => true,
301 | 'default' => null,
302 | 'length' => null,
303 | ],
304 | 'name' => [
305 | 'name' => 'name',
306 | 'type' => 'TEXT',
307 | 'nulls' => false,
308 | 'default' => null,
309 | 'length' => null,
310 | ],
311 | ],
312 | 'primary_key' => [
313 | 'roles_pk' => [
314 | 'order' => '1',
315 | 'columns' => [
316 | 0 => 'id',
317 | ],
318 | ],
319 | ],
320 | 'unique_keys' => [
321 | ],
322 | 'foreign_keys' => [
323 | ],
324 | 'indices' => [
325 | ],
326 | 'auto_increment' => true,
327 | ],
328 | 'users' => [
329 | 'name' => 'users',
330 | 'schema' => '',
331 | 'columns' => [
332 | 'id' => [
333 | 'name' => 'id',
334 | 'type' => 'INTEGER',
335 | 'nulls' => false,
336 | 'default' => null,
337 | 'length' => null,
338 | ],
339 | 'username' => [
340 | 'name' => 'username',
341 | 'type' => 'TEXT',
342 | 'nulls' => false,
343 | 'default' => null,
344 | 'length' => '255',
345 | ],
346 | 'password' => [
347 | 'name' => 'password',
348 | 'type' => 'TEXT',
349 | 'nulls' => false,
350 | 'default' => null,
351 | 'length' => null,
352 | ],
353 | 'role_id' => [
354 | 'name' => 'role_id',
355 | 'type' => 'INTEGER',
356 | 'nulls' => false,
357 | 'default' => null,
358 | 'length' => null,
359 | ],
360 | 'firstname' => [
361 | 'name' => 'firstname',
362 | 'type' => 'TEXT',
363 | 'nulls' => false,
364 | 'default' => null,
365 | 'length' => null,
366 | ],
367 | 'lastname' => [
368 | 'name' => 'lastname',
369 | 'type' => 'TEXT',
370 | 'nulls' => false,
371 | 'default' => null,
372 | 'length' => null,
373 | ],
374 | 'othernames' => [
375 | 'name' => 'othernames',
376 | 'type' => 'TEXT',
377 | 'nulls' => true,
378 | 'default' => '\'None\'',
379 | 'length' => null,
380 | ],
381 | 'status' => [
382 | 'name' => 'status',
383 | 'type' => 'INTEGER',
384 | 'nulls' => true,
385 | 'default' => '\'2\'',
386 | 'length' => null,
387 | ],
388 | 'email' => [
389 | 'name' => 'email',
390 | 'type' => 'TEXT',
391 | 'nulls' => true,
392 | 'default' => null,
393 | 'length' => null,
394 | ],
395 | 'phone' => [
396 | 'name' => 'phone',
397 | 'type' => 'TEXT',
398 | 'nulls' => true,
399 | 'default' => null,
400 | 'length' => null,
401 | ],
402 | 'office' => [
403 | 'name' => 'office',
404 | 'type' => 'INTEGER',
405 | 'nulls' => false,
406 | 'default' => null,
407 | 'length' => null,
408 | ],
409 | 'last_login_time' => [
410 | 'name' => 'last_login_time',
411 | 'type' => 'TEXT',
412 | 'nulls' => true,
413 | 'default' => null,
414 | 'length' => null,
415 | ],
416 | 'is_admin' => [
417 | 'name' => 'is_admin',
418 | 'type' => 'INTEGER',
419 | 'nulls' => true,
420 | 'default' => null,
421 | 'length' => null,
422 | ],
423 | ],
424 | 'primary_key' => [
425 | 'users_pk' => [
426 | 'order' => '1',
427 | 'columns' => [
428 | 0 => 'id',
429 | ],
430 | ],
431 | ],
432 | 'unique_keys' => [
433 | 'sqlite_autoindex_users_1' => [
434 | 'columns' => [
435 | 0 => 'username',
436 | ],
437 | 'schema' => '',
438 | ],
439 | ],
440 | 'foreign_keys' => [
441 | 'users_departments_0_fk' => [
442 | 'schema' => '',
443 | 'table' => 'users',
444 | 'columns' => [
445 | 0 => 'office',
446 | ],
447 | 'foreign_table' => 'departments',
448 | 'foreign_schema' => '',
449 | 'foreign_columns' => [
450 | 0 => 'id',
451 | ],
452 | 'on_update' => 'CASCADE',
453 | 'on_delete' => 'CASCADE',
454 | ],
455 | 'users_roles_1_fk' => [
456 | 'schema' => '',
457 | 'table' => 'users',
458 | 'columns' => [
459 | 0 => 'role_id',
460 | ],
461 | 'foreign_table' => 'roles',
462 | 'foreign_schema' => '',
463 | 'foreign_columns' => [
464 | 0 => 'id',
465 | ],
466 | 'on_update' => 'CASCADE',
467 | 'on_delete' => 'CASCADE',
468 | ],
469 | ],
470 | 'indices' => [
471 | 'user_email_idx' => [
472 | 'columns' => [
473 | 0 => 'email',
474 | ],
475 | 'schema' => '',
476 | ],
477 | ],
478 | 'auto_increment' => true,
479 | ],
480 | ],
481 | ];
482 |
--------------------------------------------------------------------------------
/tests/expected/sqlite/database_description_clean_defaults.php:
--------------------------------------------------------------------------------
1 | [
29 | 'main' => [
30 | 'name' => 'main',
31 | 'tables' => [
32 | 'departments' => [
33 | 'name' => 'departments',
34 | 'schema' => '',
35 | 'columns' => [
36 | 'id' => [
37 | 'name' => 'id',
38 | 'type' => 'INTEGER',
39 | 'nulls' => true,
40 | 'default' => null,
41 | 'length' => null,
42 | ],
43 | 'name' => [
44 | 'name' => 'name',
45 | 'type' => 'TEXT',
46 | 'nulls' => false,
47 | 'default' => null,
48 | 'length' => null,
49 | ],
50 | ],
51 | 'primary_key' => [
52 | 'departments_pk' => [
53 | 'order' => '1',
54 | 'columns' => [
55 | 0 => 'id',
56 | ],
57 | ],
58 | ],
59 | 'unique_keys' => [
60 | ],
61 | 'foreign_keys' => [
62 | ],
63 | 'indices' => [
64 | ],
65 | 'auto_increment' => true,
66 | ],
67 | 'roles' => [
68 | 'name' => 'roles',
69 | 'schema' => '',
70 | 'columns' => [
71 | 'id' => [
72 | 'name' => 'id',
73 | 'type' => 'INTEGER',
74 | 'nulls' => true,
75 | 'default' => null,
76 | 'length' => null,
77 | ],
78 | 'name' => [
79 | 'name' => 'name',
80 | 'type' => 'TEXT',
81 | 'nulls' => false,
82 | 'default' => null,
83 | 'length' => null,
84 | ],
85 | ],
86 | 'primary_key' => [
87 | 'roles_pk' => [
88 | 'order' => '1',
89 | 'columns' => [
90 | 0 => 'id',
91 | ],
92 | ],
93 | ],
94 | 'unique_keys' => [
95 | ],
96 | 'foreign_keys' => [
97 | ],
98 | 'indices' => [
99 | ],
100 | 'auto_increment' => true,
101 | ],
102 | 'users' => [
103 | 'name' => 'users',
104 | 'schema' => '',
105 | 'columns' => [
106 | 'id' => [
107 | 'name' => 'id',
108 | 'type' => 'INTEGER',
109 | 'nulls' => false,
110 | 'default' => null,
111 | 'length' => null,
112 | ],
113 | 'username' => [
114 | 'name' => 'username',
115 | 'type' => 'TEXT',
116 | 'nulls' => false,
117 | 'default' => null,
118 | 'length' => '255',
119 | ],
120 | 'password' => [
121 | 'name' => 'password',
122 | 'type' => 'TEXT',
123 | 'nulls' => false,
124 | 'default' => null,
125 | 'length' => null,
126 | ],
127 | 'role_id' => [
128 | 'name' => 'role_id',
129 | 'type' => 'INTEGER',
130 | 'nulls' => false,
131 | 'default' => null,
132 | 'length' => null,
133 | ],
134 | 'firstname' => [
135 | 'name' => 'firstname',
136 | 'type' => 'TEXT',
137 | 'nulls' => false,
138 | 'default' => null,
139 | 'length' => null,
140 | ],
141 | 'lastname' => [
142 | 'name' => 'lastname',
143 | 'type' => 'TEXT',
144 | 'nulls' => false,
145 | 'default' => null,
146 | 'length' => null,
147 | ],
148 | 'othernames' => [
149 | 'name' => 'othernames',
150 | 'type' => 'TEXT',
151 | 'nulls' => true,
152 | 'default' => 'None',
153 | 'length' => null,
154 | ],
155 | 'status' => [
156 | 'name' => 'status',
157 | 'type' => 'INTEGER',
158 | 'nulls' => true,
159 | 'default' => '2',
160 | 'length' => null,
161 | ],
162 | 'email' => [
163 | 'name' => 'email',
164 | 'type' => 'TEXT',
165 | 'nulls' => true,
166 | 'default' => null,
167 | 'length' => null,
168 | ],
169 | 'phone' => [
170 | 'name' => 'phone',
171 | 'type' => 'TEXT',
172 | 'nulls' => true,
173 | 'default' => null,
174 | 'length' => null,
175 | ],
176 | 'office' => [
177 | 'name' => 'office',
178 | 'type' => 'INTEGER',
179 | 'nulls' => false,
180 | 'default' => null,
181 | 'length' => null,
182 | ],
183 | 'last_login_time' => [
184 | 'name' => 'last_login_time',
185 | 'type' => 'TEXT',
186 | 'nulls' => true,
187 | 'default' => null,
188 | 'length' => null,
189 | ],
190 | 'is_admin' => [
191 | 'name' => 'is_admin',
192 | 'type' => 'INTEGER',
193 | 'nulls' => true,
194 | 'default' => null,
195 | 'length' => null,
196 | ],
197 | ],
198 | 'primary_key' => [
199 | 'users_pk' => [
200 | 'order' => '1',
201 | 'columns' => [
202 | 0 => 'id',
203 | ],
204 | ],
205 | ],
206 | 'unique_keys' => [
207 | 'sqlite_autoindex_users_1' => [
208 | 'columns' => [
209 | 0 => 'username',
210 | ],
211 | 'schema' => '',
212 | ],
213 | ],
214 | 'foreign_keys' => [
215 | 'users_departments_0_fk' => [
216 | 'schema' => '',
217 | 'table' => 'users',
218 | 'columns' => [
219 | 0 => 'office',
220 | ],
221 | 'foreign_table' => 'departments',
222 | 'foreign_schema' => '',
223 | 'foreign_columns' => [
224 | 0 => 'id',
225 | ],
226 | 'on_update' => 'CASCADE',
227 | 'on_delete' => 'CASCADE',
228 | ],
229 | 'users_roles_1_fk' => [
230 | 'schema' => '',
231 | 'table' => 'users',
232 | 'columns' => [
233 | 0 => 'role_id',
234 | ],
235 | 'foreign_table' => 'roles',
236 | 'foreign_schema' => '',
237 | 'foreign_columns' => [
238 | 0 => 'id',
239 | ],
240 | 'on_update' => 'CASCADE',
241 | 'on_delete' => 'CASCADE',
242 | ],
243 | ],
244 | 'indices' => [
245 | 'user_email_idx' => [
246 | 'columns' => [
247 | 0 => 'email',
248 | ],
249 | 'schema' => '',
250 | ],
251 | ],
252 | 'auto_increment' => true,
253 | ],
254 | ]
255 | ]
256 | ],
257 | 'tables' => [
258 | 'departments' => [
259 | 'name' => 'departments',
260 | 'schema' => '',
261 | 'columns' => [
262 | 'id' => [
263 | 'name' => 'id',
264 | 'type' => 'INTEGER',
265 | 'nulls' => true,
266 | 'default' => null,
267 | 'length' => null,
268 | ],
269 | 'name' => [
270 | 'name' => 'name',
271 | 'type' => 'TEXT',
272 | 'nulls' => false,
273 | 'default' => null,
274 | 'length' => null,
275 | ],
276 | ],
277 | 'primary_key' => [
278 | 'departments_pk' => [
279 | 'order' => '1',
280 | 'columns' => [
281 | 0 => 'id',
282 | ],
283 | ],
284 | ],
285 | 'unique_keys' => [
286 | ],
287 | 'foreign_keys' => [
288 | ],
289 | 'indices' => [
290 | ],
291 | 'auto_increment' => true,
292 | ],
293 | 'roles' => [
294 | 'name' => 'roles',
295 | 'schema' => '',
296 | 'columns' => [
297 | 'id' => [
298 | 'name' => 'id',
299 | 'type' => 'INTEGER',
300 | 'nulls' => true,
301 | 'default' => null,
302 | 'length' => null,
303 | ],
304 | 'name' => [
305 | 'name' => 'name',
306 | 'type' => 'TEXT',
307 | 'nulls' => false,
308 | 'default' => null,
309 | 'length' => null,
310 | ],
311 | ],
312 | 'primary_key' => [
313 | 'roles_pk' => [
314 | 'order' => '1',
315 | 'columns' => [
316 | 0 => 'id',
317 | ],
318 | ],
319 | ],
320 | 'unique_keys' => [
321 | ],
322 | 'foreign_keys' => [
323 | ],
324 | 'indices' => [
325 | ],
326 | 'auto_increment' => true,
327 | ],
328 | 'users' => [
329 | 'name' => 'users',
330 | 'schema' => '',
331 | 'columns' => [
332 | 'id' => [
333 | 'name' => 'id',
334 | 'type' => 'INTEGER',
335 | 'nulls' => false,
336 | 'default' => null,
337 | 'length' => null,
338 | ],
339 | 'username' => [
340 | 'name' => 'username',
341 | 'type' => 'TEXT',
342 | 'nulls' => false,
343 | 'default' => null,
344 | 'length' => '255',
345 | ],
346 | 'password' => [
347 | 'name' => 'password',
348 | 'type' => 'TEXT',
349 | 'nulls' => false,
350 | 'default' => null,
351 | 'length' => null,
352 | ],
353 | 'role_id' => [
354 | 'name' => 'role_id',
355 | 'type' => 'INTEGER',
356 | 'nulls' => false,
357 | 'default' => null,
358 | 'length' => null,
359 | ],
360 | 'firstname' => [
361 | 'name' => 'firstname',
362 | 'type' => 'TEXT',
363 | 'nulls' => false,
364 | 'default' => null,
365 | 'length' => null,
366 | ],
367 | 'lastname' => [
368 | 'name' => 'lastname',
369 | 'type' => 'TEXT',
370 | 'nulls' => false,
371 | 'default' => null,
372 | 'length' => null,
373 | ],
374 | 'othernames' => [
375 | 'name' => 'othernames',
376 | 'type' => 'TEXT',
377 | 'nulls' => true,
378 | 'default' => 'None',
379 | 'length' => null,
380 | ],
381 | 'status' => [
382 | 'name' => 'status',
383 | 'type' => 'INTEGER',
384 | 'nulls' => true,
385 | 'default' => '2',
386 | 'length' => null,
387 | ],
388 | 'email' => [
389 | 'name' => 'email',
390 | 'type' => 'TEXT',
391 | 'nulls' => true,
392 | 'default' => null,
393 | 'length' => null,
394 | ],
395 | 'phone' => [
396 | 'name' => 'phone',
397 | 'type' => 'TEXT',
398 | 'nulls' => true,
399 | 'default' => null,
400 | 'length' => null,
401 | ],
402 | 'office' => [
403 | 'name' => 'office',
404 | 'type' => 'INTEGER',
405 | 'nulls' => false,
406 | 'default' => null,
407 | 'length' => null,
408 | ],
409 | 'last_login_time' => [
410 | 'name' => 'last_login_time',
411 | 'type' => 'TEXT',
412 | 'nulls' => true,
413 | 'default' => null,
414 | 'length' => null,
415 | ],
416 | 'is_admin' => [
417 | 'name' => 'is_admin',
418 | 'type' => 'INTEGER',
419 | 'nulls' => true,
420 | 'default' => null,
421 | 'length' => null,
422 | ],
423 | ],
424 | 'primary_key' => [
425 | 'users_pk' => [
426 | 'order' => '1',
427 | 'columns' => [
428 | 0 => 'id',
429 | ],
430 | ],
431 | ],
432 | 'unique_keys' => [
433 | 'sqlite_autoindex_users_1' => [
434 | 'columns' => [
435 | 0 => 'username',
436 | ],
437 | 'schema' => '',
438 | ],
439 | ],
440 | 'foreign_keys' => [
441 | 'users_departments_0_fk' => [
442 | 'schema' => '',
443 | 'table' => 'users',
444 | 'columns' => [
445 | 0 => 'office',
446 | ],
447 | 'foreign_table' => 'departments',
448 | 'foreign_schema' => '',
449 | 'foreign_columns' => [
450 | 0 => 'id',
451 | ],
452 | 'on_update' => 'CASCADE',
453 | 'on_delete' => 'CASCADE',
454 | ],
455 | 'users_roles_1_fk' => [
456 | 'schema' => '',
457 | 'table' => 'users',
458 | 'columns' => [
459 | 0 => 'role_id',
460 | ],
461 | 'foreign_table' => 'roles',
462 | 'foreign_schema' => '',
463 | 'foreign_columns' => [
464 | 0 => 'id',
465 | ],
466 | 'on_update' => 'CASCADE',
467 | 'on_delete' => 'CASCADE',
468 | ],
469 | ],
470 | 'indices' => [
471 | 'user_email_idx' => [
472 | 'columns' => [
473 | 0 => 'email',
474 | ],
475 | 'schema' => '',
476 | ],
477 | ],
478 | 'auto_increment' => true,
479 | ],
480 | ],
481 | ];
482 |
--------------------------------------------------------------------------------
/tests/expected/postgresql/database_description_clean_defaults.php:
--------------------------------------------------------------------------------
1 | [
29 | 'public' => [
30 | 'name' => 'public',
31 | 'tables' => [
32 | 'departments' => [
33 | 'schema' => '',
34 | 'name' => 'departments',
35 | 'columns' => [
36 | 'id' => [
37 | 'name' => 'id',
38 | 'type' => 'integer',
39 | 'nulls' => false,
40 | 'default' => null,
41 | 'length' => null,
42 | ],
43 | 'name' => [
44 | 'name' => 'name',
45 | 'type' => 'character varying',
46 | 'nulls' => false,
47 | 'default' => null,
48 | 'length' => 255,
49 | ],
50 | ],
51 | 'primary_key' => [
52 | 'departments_pkey' => [
53 | 'columns' => [
54 | 0 => 'id',
55 | ],
56 | ],
57 | ],
58 | 'unique_keys' => [
59 | ],
60 | 'foreign_keys' => [
61 | ],
62 | 'indices' => [
63 | ],
64 | 'auto_increment' => true,
65 | ],
66 | 'roles' => [
67 | 'schema' => '',
68 | 'name' => 'roles',
69 | 'columns' => [
70 | 'id' => [
71 | 'name' => 'id',
72 | 'type' => 'integer',
73 | 'nulls' => false,
74 | 'default' => null,
75 | 'length' => null,
76 | ],
77 | 'name' => [
78 | 'name' => 'name',
79 | 'type' => 'character varying',
80 | 'nulls' => false,
81 | 'default' => null,
82 | 'length' => 255,
83 | ],
84 | ],
85 | 'primary_key' => [
86 | 'roles_pkey' => [
87 | 'columns' => [
88 | 0 => 'id',
89 | ],
90 | ],
91 | ],
92 | 'unique_keys' => [
93 | ],
94 | 'foreign_keys' => [
95 | ],
96 | 'indices' => [
97 | ],
98 | 'auto_increment' => true,
99 | ],
100 | 'users' => [
101 | 'schema' => '',
102 | 'name' => 'users',
103 | 'columns' => [
104 | 'email' => [
105 | 'name' => 'email',
106 | 'type' => 'character varying',
107 | 'nulls' => false,
108 | 'default' => null,
109 | 'length' => 255,
110 | ],
111 | 'firstname' => [
112 | 'name' => 'firstname',
113 | 'type' => 'character varying',
114 | 'nulls' => false,
115 | 'default' => null,
116 | 'length' => 255,
117 | ],
118 | 'id' => [
119 | 'name' => 'id',
120 | 'type' => 'integer',
121 | 'nulls' => false,
122 | 'default' => null,
123 | 'length' => null,
124 | ],
125 | 'is_admin' => [
126 | 'name' => 'is_admin',
127 | 'type' => 'boolean',
128 | 'nulls' => true,
129 | 'default' => null,
130 | 'length' => null,
131 | ],
132 | 'last_login_time' => [
133 | 'name' => 'last_login_time',
134 | 'type' => 'timestamp without time zone',
135 | 'nulls' => true,
136 | 'default' => null,
137 | 'length' => null,
138 | ],
139 | 'lastname' => [
140 | 'name' => 'lastname',
141 | 'type' => 'character varying',
142 | 'nulls' => false,
143 | 'default' => null,
144 | 'length' => 255,
145 | ],
146 | 'office' => [
147 | 'name' => 'office',
148 | 'type' => 'integer',
149 | 'nulls' => true,
150 | 'default' => null,
151 | 'length' => null,
152 | ],
153 | 'othernames' => [
154 | 'name' => 'othernames',
155 | 'type' => 'character varying',
156 | 'nulls' => true,
157 | 'default' => 'None',
158 | 'length' => 255,
159 | ],
160 | 'password' => [
161 | 'name' => 'password',
162 | 'type' => 'character varying',
163 | 'nulls' => false,
164 | 'default' => null,
165 | 'length' => 255,
166 | ],
167 | 'phone' => [
168 | 'name' => 'phone',
169 | 'type' => 'character varying',
170 | 'nulls' => true,
171 | 'default' => null,
172 | 'length' => 64,
173 | ],
174 | 'role_id' => [
175 | 'name' => 'role_id',
176 | 'type' => 'integer',
177 | 'nulls' => false,
178 | 'default' => null,
179 | 'length' => null,
180 | ],
181 | 'status' => [
182 | 'name' => 'status',
183 | 'type' => 'integer',
184 | 'nulls' => false,
185 | 'default' => '2',
186 | 'length' => null,
187 | ],
188 | 'username' => [
189 | 'name' => 'username',
190 | 'type' => 'character varying',
191 | 'nulls' => false,
192 | 'default' => null,
193 | 'length' => 255,
194 | ],
195 | ],
196 | 'primary_key' => [
197 | 'users_pkey' => [
198 | 'columns' => [
199 | 0 => 'id',
200 | ],
201 | ],
202 | ],
203 | 'unique_keys' => [
204 | ],
205 | 'foreign_keys' => [
206 | 'users_office_fkey' => [
207 | 'schema' => '',
208 | 'table' => 'users',
209 | 'columns' => [
210 | 0 => 'office',
211 | ],
212 | 'foreign_table' => 'departments',
213 | 'foreign_schema' => '',
214 | 'foreign_columns' => [
215 | 0 => 'id',
216 | ],
217 | 'on_update' => 'NO ACTION',
218 | 'on_delete' => 'NO ACTION',
219 | ],
220 | 'users_role_id_fkey' => [
221 | 'schema' => '',
222 | 'table' => 'users',
223 | 'columns' => [
224 | 0 => 'role_id',
225 | ],
226 | 'foreign_table' => 'roles',
227 | 'foreign_schema' => '',
228 | 'foreign_columns' => [
229 | 0 => 'id',
230 | ],
231 | 'on_update' => 'NO ACTION',
232 | 'on_delete' => 'NO ACTION',
233 | ],
234 | ],
235 | 'indices' => [
236 | ],
237 | 'auto_increment' => true,
238 | ]
239 | ]
240 | ],
241 | 'crm' => [
242 | 'name' => 'crm',
243 | 'tables' => [
244 | 'customers' => [
245 | 'schema' => 'crm',
246 | 'name' => 'customers',
247 | 'columns' => [
248 | 'employee_id' => [
249 | 'name' => 'employee_id',
250 | 'type' => 'integer',
251 | 'nulls' => false,
252 | 'default' => null,
253 | 'length' => null,
254 | ],
255 | 'id' => [
256 | 'name' => 'id',
257 | 'type' => 'integer',
258 | 'nulls' => false,
259 | 'default' => null,
260 | 'length' => null,
261 | ],
262 | 'name' => [
263 | 'name' => 'name',
264 | 'type' => 'character varying',
265 | 'nulls' => false,
266 | 'default' => null,
267 | 'length' => 255,
268 | ],
269 | ],
270 | 'primary_key' => [
271 | 'customers_pkey' => [
272 | 'columns' => [
273 | 0 => 'id',
274 | ],
275 | ],
276 | ],
277 | 'unique_keys' => [
278 | ],
279 | 'foreign_keys' => [
280 | 'customers_employee_id_fkey' => [
281 | 'schema' => 'crm',
282 | 'table' => 'customers',
283 | 'columns' => [
284 | 0 => 'employee_id',
285 | ],
286 | 'foreign_table' => 'employees',
287 | 'foreign_schema' => 'hr',
288 | 'foreign_columns' => [
289 | 0 => 'id',
290 | ],
291 | 'on_update' => 'NO ACTION',
292 | 'on_delete' => 'NO ACTION',
293 | ],
294 | ],
295 | 'indices' => [
296 | ],
297 | 'auto_increment' => true,
298 | ],
299 | ],
300 | 'views' => [
301 | ],
302 | ],
303 | 'hr' => [
304 | 'name' => 'hr',
305 | 'tables' => [
306 | 'categories' => [
307 | 'schema' => 'hr',
308 | 'name' => 'categories',
309 | 'columns' => [
310 | 'id' => [
311 | 'name' => 'id',
312 | 'type' => 'integer',
313 | 'nulls' => false,
314 | 'default' => null,
315 | 'length' => null,
316 | ],
317 | 'name' => [
318 | 'name' => 'name',
319 | 'type' => 'character varying',
320 | 'nulls' => false,
321 | 'default' => null,
322 | 'length' => 255,
323 | ],
324 | ],
325 | 'primary_key' => [
326 | 'departments_pkey' => [
327 | 'columns' => [
328 | 0 => 'id',
329 | ],
330 | ],
331 | ],
332 | 'unique_keys' => [
333 | ],
334 | 'foreign_keys' => [
335 | ],
336 | 'indices' => [
337 | ],
338 | 'auto_increment' => true,
339 | ],
340 | 'employees' => [
341 | 'schema' => 'hr',
342 | 'name' => 'employees',
343 | 'columns' => [
344 | 'date_of_birth' => [
345 | 'name' => 'date_of_birth',
346 | 'type' => 'date',
347 | 'nulls' => true,
348 | 'default' => null,
349 | 'length' => null,
350 | ],
351 | 'firstname' => [
352 | 'name' => 'firstname',
353 | 'type' => 'character varying',
354 | 'nulls' => false,
355 | 'default' => null,
356 | 'length' => 255,
357 | ],
358 | 'id' => [
359 | 'name' => 'id',
360 | 'type' => 'integer',
361 | 'nulls' => false,
362 | 'default' => null,
363 | 'length' => null,
364 | ],
365 | 'lastname' => [
366 | 'name' => 'lastname',
367 | 'type' => 'character varying',
368 | 'nulls' => true,
369 | 'default' => null,
370 | 'length' => 255,
371 | ],
372 | ],
373 | 'primary_key' => [
374 | 'employees_pkey' => [
375 | 'columns' => [
376 | 0 => 'id',
377 | ],
378 | ],
379 | ],
380 | 'unique_keys' => [
381 | ],
382 | 'foreign_keys' => [
383 | ],
384 | 'indices' => [
385 | ],
386 | 'auto_increment' => true,
387 | ],
388 | ],
389 | 'views' => [
390 | ],
391 | ],
392 | ],
393 | 'tables' => [
394 | 'departments' => [
395 | 'schema' => '',
396 | 'name' => 'departments',
397 | 'columns' => [
398 | 'id' => [
399 | 'name' => 'id',
400 | 'type' => 'integer',
401 | 'nulls' => false,
402 | 'default' => null,
403 | 'length' => null,
404 | ],
405 | 'name' => [
406 | 'name' => 'name',
407 | 'type' => 'character varying',
408 | 'nulls' => false,
409 | 'default' => null,
410 | 'length' => 255,
411 | ],
412 | ],
413 | 'primary_key' => [
414 | 'departments_pkey' => [
415 | 'columns' => [
416 | 0 => 'id',
417 | ],
418 | ],
419 | ],
420 | 'unique_keys' => [
421 | ],
422 | 'foreign_keys' => [
423 | ],
424 | 'indices' => [
425 | ],
426 | 'auto_increment' => true,
427 | ],
428 | 'roles' => [
429 | 'schema' => '',
430 | 'name' => 'roles',
431 | 'columns' => [
432 | 'id' => [
433 | 'name' => 'id',
434 | 'type' => 'integer',
435 | 'nulls' => false,
436 | 'default' => null,
437 | 'length' => null,
438 | ],
439 | 'name' => [
440 | 'name' => 'name',
441 | 'type' => 'character varying',
442 | 'nulls' => false,
443 | 'default' => null,
444 | 'length' => 255,
445 | ],
446 | ],
447 | 'primary_key' => [
448 | 'roles_pkey' => [
449 | 'columns' => [
450 | 0 => 'id',
451 | ],
452 | ],
453 | ],
454 | 'unique_keys' => [
455 | ],
456 | 'foreign_keys' => [
457 | ],
458 | 'indices' => [
459 | ],
460 | 'auto_increment' => true,
461 | ],
462 | 'users' => [
463 | 'schema' => '',
464 | 'name' => 'users',
465 | 'columns' => [
466 | 'email' => [
467 | 'name' => 'email',
468 | 'type' => 'character varying',
469 | 'nulls' => false,
470 | 'default' => null,
471 | 'length' => 255,
472 | ],
473 | 'firstname' => [
474 | 'name' => 'firstname',
475 | 'type' => 'character varying',
476 | 'nulls' => false,
477 | 'default' => null,
478 | 'length' => 255,
479 | ],
480 | 'id' => [
481 | 'name' => 'id',
482 | 'type' => 'integer',
483 | 'nulls' => false,
484 | 'default' => null,
485 | 'length' => null,
486 | ],
487 | 'is_admin' => [
488 | 'name' => 'is_admin',
489 | 'type' => 'boolean',
490 | 'nulls' => true,
491 | 'default' => null,
492 | 'length' => null,
493 | ],
494 | 'last_login_time' => [
495 | 'name' => 'last_login_time',
496 | 'type' => 'timestamp without time zone',
497 | 'nulls' => true,
498 | 'default' => null,
499 | 'length' => null,
500 | ],
501 | 'lastname' => [
502 | 'name' => 'lastname',
503 | 'type' => 'character varying',
504 | 'nulls' => false,
505 | 'default' => null,
506 | 'length' => 255,
507 | ],
508 | 'office' => [
509 | 'name' => 'office',
510 | 'type' => 'integer',
511 | 'nulls' => true,
512 | 'default' => null,
513 | 'length' => null,
514 | ],
515 | 'othernames' => [
516 | 'name' => 'othernames',
517 | 'type' => 'character varying',
518 | 'nulls' => true,
519 | 'default' => 'None',
520 | 'length' => 255,
521 | ],
522 | 'password' => [
523 | 'name' => 'password',
524 | 'type' => 'character varying',
525 | 'nulls' => false,
526 | 'default' => null,
527 | 'length' => 255,
528 | ],
529 | 'phone' => [
530 | 'name' => 'phone',
531 | 'type' => 'character varying',
532 | 'nulls' => true,
533 | 'default' => null,
534 | 'length' => 64,
535 | ],
536 | 'role_id' => [
537 | 'name' => 'role_id',
538 | 'type' => 'integer',
539 | 'nulls' => false,
540 | 'default' => null,
541 | 'length' => null,
542 | ],
543 | 'status' => [
544 | 'name' => 'status',
545 | 'type' => 'integer',
546 | 'nulls' => false,
547 | 'default' => '2',
548 | 'length' => null,
549 | ],
550 | 'username' => [
551 | 'name' => 'username',
552 | 'type' => 'character varying',
553 | 'nulls' => false,
554 | 'default' => null,
555 | 'length' => 255,
556 | ],
557 | ],
558 | 'primary_key' => [
559 | 'users_pkey' => [
560 | 'columns' => [
561 | 0 => 'id',
562 | ],
563 | ],
564 | ],
565 | 'unique_keys' => [
566 | ],
567 | 'foreign_keys' => [
568 | 'users_office_fkey' => [
569 | 'schema' => '',
570 | 'table' => 'users',
571 | 'columns' => [
572 | 0 => 'office',
573 | ],
574 | 'foreign_table' => 'departments',
575 | 'foreign_schema' => '',
576 | 'foreign_columns' => [
577 | 0 => 'id',
578 | ],
579 | 'on_update' => 'NO ACTION',
580 | 'on_delete' => 'NO ACTION',
581 | ],
582 | 'users_role_id_fkey' => [
583 | 'schema' => '',
584 | 'table' => 'users',
585 | 'columns' => [
586 | 0 => 'role_id',
587 | ],
588 | 'foreign_table' => 'roles',
589 | 'foreign_schema' => '',
590 | 'foreign_columns' => [
591 | 0 => 'id',
592 | ],
593 | 'on_update' => 'NO ACTION',
594 | 'on_delete' => 'NO ACTION',
595 | ],
596 | ],
597 | 'indices' => [
598 | ],
599 | 'auto_increment' => true,
600 | ],
601 | ],
602 | ];
603 |
--------------------------------------------------------------------------------