From 9bb10843598eac35e95fb04bb665fe8a087c6e76 Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:02:00 +0200
Subject: [PATCH 01/16] style: show the table stereotype and demote Markdown
headings
Render the <
> stereotype in the PlantUML output (stop hiding
stereotypes) so tables are labelled, and shift the Markdown template
down one heading level (## Tables, ### table, #### sections) so the
output embeds cleanly under a document title.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
src/Template/Markdown/v1.twig | 17 ++++++++---------
src/Template/Plantuml/v1.twig | 4 +---
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/src/Template/Markdown/v1.twig b/src/Template/Markdown/v1.twig
index 704dc6e..0df940a 100644
--- a/src/Template/Markdown/v1.twig
+++ b/src/Template/Markdown/v1.twig
@@ -1,9 +1,8 @@
-# Schema
+## Tables
{% for table in tables %}
+### `{{ table.name }}` table
-## `{{ table.name }}` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -12,7 +11,7 @@
{% endfor -%}
{% if table.primaryKey %}
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
@@ -20,7 +19,7 @@
{% endif -%}
{% if table.simpleIndexes|length %}
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
@@ -30,7 +29,7 @@
{% endif -%}
{% if table.uniqueIndexes|length %}
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
@@ -40,7 +39,7 @@
{% endif -%}
{% if table.fulltextIndexes|length %}
-### Fulltext Indexes
+#### Fulltext Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
@@ -50,7 +49,7 @@
{% endif -%}
{% if table.spatialIndexes|length %}
-### Spatial Indexes
+#### Spatial Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
diff --git a/src/Template/Plantuml/v1.twig b/src/Template/Plantuml/v1.twig
index 643fd11..f314a4c 100644
--- a/src/Template/Plantuml/v1.twig
+++ b/src/Template/Plantuml/v1.twig
@@ -15,7 +15,7 @@ skinparam class {
}
[\n]
!procedure $table($name)
-class "$name" << (T, TABLE_BORDER_COLOR ) >>
+class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
!endprocedure
[\n]
!function $key_columns($columns, $otherParameters="")
@@ -77,8 +77,6 @@ $from "n" }-- "1" $to
!endprocedure
[\n]
hide methods
-hide stereotypes
-[\n]
skinparam linetype polyline
left to right direction
[\n]
From b287bb17dbbf40bf1c0ceb596770cfc917b37ac7 Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:04:00 +0200
Subject: [PATCH 02/16] docs: regenerate example outputs for the updated
templates
Regenerate the .puml and .md outputs for sakila and airportdb from the
updated PlantUML and Markdown templates, and align the Markdown template
test fixtures with the new heading levels.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
example/output/airportdb/airportdb.md | 128 +++++++---------
example/output/airportdb/airportdb.puml | 4 +-
example/output/sakila/sakila.md | 142 ++++++++----------
example/output/sakila/sakila.puml | 4 +-
.../Unit/Template/Markdown/V1TemplateTest.php | 55 +++----
5 files changed, 147 insertions(+), 186 deletions(-)
diff --git a/example/output/airportdb/airportdb.md b/example/output/airportdb/airportdb.md
index a2ef931..8ab7e90 100644
--- a/example/output/airportdb/airportdb.md
+++ b/example/output/airportdb/airportdb.md
@@ -1,8 +1,7 @@
-# Schema
+## Tables
+### `airline` table
-## `airline` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -11,27 +10,26 @@
| airlinename | varchar ( 30 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
| base_airport | smallint | NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| airline_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| base_airport_idx | base_airport | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| iata_unq | iata | - |
+### `airplane` table
-## `airplane` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -40,21 +38,20 @@
| type_id | int | NOT NULL | - |
| airline_id | int | NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| airplane_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| type_id | type_id | - |
+### `airplane_type` table
-## `airplane_type` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -62,21 +59,20 @@
| identifier | varchar ( 50 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
| description | text | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| type_id | - |
-### Fulltext Indexes
+#### Fulltext Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| description_full | identifier, description | - |
+### `airport` table
-## `airport` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -85,28 +81,27 @@
| icao | char ( 4 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL | - |
| name | varchar ( 50 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| airport_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| name_idx | name | - |
| iata_idx | iata | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| icao_unq | icao | - |
+### `airport_geo` table
-## `airport_geo` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -118,36 +113,34 @@
| longitude | decimal ( 11, 8 ) | NOT NULL | - |
| geolocation | point | NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| airport_id | - |
-### Spatial Indexes
+#### Spatial Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| geolocation_spt | geolocation | - |
+### `airport_reachable` table
-## `airport_reachable` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
| airport_id | smallint | NOT NULL | - |
| hops | int | DEFAULT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| airport_id | - |
+### `booking` table
-## `booking` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -157,28 +150,27 @@
| passenger_id | int | NOT NULL | - |
| price | decimal ( 10, 2 ) | NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| booking_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| flight_idx | flight_id | - |
| passenger_idx | passenger_id | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| seatplan_unq | flight_id, seat | - |
+### `employee` table
-## `employee` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -198,21 +190,20 @@
| password | char ( 32 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
| department | enum ( Marketing, Buchhaltung, Management, Logistik, Flugfeld ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| employee_id | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| user_unq | username | - |
+### `flight` table
-## `flight` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -225,13 +216,13 @@
| airline_id | smallint | NOT NULL | - |
| airplane_id | int | NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| flight_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
@@ -242,10 +233,9 @@
| airline_idx | airline_id | - |
| airplane_idx | airplane_id | - |
| flightno | flightno | - |
+### `flight_log` table
-## `flight_log` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -269,21 +259,20 @@
| airline_id_new | smallint | NOT NULL | - |
| comment | varchar ( 200 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| flight_log_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| flight_log_ibfk_1 | flight_id | - |
+### `flightschedule` table
-## `flightschedule` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -301,23 +290,22 @@
| saturday | tinyint ( 1 ) | DEFAULT '0' | - |
| sunday | tinyint ( 1 ) | DEFAULT '0' | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| flightno | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| from_idx | from | - |
| to_idx | to | - |
| airline_idx | airline_id | - |
+### `passenger` table
-## `passenger` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -326,21 +314,20 @@
| firstname | varchar ( 100 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL | - |
| lastname | varchar ( 100 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| passenger_id | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| pass_unq | passportno | - |
+### `passengerdetails` table
-## `passengerdetails` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -354,15 +341,14 @@
| emailaddress | varchar ( 120 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
| telephoneno | varchar ( 30 ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| passenger_id | - |
+### `weatherdata` table
-## `weatherdata` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -376,7 +362,7 @@
| winddirection | smallint | NOT NULL | - |
| weather | enum ( Nebel-Schneefall, Schneefall, Regen, Regen-Schneefall, Nebel-Regen, Nebel-Regen-Gewitter, Gewitter, Nebel, Regen-Gewitter ) | CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
diff --git a/example/output/airportdb/airportdb.puml b/example/output/airportdb/airportdb.puml
index 63f792a..6de25a8 100644
--- a/example/output/airportdb/airportdb.puml
+++ b/example/output/airportdb/airportdb.puml
@@ -15,7 +15,7 @@ skinparam class {
}
!procedure $table($name)
-class "$name" << (T, TABLE_BORDER_COLOR ) >>
+class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
!endprocedure
!function $key_columns($columns, $otherParameters="")
@@ -77,8 +77,6 @@ $from "n" }-- "1" $to
!endprocedure
hide methods
-hide stereotypes
-
skinparam linetype polyline
left to right direction
diff --git a/example/output/sakila/sakila.md b/example/output/sakila/sakila.md
index 1fa6607..40d0e46 100644
--- a/example/output/sakila/sakila.md
+++ b/example/output/sakila/sakila.md
@@ -1,8 +1,7 @@
-# Schema
+## Tables
+### `actor` table
-## `actor` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -11,21 +10,20 @@
| last_name | VARCHAR ( 45 ) | NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| actor_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_actor_last_name | last_name | - |
+### `address` table
-## `address` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -38,21 +36,20 @@
| phone | VARCHAR ( 20 ) | NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| address_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_city_id | city_id | - |
+### `category` table
-## `category` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -60,15 +57,14 @@
| name | VARCHAR ( 25 ) | NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| category_id | - |
+### `city` table
-## `city` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -77,21 +73,20 @@
| country_id | SMALLINT | UNSIGNED NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| city_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_country_id | country_id | - |
+### `country` table
-## `country` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -99,15 +94,14 @@
| country | VARCHAR ( 50 ) | NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| country_id | - |
+### `customer` table
-## `customer` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -121,23 +115,22 @@
| create_date | DATETIME | NOT NULL | - |
| last_update | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| customer_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_store_id | store_id | - |
| idx_fk_address_id | address_id | - |
| idx_last_name | last_name | - |
+### `film` table
-## `film` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -155,23 +148,22 @@
| rating | ENUM ( G, PG, PG-13, R, NC-17 ) | DEFAULT 'G' | - |
| special_features | SET ( Trailers, Commentaries, Deleted Scenes, Behind the Scenes ) | DEFAULT NULL | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| film_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_title | title | - |
| idx_fk_language_id | language_id | - |
| idx_fk_original_language_id | original_language_id | - |
+### `film_actor` table
-## `film_actor` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -179,21 +171,20 @@
| film_id | SMALLINT | UNSIGNED NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| actor_id, film_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_film_id | film_id | - |
+### `film_category` table
-## `film_category` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -201,15 +192,14 @@
| category_id | TINYINT | UNSIGNED NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| film_id, category_id | - |
+### `film_text` table
-## `film_text` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -217,21 +207,20 @@
| title | VARCHAR ( 255 ) | NOT NULL | - |
| description | TEXT | - | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| film_id | - |
-### Fulltext Indexes
+#### Fulltext Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_title_description | title, description | - |
+### `inventory` table
-## `inventory` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -240,22 +229,21 @@
| store_id | TINYINT | UNSIGNED NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| inventory_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_film_id | film_id | - |
| idx_store_id_film_id | store_id, film_id | - |
+### `language` table
-## `language` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -263,15 +251,14 @@
| name | CHAR ( 20 ) | NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| language_id | - |
+### `payment` table
-## `payment` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -283,22 +270,21 @@
| payment_date | DATETIME | NOT NULL | - |
| last_update | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| payment_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_staff_id | staff_id | - |
| idx_fk_customer_id | customer_id | - |
+### `rental` table
-## `rental` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -310,13 +296,13 @@
| staff_id | TINYINT | UNSIGNED NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| rental_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
@@ -324,15 +310,14 @@
| idx_fk_customer_id | customer_id | - |
| idx_fk_staff_id | staff_id | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| | rental_date, inventory_id, customer_id | - |
+### `staff` table
-## `staff` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -348,22 +333,21 @@
| password | VARCHAR ( 40 ) | BINARY DEFAULT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| staff_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_store_id | store_id | - |
| idx_fk_address_id | address_id | - |
+### `store` table
-## `store` table
-
-### Columns
+#### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -372,19 +356,19 @@
| address_id | SMALLINT | UNSIGNED NOT NULL | - |
| last_update | TIMESTAMP | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | - |
-### Primary Key
+#### Primary Key
| Columns | Parameters |
| --- | --- |
| store_id | - |
-### Indexes
+#### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_fk_address_id | address_id | - |
-### Unique Indexes
+#### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
diff --git a/example/output/sakila/sakila.puml b/example/output/sakila/sakila.puml
index 6b7e957..458e904 100644
--- a/example/output/sakila/sakila.puml
+++ b/example/output/sakila/sakila.puml
@@ -15,7 +15,7 @@ skinparam class {
}
!procedure $table($name)
-class "$name" << (T, TABLE_BORDER_COLOR ) >>
+class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
!endprocedure
!function $key_columns($columns, $otherParameters="")
@@ -77,8 +77,6 @@ $from "n" }-- "1" $to
!endprocedure
hide methods
-hide stereotypes
-
skinparam linetype polyline
left to right direction
diff --git a/test/Unit/Template/Markdown/V1TemplateTest.php b/test/Unit/Template/Markdown/V1TemplateTest.php
index fd45d63..6da6369 100644
--- a/test/Unit/Template/Markdown/V1TemplateTest.php
+++ b/test/Unit/Template/Markdown/V1TemplateTest.php
@@ -42,11 +42,10 @@ public static function getSchemaProvider(): array
)
),
<<<'MD'
- # Schema
+ ## Tables
+ ### `actor` table
- ## `actor` table
-
- ### Columns
+ #### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -89,25 +88,23 @@ public static function getSchemaProvider(): array
->addConnection(new OneToOneConnection('member_data', 'member', ['member_id'], ['id']))
->addConnection(new OneToManyConnection('member_log', 'member', ['member_id'], ['id'])),
<<<'MD'
- # Schema
-
- ## `member` table
+ ## Tables
+ ### `member` table
- ### Columns
+ #### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
| id | INT ( 10 ) | NOT NULL DEFAULT | - |
- ### Primary Key
+ #### Primary Key
| Columns | Parameters |
| --- | --- |
| id | - |
+ ### `member_data` table
- ## `member_data` table
-
- ### Columns
+ #### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -116,28 +113,27 @@ public static function getSchemaProvider(): array
| type | VARCHAR ( 64 ) | NOT NULL | - |
| status | ENUM ( enabled, deleted ) | DEFAULT NULL | - |
- ### Primary Key
+ #### Primary Key
| Columns | Parameters |
| --- | --- |
| id | USING HASH |
- ### Indexes
+ #### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_type | type | - |
| idx_type_status | type, status | USING HASH |
- ### Unique Indexes
+ #### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_member_id | member_id | - |
+ ### `member_log` table
- ## `member_log` table
-
- ### Columns
+ #### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -145,31 +141,31 @@ public static function getSchemaProvider(): array
| member_id | INT ( 10 ) | NOT NULL | - |
| created_at | DATETIME | NOT NULL | - |
- ### Primary Key
+ #### Primary Key
| Columns | Parameters |
| --- | --- |
| id | USING HASH |
- ### Indexes
+ #### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_action | action | - |
- ### Unique Indexes
+ #### Unique Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_member_id | member_id | USING HASH |
- ### Fulltext Indexes
+ #### Fulltext Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_member_id_action | member_id, action | - |
- ### Spatial Indexes
+ #### Spatial Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
@@ -199,11 +195,10 @@ public static function getSchemaProvider(): array
->addSpatialIndex(new SpatialIndex('idx_area', ['area']))
),
<<<'MD'
- # Schema
-
- ## `measurement` table
+ ## Tables
+ ### `measurement` table
- ### Columns
+ #### Columns
| Name | Type | Parameters | Comment |
| --- | --- | --- | --- |
@@ -214,19 +209,19 @@ public static function getSchemaProvider(): array
| embedding | VECTOR ( 3 ) | NOT NULL | - |
| label | VARCHAR ( 64 ) | - | - |
- ### Primary Key
+ #### Primary Key
| Columns | Parameters |
| --- | --- |
| sensor_id, id | - |
- ### Indexes
+ #### Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
| idx_sensor_score_label | sensor_id, score, label | - |
- ### Spatial Indexes
+ #### Spatial Indexes
| Name | Columns | Parameters |
| --- | --- | --- |
From 495d22736770f11804e07a35c33eb68a5bc24bcf Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:06:00 +0200
Subject: [PATCH 03/16] refactor: drive PlantUML template whitespace with Twig
controls
Replace the [\t]/[\n] sentinel markers and the post-render whitespace
regex/strtr in the Plantuml exporter with native Twig whitespace control
(auto-stripped newlines after block tags plus {%- -%} trims) and literal
tabs/blank lines in the template. The rendered output is byte-identical
for both example schemas, so the exporter no longer needs any
post-processing.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
src/Export/Plantuml.php | 12 +--
src/Template/Plantuml/v1.twig | 140 +++++++++++++++++-----------------
2 files changed, 71 insertions(+), 81 deletions(-)
diff --git a/src/Export/Plantuml.php b/src/Export/Plantuml.php
index 79c91c4..34475b7 100644
--- a/src/Export/Plantuml.php
+++ b/src/Export/Plantuml.php
@@ -23,22 +23,12 @@ public function __construct(string $template)
public function export(SchemaInterface $schema): string
{
- $text = $this->twig->render(
+ return $this->twig->render(
'template',
[
'tables' => $schema->tables,
'connections' => $schema->connections,
]
);
-
- $text = preg_replace('/^\s+/m', '', $text);
-
- return strtr(
- $text,
- [
- '[\t]' => "\t",
- '[\n]' => '',
- ]
- );
}
}
diff --git a/src/Template/Plantuml/v1.twig b/src/Template/Plantuml/v1.twig
index f314a4c..ff049d7 100644
--- a/src/Template/Plantuml/v1.twig
+++ b/src/Template/Plantuml/v1.twig
@@ -1,115 +1,115 @@
@startuml
-[\n]
+
!ifndef TABLE_BORDER_COLOR
!define TABLE_BORDER_COLOR Chocolate
!endif
-[\n]
+
!ifndef TABLE_BACKGROUND_COLOR
!define TABLE_BACKGROUND_COLOR AntiqueWhite
!endif
-[\n]
+
skinparam class {
-[\t]BackgroundColor TABLE_BACKGROUND_COLOR
-[\t]BorderColor TABLE_BORDER_COLOR
-[\t]ArrowColor TABLE_BORDER_COLOR
+ BackgroundColor TABLE_BACKGROUND_COLOR
+ BorderColor TABLE_BORDER_COLOR
+ ArrowColor TABLE_BORDER_COLOR
}
-[\n]
+
!procedure $table($name)
class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
!endprocedure
-[\n]
+
!function $key_columns($columns, $otherParameters="")
-[\t]!$result = ""
-[\t]!foreach $column in %splitstr($columns, ", ")
-[\t][\t]!if ($result != "")
-[\t][\t][\t]!$result = $result + ", "
-[\t][\t]!endif
-[\t][\t]!$result = $result + $column
-[\t]!endfor
-[\t]!if ($otherParameters != "")
-[\t][\t]!$result = $result + ", " + $otherParameters
-[\t]!endif
-[\t]!return $result
+ !$result = ""
+ !foreach $column in %splitstr($columns, ", ")
+ !if ($result != "")
+ !$result = $result + ", "
+ !endif
+ !$result = $result + $column
+ !endfor
+ !if ($otherParameters != "")
+ !$result = $result + ", " + $otherParameters
+ !endif
+ !return $result
!endfunction
-[\n]
+
!procedure $primary_key($columns, $otherParameters="")
-[\t]!$rendered = $key_columns($columns, $otherParameters)
+ !$rendered = $key_columns($columns, $otherParameters)
-PK [$rendered]
!endprocedure
-[\n]
+
!procedure $column($name, $type, $otherParameters="", $comment="")
-[\t]!$suffix = ""
-[\t]!if ($otherParameters != "")
-[\t][\t]!$suffix = $suffix + " " + $otherParameters
-[\t]!endif
-[\t]!if ($comment != "")
-[\t][\t]!$suffix = $suffix + " COMMENT '" + $comment + "'"
-[\t]!endif
+ !$suffix = ""
+ !if ($otherParameters != "")
+ !$suffix = $suffix + " " + $otherParameters
+ !endif
+ !if ($comment != "")
+ !$suffix = $suffix + " COMMENT '" + $comment + "'"
+ !endif
+$name $type$suffix
!endprocedure
-[\n]
+
!procedure $index($columns, $otherParameters="")
-[\t]!$rendered = $key_columns($columns, $otherParameters)
+ !$rendered = $key_columns($columns, $otherParameters)
~KEY [$rendered]
!endprocedure
-[\n]
+
!procedure $unique_index($columns, $otherParameters="")
-[\t]!$rendered = $key_columns($columns, $otherParameters)
+ !$rendered = $key_columns($columns, $otherParameters)
#UNIQUE KEY [$rendered]
!endprocedure
-[\n]
+
!procedure $fulltext_index($columns, $otherParameters="")
-[\t]!$rendered = $key_columns($columns, $otherParameters)
+ !$rendered = $key_columns($columns, $otherParameters)
#FULLTEXT KEY [$rendered]
!endprocedure
-[\n]
+
!procedure $spatial_index($columns, $otherParameters="")
-[\t]!$rendered = $key_columns($columns, $otherParameters)
+ !$rendered = $key_columns($columns, $otherParameters)
#SPATIAL KEY [$rendered]
!endprocedure
-[\n]
+
!procedure $connection_one_to_one($from, $to)
$from "1" -- "1" $to
!endprocedure
-[\n]
+
!procedure $connection_one_to_many($from, $to)
$from "n" }-- "1" $to
!endprocedure
-[\n]
+
hide methods
skinparam linetype polyline
left to right direction
-[\n]
+
{% for table in tables %}
- $table({{ table.name }}) {
- {% for column in table.columns -%}
- [\t]$column('{{ column.name }}', '{{ column.type }}{% if column.typeParameters %}[{{ column.typeParameters|join(', ') }}]{% endif %}', '{{ column.otherParameters|replace({"'": '"'})|raw }}', '{{ column.comment|replace({"'": '"'})|raw }}')
- {% endfor -%}
- {% if table.primaryKey %}
- [\t]$primary_key('{{ table.primaryKey.columns|join(', ') }}', '{{ table.primaryKey.otherParameters|replace({"'": '"'})|raw }}')
- {% endif -%}
- {% for simpleIndex in table.simpleIndexes -%}
- [\t]$index('{{ simpleIndex.columns|join(', ') }}', '{{ simpleIndex.otherParameters|replace({"'": '"'})|raw }}')
- {% endfor -%}
- {% for uniqueIndex in table.uniqueIndexes -%}
- [\t]$unique_index('{{ uniqueIndex.columns|join(', ') }}', '{{ uniqueIndex.otherParameters|replace({"'": '"'})|raw }}')
- {% endfor -%}
- {% for fulltextIndex in table.fulltextIndexes -%}
- [\t]$fulltext_index('{{ fulltextIndex.columns|join(', ') }}', '{{ fulltextIndex.otherParameters|replace({"'": '"'})|raw }}')
- {% endfor -%}
- {% for spatialIndex in table.spatialIndexes -%}
- [\t]$spatial_index('{{ spatialIndex.columns|join(', ') }}', '{{ spatialIndex.otherParameters|replace({"'": '"'})|raw }}')
- {% endfor -%}
- }
+$table({{ table.name }}) {
+{% for column in table.columns %}
+ $column('{{ column.name }}', '{{ column.type }}{% if column.typeParameters %}[{{ column.typeParameters|join(', ') }}]{% endif %}', '{{ column.otherParameters|replace({"'": '"'})|raw }}', '{{ column.comment|replace({"'": '"'})|raw }}')
{% endfor %}
-[\n]
-{% for connection in connections %}
- {% if connection.type == "OneToOne" %}
- $connection_one_to_one({{ connection.childTableName }}, {{ connection.parentTableName }})
- {% endif %}
- {% if connection.type == "OneToMany" %}
- $connection_one_to_many({{ connection.childTableName }}, {{ connection.parentTableName }})
- {% endif %}
+{%- if table.primaryKey %}
+ $primary_key('{{ table.primaryKey.columns|join(', ') }}', '{{ table.primaryKey.otherParameters|replace({"'": '"'})|raw }}')
+{% endif %}
+{%- for simpleIndex in table.simpleIndexes %}
+ $index('{{ simpleIndex.columns|join(', ') }}', '{{ simpleIndex.otherParameters|replace({"'": '"'})|raw }}')
+{% endfor %}
+{%- for uniqueIndex in table.uniqueIndexes %}
+ $unique_index('{{ uniqueIndex.columns|join(', ') }}', '{{ uniqueIndex.otherParameters|replace({"'": '"'})|raw }}')
+{% endfor %}
+{%- for fulltextIndex in table.fulltextIndexes %}
+ $fulltext_index('{{ fulltextIndex.columns|join(', ') }}', '{{ fulltextIndex.otherParameters|replace({"'": '"'})|raw }}')
+{% endfor %}
+{%- for spatialIndex in table.spatialIndexes %}
+ $spatial_index('{{ spatialIndex.columns|join(', ') }}', '{{ spatialIndex.otherParameters|replace({"'": '"'})|raw }}')
{% endfor %}
-[\n]
+}
+{% endfor %}
+
+{% for connection in connections %}
+{%- if connection.type == "OneToOne" %}
+$connection_one_to_one({{ connection.childTableName }}, {{ connection.parentTableName }})
+{% endif %}
+{%- if connection.type == "OneToMany" %}
+$connection_one_to_many({{ connection.childTableName }}, {{ connection.parentTableName }})
+{% endif %}
+{%- endfor %}
+
@enduml
From 8400d4ad6abb56f5c3eee669ce87c0edf20d1586 Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:08:00 +0200
Subject: [PATCH 04/16] refactor: use the ?= default-assignment operator for
table colors
Replace the !ifndef/!define guard blocks with the modern preprocessor
!$var ?= "..." conditional-assignment operator and reference the colors
through their $ variables. Users can still override the colors by
defining $table_border_color / $table_background_color beforehand.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
example/output/airportdb/airportdb.puml | 17 ++++++-----------
example/output/sakila/sakila.puml | 17 ++++++-----------
src/Template/Plantuml/v1.twig | 17 ++++++-----------
3 files changed, 18 insertions(+), 33 deletions(-)
diff --git a/example/output/airportdb/airportdb.puml b/example/output/airportdb/airportdb.puml
index 6de25a8..e3f8b5d 100644
--- a/example/output/airportdb/airportdb.puml
+++ b/example/output/airportdb/airportdb.puml
@@ -1,21 +1,16 @@
@startuml
-!ifndef TABLE_BORDER_COLOR
-!define TABLE_BORDER_COLOR Chocolate
-!endif
-
-!ifndef TABLE_BACKGROUND_COLOR
-!define TABLE_BACKGROUND_COLOR AntiqueWhite
-!endif
+!$table_border_color ?= "Chocolate"
+!$table_background_color ?= "AntiqueWhite"
skinparam class {
- BackgroundColor TABLE_BACKGROUND_COLOR
- BorderColor TABLE_BORDER_COLOR
- ArrowColor TABLE_BORDER_COLOR
+ BackgroundColor $table_background_color
+ BorderColor $table_border_color
+ ArrowColor $table_border_color
}
!procedure $table($name)
-class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
+class "$name" <> << (T, $table_border_color ) >>
!endprocedure
!function $key_columns($columns, $otherParameters="")
diff --git a/example/output/sakila/sakila.puml b/example/output/sakila/sakila.puml
index 458e904..061cb74 100644
--- a/example/output/sakila/sakila.puml
+++ b/example/output/sakila/sakila.puml
@@ -1,21 +1,16 @@
@startuml
-!ifndef TABLE_BORDER_COLOR
-!define TABLE_BORDER_COLOR Chocolate
-!endif
-
-!ifndef TABLE_BACKGROUND_COLOR
-!define TABLE_BACKGROUND_COLOR AntiqueWhite
-!endif
+!$table_border_color ?= "Chocolate"
+!$table_background_color ?= "AntiqueWhite"
skinparam class {
- BackgroundColor TABLE_BACKGROUND_COLOR
- BorderColor TABLE_BORDER_COLOR
- ArrowColor TABLE_BORDER_COLOR
+ BackgroundColor $table_background_color
+ BorderColor $table_border_color
+ ArrowColor $table_border_color
}
!procedure $table($name)
-class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
+class "$name" <> << (T, $table_border_color ) >>
!endprocedure
!function $key_columns($columns, $otherParameters="")
diff --git a/src/Template/Plantuml/v1.twig b/src/Template/Plantuml/v1.twig
index ff049d7..cb25cb4 100644
--- a/src/Template/Plantuml/v1.twig
+++ b/src/Template/Plantuml/v1.twig
@@ -1,21 +1,16 @@
@startuml
-!ifndef TABLE_BORDER_COLOR
-!define TABLE_BORDER_COLOR Chocolate
-!endif
-
-!ifndef TABLE_BACKGROUND_COLOR
-!define TABLE_BACKGROUND_COLOR AntiqueWhite
-!endif
+!$table_border_color ?= "Chocolate"
+!$table_background_color ?= "AntiqueWhite"
skinparam class {
- BackgroundColor TABLE_BACKGROUND_COLOR
- BorderColor TABLE_BORDER_COLOR
- ArrowColor TABLE_BORDER_COLOR
+ BackgroundColor $table_background_color
+ BorderColor $table_border_color
+ ArrowColor $table_border_color
}
!procedure $table($name)
-class "$name" <> << (T, TABLE_BORDER_COLOR ) >>
+class "$name" <> << (T, $table_border_color ) >>
!endprocedure
!function $key_columns($columns, $otherParameters="")
From ae50c06f5d5343979c5204b657822b16486e619a Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:10:00 +0200
Subject: [PATCH 05/16] style: indent the connection procedure bodies in the
PlantUML template
Align the $connection_one_to_one / $connection_one_to_many procedure
bodies with the other procedures by tab-indenting them, and regenerate
the example .puml outputs.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
example/output/airportdb/airportdb.puml | 4 ++--
example/output/sakila/sakila.puml | 4 ++--
src/Template/Plantuml/v1.twig | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/example/output/airportdb/airportdb.puml b/example/output/airportdb/airportdb.puml
index e3f8b5d..9826220 100644
--- a/example/output/airportdb/airportdb.puml
+++ b/example/output/airportdb/airportdb.puml
@@ -64,11 +64,11 @@ class "$name" <> << (T, $table_border_color ) >>
!endprocedure
!procedure $connection_one_to_one($from, $to)
-$from "1" -- "1" $to
+ $from "1" -- "1" $to
!endprocedure
!procedure $connection_one_to_many($from, $to)
-$from "n" }-- "1" $to
+ $from "n" }-- "1" $to
!endprocedure
hide methods
diff --git a/example/output/sakila/sakila.puml b/example/output/sakila/sakila.puml
index 061cb74..12a6403 100644
--- a/example/output/sakila/sakila.puml
+++ b/example/output/sakila/sakila.puml
@@ -64,11 +64,11 @@ class "$name" <> << (T, $table_border_color ) >>
!endprocedure
!procedure $connection_one_to_one($from, $to)
-$from "1" -- "1" $to
+ $from "1" -- "1" $to
!endprocedure
!procedure $connection_one_to_many($from, $to)
-$from "n" }-- "1" $to
+ $from "n" }-- "1" $to
!endprocedure
hide methods
diff --git a/src/Template/Plantuml/v1.twig b/src/Template/Plantuml/v1.twig
index cb25cb4..e0b7430 100644
--- a/src/Template/Plantuml/v1.twig
+++ b/src/Template/Plantuml/v1.twig
@@ -64,11 +64,11 @@ class "$name" <> << (T, $table_border_color ) >>
!endprocedure
!procedure $connection_one_to_one($from, $to)
-$from "1" -- "1" $to
+ $from "1" -- "1" $to
!endprocedure
!procedure $connection_one_to_many($from, $to)
-$from "n" }-- "1" $to
+ $from "n" }-- "1" $to
!endprocedure
hide methods
From 7ef45fbadc65b1093f0a0f496f63adf2842af944 Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:12:00 +0200
Subject: [PATCH 06/16] docs: add JSON example outputs for sakila and airportdb
Generate example/output//.json via mysql:json so every bundled
output format (json, plantuml, markdown) has a rendered example.
Co-Authored-By: Claude Opus 4.8 (1M context)
---
example/output/airportdb/airportdb.json | 1458 +++++++++++++++++++++++
example/output/sakila/sakila.json | 1370 +++++++++++++++++++++
2 files changed, 2828 insertions(+)
create mode 100644 example/output/airportdb/airportdb.json
create mode 100644 example/output/sakila/sakila.json
diff --git a/example/output/airportdb/airportdb.json b/example/output/airportdb/airportdb.json
new file mode 100644
index 0000000..38e6159
--- /dev/null
+++ b/example/output/airportdb/airportdb.json
@@ -0,0 +1,1458 @@
+{
+ "tables": {
+ "airline": {
+ "columns": [
+ {
+ "name": "airline_id",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "iata",
+ "type": "char",
+ "typeParameters": [
+ "2"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airlinename",
+ "type": "varchar",
+ "typeParameters": [
+ "30"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "base_airport",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "base_airport"
+ ],
+ "otherParameters": "",
+ "name": "base_airport_idx"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "iata"
+ ],
+ "otherParameters": "",
+ "name": "iata_unq"
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "airline_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "airplane": {
+ "columns": [
+ {
+ "name": "airplane_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "capacity",
+ "type": "mediumint",
+ "typeParameters": [],
+ "otherParameters": "unsigned NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "type_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airline_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "type_id"
+ ],
+ "otherParameters": "",
+ "name": "type_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "airplane_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "airplane_type": {
+ "columns": [
+ {
+ "name": "type_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "identifier",
+ "type": "varchar",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "description",
+ "type": "text",
+ "typeParameters": [],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [
+ {
+ "columns": [
+ "identifier",
+ "description"
+ ],
+ "otherParameters": "",
+ "name": "description_full"
+ }
+ ],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "type_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "airport": {
+ "columns": [
+ {
+ "name": "airport_id",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "iata",
+ "type": "char",
+ "typeParameters": [
+ "3"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "icao",
+ "type": "char",
+ "typeParameters": [
+ "4"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "name",
+ "type": "varchar",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "name"
+ ],
+ "otherParameters": "",
+ "name": "name_idx"
+ },
+ {
+ "columns": [
+ "iata"
+ ],
+ "otherParameters": "",
+ "name": "iata_idx"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "icao"
+ ],
+ "otherParameters": "",
+ "name": "icao_unq"
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "airport_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "airport_geo": {
+ "columns": [
+ {
+ "name": "airport_id",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "name",
+ "type": "varchar",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "city",
+ "type": "varchar",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "country",
+ "type": "varchar",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "latitude",
+ "type": "decimal",
+ "typeParameters": [
+ "11",
+ "8"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "longitude",
+ "type": "decimal",
+ "typeParameters": [
+ "11",
+ "8"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "geolocation",
+ "type": "point",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [
+ {
+ "columns": [
+ "geolocation"
+ ],
+ "otherParameters": "",
+ "name": "geolocation_spt"
+ }
+ ],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "airport_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "airport_reachable": {
+ "columns": [
+ {
+ "name": "airport_id",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "hops",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "airport_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "booking": {
+ "columns": [
+ {
+ "name": "booking_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "flight_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "seat",
+ "type": "char",
+ "typeParameters": [
+ "4"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "passenger_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "price",
+ "type": "decimal",
+ "typeParameters": [
+ "10",
+ "2"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "flight_id"
+ ],
+ "otherParameters": "",
+ "name": "flight_idx"
+ },
+ {
+ "columns": [
+ "passenger_id"
+ ],
+ "otherParameters": "",
+ "name": "passenger_idx"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "flight_id",
+ "seat"
+ ],
+ "otherParameters": "",
+ "name": "seatplan_unq"
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "booking_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "employee": {
+ "columns": [
+ {
+ "name": "employee_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "firstname",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "lastname",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "birthdate",
+ "type": "date",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "sex",
+ "type": "char",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "street",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "city",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "zip",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "country",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "emailaddress",
+ "type": "varchar",
+ "typeParameters": [
+ "120"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "telephoneno",
+ "type": "varchar",
+ "typeParameters": [
+ "30"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "salary",
+ "type": "decimal",
+ "typeParameters": [
+ "8",
+ "2"
+ ],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "username",
+ "type": "varchar",
+ "typeParameters": [
+ "20"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "password",
+ "type": "char",
+ "typeParameters": [
+ "32"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "department",
+ "type": "enum",
+ "typeParameters": [
+ "Marketing",
+ "Buchhaltung",
+ "Management",
+ "Logistik",
+ "Flugfeld"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "username"
+ ],
+ "otherParameters": "",
+ "name": "user_unq"
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "employee_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "flight": {
+ "columns": [
+ {
+ "name": "flight_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "flightno",
+ "type": "char",
+ "typeParameters": [
+ "8"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "from",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "to",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "departure",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "arrival",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airline_id",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airplane_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "from"
+ ],
+ "otherParameters": "",
+ "name": "from_idx"
+ },
+ {
+ "columns": [
+ "to"
+ ],
+ "otherParameters": "",
+ "name": "to_idx"
+ },
+ {
+ "columns": [
+ "departure"
+ ],
+ "otherParameters": "",
+ "name": "departure_idx"
+ },
+ {
+ "columns": [
+ "arrival"
+ ],
+ "otherParameters": "",
+ "name": "arrivals_idx"
+ },
+ {
+ "columns": [
+ "airline_id"
+ ],
+ "otherParameters": "",
+ "name": "airline_idx"
+ },
+ {
+ "columns": [
+ "airplane_id"
+ ],
+ "otherParameters": "",
+ "name": "airplane_idx"
+ },
+ {
+ "columns": [
+ "flightno"
+ ],
+ "otherParameters": "",
+ "name": "flightno"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "flight_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "flight_log": {
+ "columns": [
+ {
+ "name": "flight_log_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "unsigned NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "log_date",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "user",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "flight_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "flightno_old",
+ "type": "char",
+ "typeParameters": [
+ "8"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "flightno_new",
+ "type": "char",
+ "typeParameters": [
+ "8"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "from_old",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "to_old",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "from_new",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "to_new",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "departure_old",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "arrival_old",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "departure_new",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "arrival_new",
+ "type": "datetime",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airplane_id_old",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airplane_id_new",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airline_id_old",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airline_id_new",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "comment",
+ "type": "varchar",
+ "typeParameters": [
+ "200"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "flight_id"
+ ],
+ "otherParameters": "",
+ "name": "flight_log_ibfk_1"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "flight_log_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "flightschedule": {
+ "columns": [
+ {
+ "name": "flightno",
+ "type": "char",
+ "typeParameters": [
+ "8"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "from",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "to",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "departure",
+ "type": "time",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "arrival",
+ "type": "time",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airline_id",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "monday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ },
+ {
+ "name": "tuesday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ },
+ {
+ "name": "wednesday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ },
+ {
+ "name": "thursday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ },
+ {
+ "name": "friday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ },
+ {
+ "name": "saturday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ },
+ {
+ "name": "sunday",
+ "type": "tinyint",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "DEFAULT '0'",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "from"
+ ],
+ "otherParameters": "",
+ "name": "from_idx"
+ },
+ {
+ "columns": [
+ "to"
+ ],
+ "otherParameters": "",
+ "name": "to_idx"
+ },
+ {
+ "columns": [
+ "airline_id"
+ ],
+ "otherParameters": "",
+ "name": "airline_idx"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "flightno"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "passenger": {
+ "columns": [
+ {
+ "name": "passenger_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "passportno",
+ "type": "char",
+ "typeParameters": [
+ "9"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "firstname",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "lastname",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "passportno"
+ ],
+ "otherParameters": "",
+ "name": "pass_unq"
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "passenger_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "passengerdetails": {
+ "columns": [
+ {
+ "name": "passenger_id",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "birthdate",
+ "type": "date",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "sex",
+ "type": "char",
+ "typeParameters": [
+ "1"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "street",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "city",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "zip",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "country",
+ "type": "varchar",
+ "typeParameters": [
+ "100"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "emailaddress",
+ "type": "varchar",
+ "typeParameters": [
+ "120"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "telephoneno",
+ "type": "varchar",
+ "typeParameters": [
+ "30"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "passenger_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "weatherdata": {
+ "columns": [
+ {
+ "name": "log_date",
+ "type": "date",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "time",
+ "type": "time",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "station",
+ "type": "int",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "temp",
+ "type": "decimal",
+ "typeParameters": [
+ "3",
+ "1"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "humidity",
+ "type": "decimal",
+ "typeParameters": [
+ "4",
+ "1"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "airpressure",
+ "type": "decimal",
+ "typeParameters": [
+ "10",
+ "2"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "wind",
+ "type": "decimal",
+ "typeParameters": [
+ "5",
+ "2"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "winddirection",
+ "type": "smallint",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "weather",
+ "type": "enum",
+ "typeParameters": [
+ "Nebel-Schneefall",
+ "Schneefall",
+ "Regen",
+ "Regen-Schneefall",
+ "Nebel-Regen",
+ "Nebel-Regen-Gewitter",
+ "Gewitter",
+ "Nebel",
+ "Regen-Gewitter"
+ ],
+ "otherParameters": "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "log_date",
+ "time",
+ "station"
+ ],
+ "otherParameters": ""
+ }
+ }
+ },
+ "connections": [
+ {
+ "type": "OneToMany",
+ "childTableName": "airline",
+ "childTableColumns": [
+ "base_airport"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "airplane",
+ "childTableColumns": [
+ "type_id"
+ ],
+ "parentTableName": "airplane_type",
+ "parentTableColumns": [
+ "type_id"
+ ]
+ },
+ {
+ "type": "OneToOne",
+ "childTableName": "airport_geo",
+ "childTableColumns": [
+ "airport_id"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToOne",
+ "childTableName": "airport_reachable",
+ "childTableColumns": [
+ "airport_id"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "booking",
+ "childTableColumns": [
+ "flight_id"
+ ],
+ "parentTableName": "flight",
+ "parentTableColumns": [
+ "flight_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "booking",
+ "childTableColumns": [
+ "passenger_id"
+ ],
+ "parentTableName": "passenger",
+ "parentTableColumns": [
+ "passenger_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flight",
+ "childTableColumns": [
+ "from"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flight",
+ "childTableColumns": [
+ "to"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flight",
+ "childTableColumns": [
+ "airline_id"
+ ],
+ "parentTableName": "airline",
+ "parentTableColumns": [
+ "airline_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flight",
+ "childTableColumns": [
+ "airplane_id"
+ ],
+ "parentTableName": "airplane",
+ "parentTableColumns": [
+ "airplane_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flight",
+ "childTableColumns": [
+ "flightno"
+ ],
+ "parentTableName": "flightschedule",
+ "parentTableColumns": [
+ "flightno"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flight_log",
+ "childTableColumns": [
+ "flight_id"
+ ],
+ "parentTableName": "flight",
+ "parentTableColumns": [
+ "flight_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flightschedule",
+ "childTableColumns": [
+ "from"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flightschedule",
+ "childTableColumns": [
+ "to"
+ ],
+ "parentTableName": "airport",
+ "parentTableColumns": [
+ "airport_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "flightschedule",
+ "childTableColumns": [
+ "airline_id"
+ ],
+ "parentTableName": "airline",
+ "parentTableColumns": [
+ "airline_id"
+ ]
+ },
+ {
+ "type": "OneToOne",
+ "childTableName": "passengerdetails",
+ "childTableColumns": [
+ "passenger_id"
+ ],
+ "parentTableName": "passenger",
+ "parentTableColumns": [
+ "passenger_id"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/example/output/sakila/sakila.json b/example/output/sakila/sakila.json
new file mode 100644
index 0000000..69bd327
--- /dev/null
+++ b/example/output/sakila/sakila.json
@@ -0,0 +1,1370 @@
+{
+ "tables": {
+ "actor": {
+ "columns": [
+ {
+ "name": "actor_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "first_name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "45"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "45"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "last_name"
+ ],
+ "otherParameters": "",
+ "name": "idx_actor_last_name"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "actor_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "address": {
+ "columns": [
+ {
+ "name": "address_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "address",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "address2",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "district",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "20"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "city_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "postal_code",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "10"
+ ],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "phone",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "20"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "city_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_city_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "address_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "category": {
+ "columns": [
+ {
+ "name": "category_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "25"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "category_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "city": {
+ "columns": [
+ {
+ "name": "city_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "city",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "country_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "country_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_country_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "city_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "country": {
+ "columns": [
+ {
+ "name": "country_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "country",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "country_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "customer": {
+ "columns": [
+ {
+ "name": "customer_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "store_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "first_name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "45"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "45"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "email",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "address_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "active",
+ "type": "BOOLEAN",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT TRUE",
+ "comment": ""
+ },
+ {
+ "name": "create_date",
+ "type": "DATETIME",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "store_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_store_id"
+ },
+ {
+ "columns": [
+ "address_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_address_id"
+ },
+ {
+ "columns": [
+ "last_name"
+ ],
+ "otherParameters": "",
+ "name": "idx_last_name"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "customer_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "film": {
+ "columns": [
+ {
+ "name": "film_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "title",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "255"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "description",
+ "type": "TEXT",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "release_year",
+ "type": "YEAR",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "language_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "original_language_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "rental_duration",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL DEFAULT 3",
+ "comment": ""
+ },
+ {
+ "name": "rental_rate",
+ "type": "DECIMAL",
+ "typeParameters": [
+ "4",
+ "2"
+ ],
+ "otherParameters": "NOT NULL DEFAULT 4.99",
+ "comment": ""
+ },
+ {
+ "name": "length",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "replacement_cost",
+ "type": "DECIMAL",
+ "typeParameters": [
+ "5",
+ "2"
+ ],
+ "otherParameters": "NOT NULL DEFAULT 19.99",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ },
+ {
+ "name": "rating",
+ "type": "ENUM",
+ "typeParameters": [
+ "G",
+ "PG",
+ "PG-13",
+ "R",
+ "NC-17"
+ ],
+ "otherParameters": "DEFAULT 'G'",
+ "comment": ""
+ },
+ {
+ "name": "special_features",
+ "type": "SET",
+ "typeParameters": [
+ "Trailers",
+ "Commentaries",
+ "Deleted Scenes",
+ "Behind the Scenes"
+ ],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "title"
+ ],
+ "otherParameters": "",
+ "name": "idx_title"
+ },
+ {
+ "columns": [
+ "language_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_language_id"
+ },
+ {
+ "columns": [
+ "original_language_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_original_language_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "film_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "film_actor": {
+ "columns": [
+ {
+ "name": "actor_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "film_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "film_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_film_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "actor_id",
+ "film_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "film_category": {
+ "columns": [
+ {
+ "name": "film_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "category_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "film_id",
+ "category_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "film_text": {
+ "columns": [
+ {
+ "name": "film_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "title",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "255"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "description",
+ "type": "TEXT",
+ "typeParameters": [],
+ "otherParameters": "",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [
+ {
+ "columns": [
+ "title",
+ "description"
+ ],
+ "otherParameters": "",
+ "name": "idx_title_description"
+ }
+ ],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "film_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "inventory": {
+ "columns": [
+ {
+ "name": "inventory_id",
+ "type": "MEDIUMINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "film_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "store_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "film_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_film_id"
+ },
+ {
+ "columns": [
+ "store_id",
+ "film_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_store_id_film_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "inventory_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "language": {
+ "columns": [
+ {
+ "name": "language_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "name",
+ "type": "CHAR",
+ "typeParameters": [
+ "20"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "language_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "payment": {
+ "columns": [
+ {
+ "name": "payment_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "customer_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "staff_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "rental_id",
+ "type": "INT",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "amount",
+ "type": "DECIMAL",
+ "typeParameters": [
+ "5",
+ "2"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "payment_date",
+ "type": "DATETIME",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "staff_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_staff_id"
+ },
+ {
+ "columns": [
+ "customer_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_customer_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "payment_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "rental": {
+ "columns": [
+ {
+ "name": "rental_id",
+ "type": "INT",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "rental_date",
+ "type": "DATETIME",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "inventory_id",
+ "type": "MEDIUMINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "customer_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "return_date",
+ "type": "DATETIME",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "staff_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "inventory_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_inventory_id"
+ },
+ {
+ "columns": [
+ "customer_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_customer_id"
+ },
+ {
+ "columns": [
+ "staff_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_staff_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "rental_date",
+ "inventory_id",
+ "customer_id"
+ ],
+ "otherParameters": "",
+ "name": ""
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "rental_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "staff": {
+ "columns": [
+ {
+ "name": "staff_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "first_name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "45"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_name",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "45"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "address_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "picture",
+ "type": "BLOB",
+ "typeParameters": [],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "email",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "50"
+ ],
+ "otherParameters": "DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "store_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "active",
+ "type": "BOOLEAN",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT TRUE",
+ "comment": ""
+ },
+ {
+ "name": "username",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "16"
+ ],
+ "otherParameters": "NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "password",
+ "type": "VARCHAR",
+ "typeParameters": [
+ "40"
+ ],
+ "otherParameters": "BINARY DEFAULT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "store_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_store_id"
+ },
+ {
+ "columns": [
+ "address_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_address_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": []
+ },
+ "primaryKey": {
+ "columns": [
+ "staff_id"
+ ],
+ "otherParameters": ""
+ }
+ },
+ "store": {
+ "columns": [
+ {
+ "name": "store_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL AUTO_INCREMENT",
+ "comment": ""
+ },
+ {
+ "name": "manager_staff_id",
+ "type": "TINYINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "address_id",
+ "type": "SMALLINT",
+ "typeParameters": [],
+ "otherParameters": "UNSIGNED NOT NULL",
+ "comment": ""
+ },
+ {
+ "name": "last_update",
+ "type": "TIMESTAMP",
+ "typeParameters": [],
+ "otherParameters": "NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",
+ "comment": ""
+ }
+ ],
+ "indexes": {
+ "simple": [
+ {
+ "columns": [
+ "address_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_fk_address_id"
+ }
+ ],
+ "spatial": [],
+ "fulltext": [],
+ "unique": [
+ {
+ "columns": [
+ "manager_staff_id"
+ ],
+ "otherParameters": "",
+ "name": "idx_unique_manager"
+ }
+ ]
+ },
+ "primaryKey": {
+ "columns": [
+ "store_id"
+ ],
+ "otherParameters": ""
+ }
+ }
+ },
+ "connections": [
+ {
+ "type": "OneToMany",
+ "childTableName": "address",
+ "childTableColumns": [
+ "city_id"
+ ],
+ "parentTableName": "city",
+ "parentTableColumns": [
+ "city_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "city",
+ "childTableColumns": [
+ "country_id"
+ ],
+ "parentTableName": "country",
+ "parentTableColumns": [
+ "country_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "customer",
+ "childTableColumns": [
+ "address_id"
+ ],
+ "parentTableName": "address",
+ "parentTableColumns": [
+ "address_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "customer",
+ "childTableColumns": [
+ "store_id"
+ ],
+ "parentTableName": "store",
+ "parentTableColumns": [
+ "store_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "film",
+ "childTableColumns": [
+ "language_id"
+ ],
+ "parentTableName": "language",
+ "parentTableColumns": [
+ "language_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "film",
+ "childTableColumns": [
+ "original_language_id"
+ ],
+ "parentTableName": "language",
+ "parentTableColumns": [
+ "language_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "film_actor",
+ "childTableColumns": [
+ "actor_id"
+ ],
+ "parentTableName": "actor",
+ "parentTableColumns": [
+ "actor_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "film_actor",
+ "childTableColumns": [
+ "film_id"
+ ],
+ "parentTableName": "film",
+ "parentTableColumns": [
+ "film_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "film_category",
+ "childTableColumns": [
+ "film_id"
+ ],
+ "parentTableName": "film",
+ "parentTableColumns": [
+ "film_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "film_category",
+ "childTableColumns": [
+ "category_id"
+ ],
+ "parentTableName": "category",
+ "parentTableColumns": [
+ "category_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "inventory",
+ "childTableColumns": [
+ "store_id"
+ ],
+ "parentTableName": "store",
+ "parentTableColumns": [
+ "store_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "inventory",
+ "childTableColumns": [
+ "film_id"
+ ],
+ "parentTableName": "film",
+ "parentTableColumns": [
+ "film_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "payment",
+ "childTableColumns": [
+ "rental_id"
+ ],
+ "parentTableName": "rental",
+ "parentTableColumns": [
+ "rental_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "payment",
+ "childTableColumns": [
+ "customer_id"
+ ],
+ "parentTableName": "customer",
+ "parentTableColumns": [
+ "customer_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "payment",
+ "childTableColumns": [
+ "staff_id"
+ ],
+ "parentTableName": "staff",
+ "parentTableColumns": [
+ "staff_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "rental",
+ "childTableColumns": [
+ "staff_id"
+ ],
+ "parentTableName": "staff",
+ "parentTableColumns": [
+ "staff_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "rental",
+ "childTableColumns": [
+ "inventory_id"
+ ],
+ "parentTableName": "inventory",
+ "parentTableColumns": [
+ "inventory_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "rental",
+ "childTableColumns": [
+ "customer_id"
+ ],
+ "parentTableName": "customer",
+ "parentTableColumns": [
+ "customer_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "staff",
+ "childTableColumns": [
+ "store_id"
+ ],
+ "parentTableName": "store",
+ "parentTableColumns": [
+ "store_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "staff",
+ "childTableColumns": [
+ "address_id"
+ ],
+ "parentTableName": "address",
+ "parentTableColumns": [
+ "address_id"
+ ]
+ },
+ {
+ "type": "OneToOne",
+ "childTableName": "store",
+ "childTableColumns": [
+ "manager_staff_id"
+ ],
+ "parentTableName": "staff",
+ "parentTableColumns": [
+ "staff_id"
+ ]
+ },
+ {
+ "type": "OneToMany",
+ "childTableName": "store",
+ "childTableColumns": [
+ "address_id"
+ ],
+ "parentTableName": "address",
+ "parentTableColumns": [
+ "address_id"
+ ]
+ }
+ ]
+}
\ No newline at end of file
From 886b378d9b6f03728f34ed29a9fe659eb10d2895 Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:14:00 +0200
Subject: [PATCH 07/16] refactor: drop the unused post-processing from the
Markdown exporter
The Markdown template already emits flush-left, single-blank-line output
with a trailing newline, so the leading-whitespace strip, blank-line
collapse and trailing-newline normalization were dead for the bundled
template (and the whitespace strip would corrupt intentional indentation
in a custom template). Export now returns the rendered output directly,
matching the PlantUML exporter. Align the export test with the raw render
(no injected trailing newline).
Co-Authored-By: Claude Opus 4.8 (1M context)
---
src/Export/Markdown.php | 7 +------
test/Unit/Export/MarkdownTest.php | 2 +-
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/src/Export/Markdown.php b/src/Export/Markdown.php
index d56e7b0..1f4db89 100644
--- a/src/Export/Markdown.php
+++ b/src/Export/Markdown.php
@@ -23,17 +23,12 @@ public function __construct(string $template)
public function export(SchemaInterface $schema): string
{
- $text = $this->twig->render(
+ return $this->twig->render(
'template',
[
'tables' => $schema->tables,
'connections' => $schema->connections,
]
);
-
- $text = preg_replace('/^[ \t]+/m', '', $text);
- $text = preg_replace('/\n{3,}/', "\n\n", (string) $text);
-
- return rtrim((string) $text, "\n") . "\n";
}
}
diff --git a/test/Unit/Export/MarkdownTest.php b/test/Unit/Export/MarkdownTest.php
index 52d9530..02ab311 100644
--- a/test/Unit/Export/MarkdownTest.php
+++ b/test/Unit/Export/MarkdownTest.php
@@ -77,7 +77,7 @@ public function testExportTableWithColumns(SchemaInterface $schema): void
$this->assertEquals(
strtr(
- "tables:%tables%|connections:%connections%\n",
+ 'tables:%tables%|connections:%connections%',
[
'%tables%' => json_encode($schema->tables->jsonSerialize()),
'%connections%' => json_encode($schema->connections->jsonSerialize()),
From bdef0b28ce07cbc923700fbaf2c2d6e2e528aefa Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:16:00 +0200
Subject: [PATCH 08/16] refactor: stream PlantUML image generation through
-pipe
Feed the PlantUML source to the jar on stdin and read the rendered image
from stdout via proc_open and the PlantUML -pipe mode, instead of writing
a temporary .puml source file, shelling out with a -output folder and
reading back a temporary image file. This drops the temp-file round trip
and the whole output-folder dependency (ImageGenerator no longer takes an
output folder; ImageCommand no longer passes a tmp dir).
Co-Authored-By: Claude Opus 4.8 (1M context)
---
src/Command/ImageCommand.php | 5 +-
src/Generator/ImageGenerator.php | 78 ++++++++++++--------------------
2 files changed, 30 insertions(+), 53 deletions(-)
diff --git a/src/Command/ImageCommand.php b/src/Command/ImageCommand.php
index c8cd63d..a9b2a8c 100644
--- a/src/Command/ImageCommand.php
+++ b/src/Command/ImageCommand.php
@@ -22,8 +22,6 @@ final class ImageCommand extends CommandAbstract
private const string PLANTUML_BIN = 'bin/plantuml-mit-1.2026.6.jar';
- private const string TEMP_DIR = 'tmp';
-
protected function configure(): void
{
$this
@@ -45,8 +43,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$image = new ImageGenerator(
$this->getImageType($input),
- $this->rootDir . self::PLANTUML_BIN,
- $this->rootDir . self::TEMP_DIR
+ $this->rootDir . self::PLANTUML_BIN
);
echo $image->generate($generatedPlantuml);
diff --git a/src/Generator/ImageGenerator.php b/src/Generator/ImageGenerator.php
index dd746c3..cdea2e4 100644
--- a/src/Generator/ImageGenerator.php
+++ b/src/Generator/ImageGenerator.php
@@ -4,11 +4,7 @@
namespace Pongee\DatabaseSchemaVisualization\Generator;
-use DateTime;
use Exception;
-use RuntimeException;
-use SplFileObject;
-use Twig\Environment;
final readonly class ImageGenerator
{
@@ -16,8 +12,7 @@
public function __construct(
private string $imageType,
- private string $plantumlJarPath,
- private string $outputFolder
+ private string $plantumlJarPath
) {
}
@@ -26,56 +21,41 @@ public function __construct(
*/
public function generate(string $plantuml): string
{
- $fileName = sprintf('plantuml-%s-%s', new DateTime()->format('Y-m-d-H-i-s-u'), mt_rand());
+ $command = sprintf(
+ 'java -DPLANTUML_LIMIT_SIZE=%s -jar %s -pipe -t%s',
+ self::PLANTUML_LIMIT_SIZE,
+ escapeshellarg($this->plantumlJarPath),
+ escapeshellarg($this->imageType)
+ );
+
+ $process = proc_open(
+ $command,
+ [
+ 0 => ['pipe', 'r'],
+ 1 => ['pipe', 'w'],
+ 2 => ['pipe', 'w'],
+ ],
+ $pipes
+ );
- $sourceFile = new SplFileObject($this->outputFolder . '/' . $fileName . '.puml', 'w');
- $sourceFile->fwrite($plantuml);
+ if (!is_resource($process)) {
+ throw new Exception('Plantuml diagram generation failed. Unable to start the process.');
+ }
- $output = $this->generatePlantuml($sourceFile);
+ fwrite($pipes[0], $plantuml);
+ fclose($pipes[0]);
- try {
- $outputFile = new SplFileObject($this->outputFolder . '/' . $fileName . '.' . $this->imageType, 'r');
- } catch (RuntimeException $runtimeException) {
- $this->deleteFiles($sourceFile);
+ $image = stream_get_contents($pipes[1]);
+ $error = stream_get_contents($pipes[2]);
+ fclose($pipes[1]);
+ fclose($pipes[2]);
+ if (proc_close($process) !== 0 || $image === false || $image === '') {
throw new Exception(
- sprintf(
- 'Plantuml diagram generation failed. Original error: [%s] output: [%s]',
- $runtimeException->getMessage(),
- $output
- )
+ sprintf('Plantuml diagram generation failed. Original error: [%s]', $error)
);
}
- $plantumlPng = $outputFile->fread($outputFile->getSize());
-
- $this->deleteFiles($sourceFile, $outputFile);
-
- return $plantumlPng;
- }
-
- private function generatePlantuml(SplFileObject $sourceFile): string
- {
- exec(
- sprintf(
- 'java -DPLANTUML_LIMIT_SIZE=%s -jar %s %s -t%s -output %s 2>&1',
- self::PLANTUML_LIMIT_SIZE,
- $this->plantumlJarPath,
- $sourceFile->getRealPath(),
- escapeshellarg($this->imageType),
- $this->outputFolder
- ),
- $output,
- $return
- );
-
- return implode("\n", $output);
- }
-
- private function deleteFiles(SplFileObject ...$files): void
- {
- foreach ($files as $file) {
- unlink($file->getRealPath());
- }
+ return $image;
}
}
From 1664db9c1896215f127ce928d7c0a56f5db0d2e5 Mon Sep 17 00:00:00 2001
From: Balazs Selyem <36171363+pongee@users.noreply.github.com>
Date: Tue, 28 Jul 2026 17:18:00 +0200
Subject: [PATCH 09/16] docs: regenerate example diagrams with the current
PlantUML template
Re-render the sakila and airportdb PNG/SVG outputs so they reflect the
current template: the <