diff --git a/Makefile b/Makefile
index 521f9858..3b81c340 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,8 @@ db: db_sqlite
usql my://root:mypass@localhost:33308/testdb -f testdata/ddl/mysql.sql
usql my://root:mypass@localhost:33308/testdb -c "CREATE DATABASE IF NOT EXISTS relations;"
usql my://root:mypass@localhost:33308/relations -f testdata/ddl/detect_relations.sql
+ usql my://root:mypass@localhost:33308/testdb -c "CREATE DATABASE IF NOT EXISTS relations_singular;"
+ usql my://root:mypass@localhost:33308/relations_singular -f testdata/ddl/detect_relations_singular.sql
usql maria://root:mypass@localhost:33309/testdb -f testdata/ddl/maria.sql
usql ms://SA:MSSQLServer-Passw0rd@localhost:11433/master -c "IF NOT EXISTS (SELECT * FROM sys.databases WHERE NAME = 'testdb') CREATE DATABASE testdb;"
usql ms://SA:MSSQLServer-Passw0rd@localhost:11433/testdb -f testdata/ddl/mssql.sql || true
@@ -49,6 +51,7 @@ doc: build doc_sqlite
./tbls doc my://root:mypass@localhost:33306/testdb -c testdata/test_tbls.yml -f sample/mysql56
./tbls doc my://root:mypass@localhost:33308/testdb -c testdata/test_tbls.yml -f sample/mysql
./tbls doc my://root:mypass@localhost:33308/relations -c testdata/test_tbls_detect_relations.yml -f sample/detect_relations
+ ./tbls doc my://root:mypass@localhost:33308/relations_singular -c testdata/test_tbls_detect_relations_singular.yml -f sample/detect_relations_singular
./tbls doc maria://root:mypass@localhost:33309/testdb -c testdata/test_tbls.yml -f sample/mariadb
./tbls doc ms://SA:MSSQLServer-Passw0rd@localhost:11433/testdb -c testdata/test_tbls_mssql.yml -f sample/mssql
env AWS_ENDPOINT_URL=http://localhost:18000 ./tbls doc dynamodb://ap-northeast-1 -c testdata/test_tbls_dynamodb.yml -f sample/dynamodb
@@ -68,6 +71,7 @@ testdoc: build testdoc_sqlite
./tbls diff my://root:mypass@localhost:33306/testdb -c testdata/test_tbls.yml sample/mysql56
./tbls diff my://root:mypass@localhost:33308/testdb -c testdata/test_tbls.yml sample/mysql
./tbls diff my://root:mypass@localhost:33308/relations -c testdata/test_tbls_detect_relations.yml sample/detect_relations
+ ./tbls diff my://root:mypass@localhost:33308/relations_singular -c testdata/test_tbls_detect_relations_singular.yml sample/detect_relations_singular
./tbls diff maria://root:mypass@localhost:33309/testdb -c testdata/test_tbls.yml sample/mariadb
./tbls diff ms://SA:MSSQLServer-Passw0rd@localhost:11433/testdb -c testdata/test_tbls_mssql.yml sample/mssql
env AWS_ENDPOINT_URL=http://localhost:18000 ./tbls diff dynamodb://ap-northeast-1 -c testdata/test_tbls_dynamodb.yml sample/dynamodb
diff --git a/config/naming.go b/config/naming.go
index aed0488f..66c7a837 100644
--- a/config/naming.go
+++ b/config/naming.go
@@ -2,8 +2,9 @@ package config
import (
"fmt"
- "github.com/gertd/go-pluralize"
"strings"
+
+ "github.com/gertd/go-pluralize"
)
var (
@@ -28,6 +29,12 @@ func SelectNamingStrategy(name string) bool {
switch name {
case "":
// TODO: Add case if added naming strategy
+
+ case "singularTableName":
+ namingStrategy.ParentTable = singularTableParentTableNamer
+ namingStrategy.ParentColumn = singularTableParentColumnNamer
+ return true
+
default:
fmt.Printf("Naming strategy does not exist. strategy: %s\n", name)
return false
@@ -68,3 +75,16 @@ func defaultParentTableNamer(name string) string {
func defaultParentColumnNamer(name string) string {
return "id"
}
+
+func singularTableParentTableNamer(name string) string {
+ index := strings.LastIndex(name, "_")
+
+ if index == -1 || name[index+1:] != "id" {
+ return ""
+ }
+ return pluralizeClient.Singular(name[:index])
+}
+
+func singularTableParentColumnNamer(name string) string {
+ return "id"
+}
diff --git a/config/naming_test.go b/config/naming_test.go
new file mode 100644
index 00000000..5ecf2bf5
--- /dev/null
+++ b/config/naming_test.go
@@ -0,0 +1,38 @@
+package config
+
+import "testing"
+
+func TestSingularTableParentTableNamer(t *testing.T) {
+ tests := []struct {
+ name string
+ want string
+ }{
+ {"user_id", "user"},
+ }
+
+ for _, tt := range tests {
+ got := singularTableParentTableNamer(tt.name)
+
+ if got != tt.want {
+ t.Errorf("name %v\ngot %v\nwant %v", tt.name, got, tt.want)
+ }
+ }
+}
+
+func TestSingularTableParentColumnNamer(t *testing.T) {
+ tests := []struct {
+ name string
+ want string
+ }{
+ // normal
+ {"user_id", "id"},
+ }
+
+ for _, tt := range tests {
+ got := singularTableParentColumnNamer(tt.name)
+
+ if got != tt.want {
+ t.Errorf("name %v\ngot %v\nwant %v", tt.name, got, tt.want)
+ }
+ }
+}
diff --git a/sample/detect_relations_singular/CamelizeTable.md b/sample/detect_relations_singular/CamelizeTable.md
new file mode 100644
index 00000000..3cb9bfbb
--- /dev/null
+++ b/sample/detect_relations_singular/CamelizeTable.md
@@ -0,0 +1,43 @@
+# CamelizeTable
+
+## Description
+
+
+Table Definition
+
+```sql
+CREATE TABLE `CamelizeTable` (
+ `id` bigint NOT NULL AUTO_INCREMENT,
+ `created` datetime NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | bigint | | false | auto_increment | | | |
+| created | datetime | | false | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+
+## Relations
+
+![er](CamelizeTable.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/CamelizeTable.svg b/sample/detect_relations_singular/CamelizeTable.svg
new file mode 100644
index 00000000..f3c84128
--- /dev/null
+++ b/sample/detect_relations_singular/CamelizeTable.svg
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/README.md b/sample/detect_relations_singular/README.md
new file mode 100644
index 00000000..96ecb08c
--- /dev/null
+++ b/sample/detect_relations_singular/README.md
@@ -0,0 +1,31 @@
+# relations_singular
+
+## Description
+
+Sample database document.
+
+## Labels
+
+`sample` `tbls`
+
+## Tables
+
+| Name | Columns | Comment | Type |
+| ---- | ------- | ------- | ---- |
+| [CamelizeTable](CamelizeTable.md) | 2 | | BASE TABLE |
+| [comment](comment.md) | 6 | Comment
Multi-line
table
comment | BASE TABLE |
+| [comment_star](comment_star.md) | 6 | | BASE TABLE |
+| [hyphen-table](hyphen-table.md) | 3 | | BASE TABLE |
+| [log](log.md) | 7 | Auditログ | BASE TABLE |
+| [post](post.md) | 7 | Post table | BASE TABLE |
+| [post_comment](post_comment.md) | 7 | post and comments View table | VIEW |
+| [user](user.md) | 6 | User table | BASE TABLE |
+| [user_option](user_option.md) | 4 | User option table | BASE TABLE |
+
+## Relations
+
+![er](schema.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/comment.md b/sample/detect_relations_singular/comment.md
new file mode 100644
index 00000000..c8496a49
--- /dev/null
+++ b/sample/detect_relations_singular/comment.md
@@ -0,0 +1,61 @@
+# comment
+
+## Description
+
+Comment
+Multi-line
+table
+comment
+
+
+Table Definition
+
+```sql
+CREATE TABLE `comment` (
+ `id` bigint NOT NULL AUTO_INCREMENT,
+ `post_id` bigint NOT NULL,
+ `user_id` int NOT NULL,
+ `comment` text NOT NULL COMMENT 'Comment\nMulti-line\r\ncolumn\rcomment',
+ `created` datetime NOT NULL,
+ `updated` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `post_id` (`post_id`,`user_id`),
+ KEY `comment_post_id_user_id_idx` (`post_id`,`user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Comment\nMulti-line\r\ntable\rcomment'
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | bigint | | false | auto_increment | [log](log.md) | | |
+| post_id | bigint | | false | | | [post](post.md) | |
+| user_id | int | | false | | | [user](user.md) | |
+| comment | text | | false | | | | Comment
Multi-line
column
comment |
+| created | datetime | | false | | | | |
+| updated | datetime | | true | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| post_id | UNIQUE | UNIQUE KEY post_id (post_id, user_id) |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| comment_post_id_user_id_idx | KEY comment_post_id_user_id_idx (post_id, user_id) USING BTREE |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+| post_id | UNIQUE KEY post_id (post_id, user_id) USING BTREE |
+
+## Relations
+
+![er](comment.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/comment.svg b/sample/detect_relations_singular/comment.svg
new file mode 100644
index 00000000..862e8768
--- /dev/null
+++ b/sample/detect_relations_singular/comment.svg
@@ -0,0 +1,149 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/comment_star.md b/sample/detect_relations_singular/comment_star.md
new file mode 100644
index 00000000..1c64eab8
--- /dev/null
+++ b/sample/detect_relations_singular/comment_star.md
@@ -0,0 +1,54 @@
+# comment_star
+
+## Description
+
+
+Table Definition
+
+```sql
+CREATE TABLE `comment_star` (
+ `id` bigint NOT NULL AUTO_INCREMENT,
+ `user_id` int NOT NULL,
+ `comment_post_id` bigint NOT NULL,
+ `comment_user_id` int NOT NULL,
+ `created` timestamp NOT NULL,
+ `updated` timestamp NULL DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `user_id` (`user_id`,`comment_post_id`,`comment_user_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | bigint | | false | auto_increment | [log](log.md) | | |
+| user_id | int | | false | | | [user](user.md) | |
+| comment_post_id | bigint | | false | | | | |
+| comment_user_id | int | | false | | | | |
+| created | timestamp | | false | | | | |
+| updated | timestamp | | true | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+| user_id | UNIQUE | UNIQUE KEY user_id (user_id, comment_post_id, comment_user_id) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+| user_id | UNIQUE KEY user_id (user_id, comment_post_id, comment_user_id) USING BTREE |
+
+## Relations
+
+![er](comment_star.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/comment_star.svg b/sample/detect_relations_singular/comment_star.svg
new file mode 100644
index 00000000..d9056f32
--- /dev/null
+++ b/sample/detect_relations_singular/comment_star.svg
@@ -0,0 +1,112 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/hyphen-table.md b/sample/detect_relations_singular/hyphen-table.md
new file mode 100644
index 00000000..be05c937
--- /dev/null
+++ b/sample/detect_relations_singular/hyphen-table.md
@@ -0,0 +1,45 @@
+# hyphen-table
+
+## Description
+
+
+Table Definition
+
+```sql
+CREATE TABLE `hyphen-table` (
+ `id` bigint NOT NULL AUTO_INCREMENT,
+ `hyphen-column` text NOT NULL,
+ `created` datetime NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | bigint | | false | auto_increment | | | |
+| hyphen-column | text | | false | | | | |
+| created | datetime | | false | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+
+## Relations
+
+![er](hyphen-table.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/hyphen-table.svg b/sample/detect_relations_singular/hyphen-table.svg
new file mode 100644
index 00000000..fa02ecd0
--- /dev/null
+++ b/sample/detect_relations_singular/hyphen-table.svg
@@ -0,0 +1,32 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/log.md b/sample/detect_relations_singular/log.md
new file mode 100644
index 00000000..c4106ce1
--- /dev/null
+++ b/sample/detect_relations_singular/log.md
@@ -0,0 +1,55 @@
+# log
+
+## Description
+
+Auditログ
+
+
+Table Definition
+
+```sql
+CREATE TABLE `log` (
+ `id` bigint NOT NULL AUTO_INCREMENT,
+ `user_id` int NOT NULL,
+ `post_id` bigint DEFAULT NULL,
+ `comment_id` bigint DEFAULT NULL,
+ `comment_star_id` bigint DEFAULT NULL,
+ `payload` text,
+ `created` datetime NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Auditログ'
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | bigint | | false | auto_increment | | | |
+| user_id | int | | false | | | [user](user.md) | |
+| post_id | bigint | | true | | | [post](post.md) | |
+| comment_id | bigint | | true | | | [comment](comment.md) | |
+| comment_star_id | bigint | | true | | | [comment_star](comment_star.md) | |
+| payload | text | | true | | | | |
+| created | datetime | | false | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+
+## Relations
+
+![er](log.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/log.svg b/sample/detect_relations_singular/log.svg
new file mode 100644
index 00000000..e0209c87
--- /dev/null
+++ b/sample/detect_relations_singular/log.svg
@@ -0,0 +1,183 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/post.md b/sample/detect_relations_singular/post.md
new file mode 100644
index 00000000..cb3125a1
--- /dev/null
+++ b/sample/detect_relations_singular/post.md
@@ -0,0 +1,70 @@
+# post
+
+## Description
+
+Post table
+
+
+Table Definition
+
+```sql
+CREATE TABLE `post` (
+ `id` bigint NOT NULL AUTO_INCREMENT,
+ `user_id` int NOT NULL,
+ `title` varchar(255) NOT NULL,
+ `body` text NOT NULL,
+ `post_type` enum('public','private','draft') NOT NULL COMMENT 'public/private/draft',
+ `created` datetime NOT NULL,
+ `updated` datetime DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `user_id` (`user_id`,`title`),
+ KEY `post_user_id_idx` (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Post table'
+```
+
+
+
+## Labels
+
+`green` `red` `blue`
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | bigint | | false | auto_increment | [comment](comment.md) [log](log.md) | | |
+| user_id | int | | false | | | [user](user.md) | |
+| title | varchar(255) | | false | | | | |
+| body | text | | false | | | | post body |
+| post_type | enum('public','private','draft') | | false | | | | public/private/draft |
+| created | datetime | | false | | | | |
+| updated | datetime | | true | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+| user_id | UNIQUE | UNIQUE KEY user_id (user_id, title) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| post_user_id_idx | KEY post_user_id_idx (id) USING BTREE |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+| user_id | UNIQUE KEY user_id (user_id, title) USING BTREE |
+
+## Triggers
+
+| Name | Definition |
+| ---- | ---------- |
+| update_posts_updated | CREATE TRIGGER update_posts_updated BEFORE UPDATE ON post
FOR EACH ROW
SET NEW.updated = CURRENT_TIMESTAMP() |
+
+## Relations
+
+![er](post.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/post.svg b/sample/detect_relations_singular/post.svg
new file mode 100644
index 00000000..6cca93e8
--- /dev/null
+++ b/sample/detect_relations_singular/post.svg
@@ -0,0 +1,149 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/post_comment.md b/sample/detect_relations_singular/post_comment.md
new file mode 100644
index 00000000..d18e90e8
--- /dev/null
+++ b/sample/detect_relations_singular/post_comment.md
@@ -0,0 +1,40 @@
+# post_comment
+
+## Description
+
+post and comments View table
+
+
+Table Definition
+
+```sql
+CREATE VIEW post_comment AS (select `c`.`id` AS `id`,`p`.`title` AS `title`,`u2`.`username` AS `post_user`,`c`.`comment` AS `comment`,`u2`.`username` AS `comment_user`,`c`.`created` AS `created`,`c`.`updated` AS `updated` from (((`relations_singular`.`post` `p` left join `relations_singular`.`comment` `c` on((`p`.`id` = `c`.`post_id`))) left join `relations_singular`.`user` `u` on((`u`.`id` = `p`.`user_id`))) left join `relations_singular`.`user` `u2` on((`u2`.`id` = `c`.`user_id`))))
+```
+
+
+
+## Referenced Tables
+
+- [post](post.md)
+- [comment](comment.md)
+- [user](user.md)
+
+## Columns
+
+| Name | Type | Default | Nullable | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
+| id | bigint | 0 | true | | | comment.id |
+| title | varchar(255) | | false | | | post.title |
+| post_user | varchar(50) | | true | | | post.user.username |
+| comment | text | | true | | | Comment
Multi-line
column
comment |
+| comment_user | varchar(50) | | true | | | comment.user.username |
+| created | datetime | | true | | | comment.created |
+| updated | datetime | | true | | | comment.updated |
+
+## Relations
+
+![er](post_comment.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/post_comment.svg b/sample/detect_relations_singular/post_comment.svg
new file mode 100644
index 00000000..d1429ada
--- /dev/null
+++ b/sample/detect_relations_singular/post_comment.svg
@@ -0,0 +1,44 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/schema.svg b/sample/detect_relations_singular/schema.svg
new file mode 100644
index 00000000..e19bcdae
--- /dev/null
+++ b/sample/detect_relations_singular/schema.svg
@@ -0,0 +1,301 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/user.md b/sample/detect_relations_singular/user.md
new file mode 100644
index 00000000..eb2c1ee6
--- /dev/null
+++ b/sample/detect_relations_singular/user.md
@@ -0,0 +1,59 @@
+# user
+
+## Description
+
+User table
+
+
+Table Definition
+
+```sql
+CREATE TABLE `user` (
+ `id` int NOT NULL AUTO_INCREMENT,
+ `username` varchar(50) NOT NULL,
+ `password` varchar(50) NOT NULL,
+ `email` varchar(355) NOT NULL COMMENT 'ex. user@example.com',
+ `created` timestamp NOT NULL,
+ `updated` timestamp NULL DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`),
+ UNIQUE KEY `email` (`email`)
+) ENGINE=InnoDB AUTO_INCREMENT=[Redacted by tbls] DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='User table'
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Extra Definition | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | --------------- | -------- | ------- | ------- |
+| id | int | | false | auto_increment | [user_option](user_option.md) [comment](comment.md) [comment_star](comment_star.md) [log](log.md) [post](post.md) | | |
+| username | varchar(50) | | false | | | | |
+| password | varchar(50) | | false | | | | |
+| email | varchar(355) | | false | | | | ex. user@example.com |
+| created | timestamp | | false | | | | |
+| updated | timestamp | | true | | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| email | UNIQUE | UNIQUE KEY email (email) |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (id) |
+| username | UNIQUE | UNIQUE KEY username (username) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| PRIMARY | PRIMARY KEY (id) USING BTREE |
+| email | UNIQUE KEY email (email) USING BTREE |
+| username | UNIQUE KEY username (username) USING BTREE |
+
+## Relations
+
+![er](user.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/user.svg b/sample/detect_relations_singular/user.svg
new file mode 100644
index 00000000..5779e2ff
--- /dev/null
+++ b/sample/detect_relations_singular/user.svg
@@ -0,0 +1,211 @@
+
+
+
+
+
diff --git a/sample/detect_relations_singular/user_option.md b/sample/detect_relations_singular/user_option.md
new file mode 100644
index 00000000..0baf438f
--- /dev/null
+++ b/sample/detect_relations_singular/user_option.md
@@ -0,0 +1,54 @@
+# user_option
+
+## Description
+
+User option table
+
+
+Table Definition
+
+```sql
+CREATE TABLE `user_option` (
+ `user_id` int NOT NULL,
+ `show_email` tinyint(1) NOT NULL DEFAULT '0',
+ `created` timestamp NOT NULL,
+ `updated` timestamp NULL DEFAULT NULL,
+ PRIMARY KEY (`user_id`),
+ UNIQUE KEY `user_id` (`user_id`),
+ CONSTRAINT `user_option_user_id_fk` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='User option table'
+```
+
+
+
+## Columns
+
+| Name | Type | Default | Nullable | Children | Parents | Comment |
+| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
+| user_id | int | | false | | [user](user.md) | |
+| show_email | tinyint(1) | 0 | false | | | |
+| created | timestamp | | false | | | |
+| updated | timestamp | | true | | | |
+
+## Constraints
+
+| Name | Type | Definition |
+| ---- | ---- | ---------- |
+| PRIMARY | PRIMARY KEY | PRIMARY KEY (user_id) |
+| user_id | UNIQUE | UNIQUE KEY user_id (user_id) |
+| user_option_user_id_fk | FOREIGN KEY | FOREIGN KEY (user_id) REFERENCES user (id) |
+
+## Indexes
+
+| Name | Definition |
+| ---- | ---------- |
+| PRIMARY | PRIMARY KEY (user_id) USING BTREE |
+| user_id | UNIQUE KEY user_id (user_id) USING BTREE |
+
+## Relations
+
+![er](user_option.svg)
+
+---
+
+> Generated by [tbls](https://github.com/k1LoW/tbls)
diff --git a/sample/detect_relations_singular/user_option.svg b/sample/detect_relations_singular/user_option.svg
new file mode 100644
index 00000000..43128a3d
--- /dev/null
+++ b/sample/detect_relations_singular/user_option.svg
@@ -0,0 +1,69 @@
+
+
+
+
+
diff --git a/testdata/ddl/detect_relations_singular.sql b/testdata/ddl/detect_relations_singular.sql
new file mode 100644
index 00000000..5d764390
--- /dev/null
+++ b/testdata/ddl/detect_relations_singular.sql
@@ -0,0 +1,97 @@
+DROP TRIGGER IF EXISTS update_posts_updated;
+DROP VIEW IF EXISTS post_comment;
+DROP TABLE IF EXISTS `hyphen-table`;
+DROP TABLE IF EXISTS CamelizeTable;
+DROP TABLE IF EXISTS log;
+DROP TABLE IF EXISTS comment_star;
+DROP TABLE IF EXISTS comment;
+DROP TABLE IF EXISTS post;
+DROP TABLE IF EXISTS user_option;
+DROP TABLE IF EXISTS user;
+
+CREATE TABLE user (
+ id int PRIMARY KEY AUTO_INCREMENT,
+ username varchar (50) UNIQUE NOT NULL,
+ password varchar (50) NOT NULL,
+ email varchar (355) UNIQUE NOT NULL COMMENT 'ex. user@example.com',
+ created timestamp NOT NULL,
+ updated timestamp
+) COMMENT = 'User table' AUTO_INCREMENT = 100;
+
+CREATE TABLE user_option (
+ user_id int PRIMARY KEY,
+ show_email boolean NOT NULL DEFAULT false,
+ created timestamp NOT NULL,
+ updated timestamp,
+ UNIQUE(user_id),
+ CONSTRAINT user_option_user_id_fk FOREIGN KEY(user_id) REFERENCES user(id) ON UPDATE NO ACTION ON DELETE CASCADE
+) COMMENT = 'User option table';
+
+CREATE TABLE post (
+ id bigint AUTO_INCREMENT,
+ user_id int NOT NULL,
+ title varchar (255) NOT NULL,
+ body text NOT NULL,
+ post_type enum('public', 'private', 'draft') NOT NULL COMMENT 'public/private/draft',
+ created datetime NOT NULL,
+ updated datetime,
+ CONSTRAINT post_id_pk PRIMARY KEY(id),
+ UNIQUE(user_id, title)
+) COMMENT = 'Post table';
+CREATE INDEX post_user_id_idx ON post(id) USING BTREE;
+
+CREATE TABLE comment (
+ id bigint AUTO_INCREMENT,
+ post_id bigint NOT NULL,
+ user_id int NOT NULL,
+ comment text NOT NULL COMMENT 'Comment\nMulti-line\r\ncolumn\rcomment',
+ created datetime NOT NULL,
+ updated datetime,
+ CONSTRAINT comment_id_pk PRIMARY KEY(id),
+ UNIQUE(post_id, user_id)
+) COMMENT = 'Comment\nMulti-line\r\ntable\rcomment';
+CREATE INDEX comment_post_id_user_id_idx ON comment(post_id, user_id) USING HASH;
+
+CREATE TABLE comment_star (
+ id bigint AUTO_INCREMENT,
+ user_id int NOT NULL,
+ comment_post_id bigint NOT NULL,
+ comment_user_id int NOT NULL,
+ created timestamp NOT NULL,
+ updated timestamp,
+ CONSTRAINT comment_star_id_pk PRIMARY KEY(id),
+ UNIQUE(user_id, comment_post_id, comment_user_id)
+);
+
+CREATE TABLE log (
+ id bigint PRIMARY KEY AUTO_INCREMENT,
+ user_id int NOT NULL,
+ post_id bigint,
+ comment_id bigint,
+ comment_star_id bigint,
+ payload text,
+ created datetime NOT NULL
+) COMMENT = 'Auditログ';
+
+CREATE VIEW post_comment AS (
+ SELECT c.id, p.title, u2.username AS post_user, c.comment, u2.username AS comment_user, c.created, c.updated
+ FROM post AS p
+ LEFT JOIN comment AS c on p.id = c.post_id
+ LEFT JOIN user AS u on u.id = p.user_id
+ LEFT JOIN user AS u2 on u2.id = c.user_id
+);
+
+CREATE TABLE CamelizeTable (
+ id bigint PRIMARY KEY AUTO_INCREMENT,
+ created datetime NOT NULL
+);
+
+CREATE TABLE `hyphen-table` (
+ id bigint PRIMARY KEY AUTO_INCREMENT,
+ `hyphen-column` text NOT NULL,
+ created datetime NOT NULL
+);
+
+CREATE TRIGGER update_posts_updated BEFORE UPDATE ON post
+ FOR EACH ROW
+ SET NEW.updated = CURRENT_TIMESTAMP();
diff --git a/testdata/test_tbls_detect_relations_singular.yml b/testdata/test_tbls_detect_relations_singular.yml
new file mode 100644
index 00000000..a596daad
--- /dev/null
+++ b/testdata/test_tbls_detect_relations_singular.yml
@@ -0,0 +1,39 @@
+---
+desc: Sample database document.
+labels:
+ - sample
+ - tbls
+detectVirtualRelations:
+ enabled: true
+ strategy: singularTableName
+lint:
+ requireColumnComment:
+ enabled: true
+ exclude:
+ - id
+ - created
+ - updated
+ columnCount:
+ enabled: true
+ max: 5
+comments:
+ -
+ table: post
+ columnComments:
+ body: post body
+ labels:
+ - green
+ - red
+ - blue
+ -
+ table: log
+ -
+ table: post_comment
+ tableComment: post and comments View table
+ columnComments:
+ id: comment.id
+ title: post.title
+ post_user: post.user.username
+ comment_user: comment.user.username
+ created: comment.created
+ updated: comment.updated