Skip to content

Commit

Permalink
Include PostgreSQL driver (#56)
Browse files Browse the repository at this point in the history
* Rename standard migration file for testing

* Implement PostgreSQL

* Throw a exception if the error occurred

* Include PostgreSQL description in the README

* To enable PostgreSQL testing

* Remove comments from pgsql.sql

* Refactor to achieve balance with PostgreSQL

* Include PostgreSQL driver

* Run `composer lint` to format the code

* Fix a directory bug
  • Loading branch information
cable8mm authored Mar 22, 2024
1 parent 272a621 commit 9b4bb5d
Show file tree
Hide file tree
Showing 40 changed files with 520 additions and 166 deletions.
4 changes: 4 additions & 0 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Xeed는 기존 데이터베이스 테이블에서 가져온 데이터를 기반
- [x] Laravel을 위한 마이그레이션 생성
- [x] Laravel 다중 및 예약된 열 지원
- [x] Laravel 통합
- [x] MySQL, SQLite 그리고 PostgreSQL 지원

> [!CAUTION]
> PostgreSQL은 Beta 지원이며, 문제가 발생할 경우 깃헙 이슈를 통해서 리포팅 해 주세요.
### 미리보기

Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ We have provided the API Documentation on the web. For more information, please
- [x] Generate migrations for Laravel
- [x] Laravel multi & reserved columns supported
- [x] Laravel integration
- [x] MySQL, SQLite and PostgreSQL supported

> [!CAUTION]
> PostgreSQL support is in beta. If you encounter any issues, please report them via GitHub issues.
### Preview

Expand Down
112 changes: 112 additions & 0 deletions database/xeeds.pgsql.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
DROP TABLE IF EXISTS public.xeeds;
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
CREATE TABLE public.xeeds (
id bigint NOT NULL,
big_integer bigint NOT NULL,
"binary" bytea NOT NULL,
"boolean" boolean NOT NULL,
"char" character(100) NOT NULL,
date_time_tz timestamp(0) with time zone NOT NULL,
date_time timestamp(0) without time zone NOT NULL,
date date NOT NULL,
"decimal" numeric(8, 2) NOT NULL,
double double precision NOT NULL,
enum character varying(255) NOT NULL,
"float" real NOT NULL,
foreign_id bigint NOT NULL,
foreign_ulid character(26) NOT NULL,
foreign_uuid uuid NOT NULL,
geometry public.geometry NOT NULL,
"integer" integer NOT NULL,
ip_address inet NOT NULL,
json json NOT NULL,
jsonb jsonb NOT NULL,
long_text text NOT NULL,
mac_address macaddr NOT NULL,
medium_integer integer NOT NULL,
medium_text text NOT NULL,
morphs_type character varying(255) NOT NULL,
morphs_id bigint NOT NULL,
nullable_morphs_type character varying(255),
nullable_morphs_id bigint,
nullable_ulid_morphs_type character varying(255),
nullable_ulid_morphs_id character(26),
nullable_uuid_morphs_type character varying(255),
nullable_uuid_morphs_id uuid,
remember_token character varying(100),
small_integer smallint NOT NULL,
soft_deletes_tz timestamp(0) with time zone,
soft_deletes timestamp(0) without time zone,
string character varying(100) NOT NULL,
text text NOT NULL,
time_tz time(0) with time zone NOT NULL,
"time" time(0) without time zone NOT NULL,
timestamp_tz timestamp(0) with time zone NOT NULL,
"timestamp" timestamp(0) without time zone NOT NULL,
created_at timestamp(0) without time zone,
updated_at timestamp(0) without time zone,
tiny_integer smallint NOT NULL,
tiny_text character varying(255) NOT NULL,
unsigned_big_integer bigint NOT NULL,
unsigned_integer integer NOT NULL,
unsigned_medium_integer integer NOT NULL,
unsigned_small_integer smallint NOT NULL,
unsigned_tiny_integer smallint NOT NULL,
ulid_morphs_type character varying(255) NOT NULL,
ulid_morphs_id character(26) NOT NULL,
uuid_morphs_type character varying(255) NOT NULL,
uuid_morphs_id uuid NOT NULL,
ulid character(26) NOT NULL,
uuid uuid NOT NULL,
year integer NOT NULL,
CONSTRAINT xeeds_enum_check CHECK (
(
(enum)::text = ANY (
(
ARRAY ['easy'::character varying, 'hard'::character varying]
)::text []
)
)
)
);
CREATE SEQUENCE public.xeeds_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
ALTER SEQUENCE public.xeeds_id_seq OWNED BY public.xeeds.id;
ALTER TABLE ONLY public.xeeds
ALTER COLUMN id
SET DEFAULT nextval('public.xeeds_id_seq'::regclass);
ALTER TABLE ONLY public.xeeds
ADD CONSTRAINT xeeds_pkey PRIMARY KEY (id);
CREATE INDEX xeeds_morphs_type_morphs_id_index ON public.xeeds USING btree (morphs_type, morphs_id);
CREATE INDEX xeeds_nullable_morphs_type_nullable_morphs_id_index ON public.xeeds USING btree (nullable_morphs_type, nullable_morphs_id);
CREATE INDEX xeeds_nullable_ulid_morphs_type_nullable_ulid_morphs_id_index ON public.xeeds USING btree (
nullable_ulid_morphs_type,
nullable_ulid_morphs_id
);
CREATE INDEX xeeds_nullable_uuid_morphs_type_nullable_uuid_morphs_id_index ON public.xeeds USING btree (
nullable_uuid_morphs_type,
nullable_uuid_morphs_id
);
CREATE INDEX xeeds_ulid_morphs_type_ulid_morphs_id_index ON public.xeeds USING btree (ulid_morphs_type, ulid_morphs_id);
CREATE INDEX xeeds_uuid_morphs_type_uuid_morphs_id_index ON public.xeeds USING btree (uuid_morphs_type, uuid_morphs_id);
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SELECT pg_catalog.setval('public.migrations_id_seq', 1, true);
8 changes: 8 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
<php>
<env name="DB_CONNECTION" value="sqlite" />
<env name="DB_DATABASE" value="tests/Generate/database.sqlite" />

<!-- <env name="DB_CONNECTION" value="pgsql" />
<env name="DB_HOST" value="127.0.0.1" />
<env name="DB_PORT" value="5432" />
<env name="DB_DATABASE" value="xeed" />
<env name="DB_USERNAME" value="postgres" />
<env name="DB_PASSWORD" value="" /> -->

<!-- <env name="DB_CONNECTION" value="mysql" />
<env name="DB_HOST" value="127.0.0.1" />
<env name="DB_PORT" value="3306" />
Expand Down
2 changes: 1 addition & 1 deletion src/Command/GenerateDatabaseSeederCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
DatabaseSeederGenerator::make($tables)->run(force: $force);

$output->writeln('<info>'.Path::seeder().DIRECTORY_SEPARATOR.'DatabaseSeeder.php seeder have been generated.'.'</info>');
} catch (\Exception $e) {
} catch (\RuntimeException $e) {
$output->writeln('<error>'.Path::seeder().DIRECTORY_SEPARATOR.'DatabaseSeeder.php seeder file already exists.'.'</error>');
}

Expand Down
2 changes: 1 addition & 1 deletion src/Command/GenerateFactoriesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
FactoryGenerator::make($table)->run(force: $force);

$output->writeln('<info>'.Path::factory().DIRECTORY_SEPARATOR.$table->factory().'.php have been generated.'.'</info>');
} catch (\Exception $e) {
} catch (\RuntimeException $e) {
$output->writeln('<error>'.Path::factory().DIRECTORY_SEPARATOR.$table->factory().'.php file already exists.'.'<error>');
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/GenerateMigrationsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
)->run(force: $force);

$output->writeln('<info>'.Path::migration().DIRECTORY_SEPARATOR.$table->migration().' has been generated.'.'</info>');
} catch (\Exception $e) {
} catch (\RuntimeException $e) {
$output->writeln('<error>'.Path::migration().DIRECTORY_SEPARATOR.$table->migration().'.php file already exists.'.'</error>');
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/GenerateModelsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
ModelGenerator::make($table)->run(force: $force);

$output->writeln('<info>'.Path::model().DIRECTORY_SEPARATOR.$table->model().'.php have been generated.'.'</info>');
} catch (\Exception $e) {
} catch (\RuntimeException $e) {
$output->writeln('<error>'.Path::model().DIRECTORY_SEPARATOR.$table->model().'.php file already exists.'.'</error>');
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/GenerateSeedersCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
SeederGenerator::make($table)->run(force: $force);

$output->writeln('<info>'.Path::seeder().DIRECTORY_SEPARATOR.$table->seeder().'.php have been generated.'.'</info>');
} catch (\Exception $e) {
} catch (\RuntimeException $e) {
$output->writeln('<error>'.Path::seeder().DIRECTORY_SEPARATOR.$table->seeder().'.php file already exists.'.'</error>');
}
}
Expand Down
28 changes: 19 additions & 9 deletions src/Command/ImportXeedCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,30 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($argument === 'drop' || $argument === 'refresh') {
$sql = 'DROP TABLE IF EXISTS '.self::TABLE_NAME;

$xeed->pdo->exec($sql);

$output->writeln('`'.self::TABLE_NAME.'` table was dropped.');
if ($xeed->pdo->exec($sql) !== false) {
$output->writeln('`'.self::TABLE_NAME.'` table was successfully dropped.');
} else {
$output->writeln('`'.self::TABLE_NAME.'` table failed to drop.');
}
}

if ($argument === 'import' || $argument === 'refresh') {
$filename = Path::database().DIRECTORY_SEPARATOR.self::TABLE_NAME.'.'.$xeed->driver.'.sql';

$sql = File::system()->read($filename);

$xeed->pdo->exec($sql);

$output->writeln($filename.' was imported.');

switch ($xeed->driver) {
case 'pgsql':
$sql = File::system()->readSql($filename);
break;
default:
$sql = File::system()->read($filename);
break;
}

if ($xeed->pdo->exec($sql) !== false) {
$output->writeln($filename.' was imported.');
} else {
$output->writeln($filename.' wasn\'t imported.');
}
}

$output->writeln('<info>import-xeed</info> command executed successfully.');
Expand Down
81 changes: 81 additions & 0 deletions src/Provider/PgsqlProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Cable8mm\Xeed\Provider;

use Cable8mm\Xeed\Column;
use Cable8mm\Xeed\Interfaces\ProviderInterface;
use Cable8mm\Xeed\Table;
use Cable8mm\Xeed\Xeed;
use PDO;

/**
* PostgreSQL provider can help to retrieve data from mysql database and marshalling between another fields for PostgreSQL.
*/
final class PgsqlProvider implements ProviderInterface
{
/**
* {@inheritDoc}
*/
public function attach(Xeed $xeed): void
{
$tables = $xeed->pdo->query('SELECT table_name FROM information_schema.tables WHERE table_schema = \'public\' ORDER BY table_name')->fetchAll(PDO::FETCH_COLUMN);

$tables = array_diff($tables, [
'geography_columns',
'geometry_columns',
'spatial_ref_sys',
]);

foreach ($tables as $table) {
$columns = $xeed->pdo->query('SELECT * FROM information_schema.columns WHERE table_schema = \'public\' AND table_name = \''.$table.'\'')->fetchAll(PDO::FETCH_ASSOC);

try {
$primaryKeys = $xeed->pdo->query('SELECT a.attname AS column_name FROM pg_index i JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey) WHERE i.indrelid = \''.$table.'\'::regclass AND i.indisprimary')->fetchAll(PDO::FETCH_COLUMN);
} catch (\PDOException $e) {
$primaryKeys = [];
}

$tableColumns = array_map(
function (array $column) use ($primaryKeys) {

$column['primary_key'] = in_array($column['column_name'], $primaryKeys);

return new Column(...self::map($column));
},
$columns
);

$xeed[$table] = new Table($table, $tableColumns);
}
}

/**
* {@inheritDoc}
*/
public static function map(array $column, ?string $table = null, ?Xeed $xeed = null): array
{
$bracket = ! empty($column['numeric_precision']) ? '('.$column['numeric_precision'].', '.$column['numeric_precision_radix'].')' : null;

$primaryKey = $column['primary_key'];

$autoIncrement = isset($column['column_default']) ? str_contains($column['column_default'], 'nextval') : false;

$notNull = $column['is_nullable'] === 'NO';

$unsigned = false;

$type = $column['data_type'];

return [
'field' => $column['column_name'],
'type' => $type,
'unsigned' => $unsigned,
'autoIncrement' => $autoIncrement,
'notNull' => $notNull,
'primaryKey' => $primaryKey,
'bracket' => $bracket,
'default' => $column['column_default'],
'extra' => null,
];
}
}
Loading

0 comments on commit 9b4bb5d

Please sign in to comment.