Skip to content

Commit b8bf70c

Browse files
authored
Merge pull request #18 from onozaty/16-add-mysql-support
Add MySQL support #16
2 parents 9240d86 + 461109a commit b8bf70c

23 files changed

+1500
-385
lines changed

.claude/settings.local.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(find:*)",
5+
"Bash(pnpm run test:*)",
6+
"Bash(pnpm run lint:*)",
7+
"Bash(pnpm run format:*)",
8+
"WebFetch(domain:github.com)"
9+
],
10+
"deny": []
11+
}
12+
}

.devcontainer/docker-compose.yml

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
version: "3.8"
2-
31
services:
42
app:
53
build:
@@ -9,7 +7,9 @@ services:
97
- ../..:/workspaces:cached
108
- app-node-modules:/workspaces/app/node_modules
119
command: sleep infinity
12-
network_mode: service:db
10+
depends_on:
11+
- db
12+
- mysql
1313
db:
1414
image: postgres:latest
1515
restart: unless-stopped
@@ -19,6 +19,23 @@ services:
1919
POSTGRES_PASSWORD: db_password
2020
POSTGRES_USER: db_user
2121
POSTGRES_DB: sample
22+
ports:
23+
- 5432:5432
24+
25+
mysql:
26+
image: mysql:latest
27+
restart: unless-stopped
28+
volumes:
29+
- mysql-data:/var/lib/mysql
30+
- ./mysql-init:/docker-entrypoint-initdb.d
31+
environment:
32+
MYSQL_ROOT_PASSWORD: root_password
33+
MYSQL_DATABASE: sample
34+
MYSQL_USER: db_user
35+
MYSQL_PASSWORD: db_password
36+
ports:
37+
- 3306:3306
38+
2239
pgadmin4:
2340
image: dpage/pgadmin4:latest
2441
ports:
@@ -33,7 +50,21 @@ services:
3350
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
3451
depends_on:
3552
- db
53+
54+
phpmyadmin:
55+
image: phpmyadmin/phpmyadmin:latest
56+
restart: unless-stopped
57+
ports:
58+
- 8081:80
59+
environment:
60+
PMA_HOST: mysql
61+
PMA_USER: db_user
62+
PMA_PASSWORD: db_password
63+
MYSQL_ROOT_PASSWORD: root_password
64+
depends_on:
65+
- mysql
3666
volumes:
3767
postgres-data:
68+
mysql-data:
3869
app-node-modules:
3970
pgadmin-data:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Grant required privileges to db_user for Prisma shadow database
2+
-- Based on: https://www.prisma.io/docs/orm/prisma-migrate/understanding-prisma-migrate/shadow-database#shadow-database-user-permissions
3+
GRANT CREATE, ALTER, DROP, REFERENCES, CREATE ROUTINE, ALTER ROUTINE ON *.* TO 'db_user'@'%';
4+
FLUSH PRIVILEGES;

.env.mysql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DATABASE_URL="mysql://db_user:db_password@mysql:3306/sample"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
node_modules/
22
dist/
33
prisma/migrations/
4+
prisma-mysql/migrations/
45
coverage/
56
src/__fixtures__/**/migrations/

CLAUDE.md

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,14 @@ Always run `pnpm run format` after editing code to ensure consistent formatting
2525

2626
## Architecture
2727

28-
The codebase follows a clear separation of concerns with four main modules:
28+
The codebase follows a clear separation of concerns with five main modules:
2929

3030
### Core Flow
3131
1. **parser.ts** - Parses Prisma DMMF (Data Model Meta Format) into internal types
32-
2. **comment.ts** - Creates comment structures and handles diff logic
33-
3. **statement.ts** - Generates SQL `COMMENT ON` statements
34-
4. **generator.ts** - Main generator that orchestrates the process and handles file I/O
32+
2. **config.ts** - Handles configuration reading and validation
33+
3. **comment.ts** - Creates comment structures and handles diff logic
34+
4. **statement.ts** - Generates SQL `COMMENT ON` statements
35+
5. **generator.ts** - Main generator that orchestrates the process and handles file I/O
3536

3637
### Key Components
3738

@@ -40,37 +41,48 @@ The codebase follows a clear separation of concerns with four main modules:
4041
- Filters only scalar and enum fields (ignores relations)
4142
- Maps Prisma names to database names using `@@map` directives
4243

44+
**config.ts** (`src/config.ts`):
45+
- Defines the `Config` interface for all configuration options
46+
- Implements `readConfig` function to extract and validate configuration from GeneratorOptions
47+
- Handles parsing of targets, patterns, provider detection, and output directory resolution
48+
- Centralizes all configuration logic for better maintainability
49+
4350
**comment.ts** (`src/comment.ts`):
44-
- Creates comment structures for tables and columns based on targets
51+
- Creates comment structures for tables and columns based on configuration
4552
- Implements diffing logic to generate only changed comments
46-
- Handles filtering via `ignorePattern` and `ignoreCommentPattern`
53+
- Accepts `Config` object instead of individual parameters for cleaner interface
4754
- Supports enum information injection into field comments
4855

4956
**statement.ts** (`src/statement.ts`):
50-
- Generates PostgreSQL `COMMENT ON TABLE` and `COMMENT ON COLUMN` statements
57+
- Generates PostgreSQL and MySQL `COMMENT ON` statements
5158
- Handles comment escaping and multi-line comments with E-strings
52-
- Supports schema-qualified table names
59+
- Supports schema-qualified table names and multiple database providers
5360

5461
**generator.ts** (`src/generator.ts`):
5562
- Entry point that implements Prisma's generator interface
63+
- Uses `readConfig` to obtain all configuration settings
5664
- Manages state persistence via `comments-latest.json`
5765
- Creates migration files with timestamped directories
58-
- Orchestrates the entire generation process
66+
- Orchestrates the entire generation process with improved separation of concerns
5967

6068
### State Management
6169
The generator maintains state between runs by storing the current comments in `prisma/migrations/comments-latest.json`. This enables differential generation - only creating migration files for changed comments.
6270

6371
### Configuration Options
72+
All configuration options are now centralized in `config.ts`:
6473
- `targets` - Select "table", "column", or both for comment generation
6574
- `ignorePattern` - Regex to exclude models by database name
6675
- `ignoreCommentPattern` - Regex to exclude comments by content
6776
- `includeEnumInFieldComment` - Add enum values to field comments
77+
- `provider` - Auto-detected database provider (PostgreSQL or MySQL)
78+
- `outputDir` - Parsed output directory from generator configuration
6879

6980
## Testing
7081

7182
Tests use Vitest and are organized as:
72-
- Unit tests for each module (e.g., `comment.test.ts`)
83+
- Unit tests for each module (e.g., `comment.test.ts`, `config.test.ts`)
7384
- Integration tests for the generator (`generator.test.ts`)
85+
- Configuration testing with comprehensive coverage (`config.test.ts` - 17 test cases)
7486
- Snapshot testing for generated SQL output
7587
- Test fixtures in `src/__fixtures__/` with various schema scenarios
7688

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,24 @@ COMMENT ON COLUMN "products"."type" IS E'Product Type\nenum: enum_product_type(B
240240
## Supported Databases
241241

242242
- PostgreSQL
243+
- MySQL (Experimental)
243244

244-
Other databases may be available, but the above is the only one checked.
245+
### MySQL Support (Experimental)
246+
247+
MySQL support is currently experimental and we are seeking feedback. For MySQL, this generator uses stored procedures to manage column comments due to MySQL's specific syntax requirements for column comment updates.
248+
249+
**Why Stored Procedures are Used:**
250+
MySQL requires the full column definition when updating column comments via `ALTER TABLE ... MODIFY COLUMN`. To handle this complexity, the generator creates a stored procedure (`prisma_update_column_comment`) that dynamically retrieves the current column definition from `information_schema` and safely applies the comment update.
251+
252+
**Required MySQL Permissions:**
253+
- `CREATE ROUTINE` - Required to create stored procedures for column comments
254+
- `ALTER ROUTINE` - Required to modify stored procedures if needed
255+
256+
The generated SQL includes:
257+
- Direct `ALTER TABLE` statements for table comments
258+
- Stored procedures for column comment updates (automatically created and cleaned up)
259+
260+
Other databases may be available, but the above are the only ones tested.
245261

246262
## License
247263

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@
1313
"scripts": {
1414
"build": "rm -rf dist/ && node build.js",
1515
"prepare": "pnpm run build",
16+
"typecheck": "tsc --noEmit",
1617
"format": "prettier --write src/**",
1718
"lint": "eslint 'src/**/*.ts' --ignore-pattern 'src/__fixtures__/**'",
1819
"test": "vitest run",
1920
"test:cov": "vitest run --coverage",
21+
"db:init": "rm -rf prisma/migrations && prisma migrate reset --skip-generate",
2022
"db:migrate": "prisma migrate dev && prisma generate && pnpm run db:deploy",
21-
"db:reset": "prisma migrate reset",
22-
"db:deploy": "prisma migrate deploy"
23+
"db:deploy": "prisma migrate deploy",
24+
"db:init:mysql": "rm -rf prisma-mysql/migrations && dotenv -e .env.mysql -- prisma migrate reset --skip-generate --schema=prisma-mysql/schema.prisma",
25+
"db:migrate:mysql": "dotenv -e .env.mysql -- prisma migrate dev --schema=prisma-mysql/schema.prisma && dotenv -e .env.mysql -- prisma generate --schema=prisma-mysql/schema.prisma && pnpm run db:deploy:mysql",
26+
"db:deploy:mysql": "dotenv -e .env.mysql -- prisma migrate deploy --schema=prisma-mysql/schema.prisma"
2327
},
2428
"repository": {
2529
"type": "git",
@@ -42,6 +46,7 @@
4246
"@types/node": "^22.0.0",
4347
"@vitest/coverage-v8": "^3.2.0",
4448
"@vitest/eslint-plugin": "^1.2.1",
49+
"dotenv-cli": "^8.0.0",
4550
"esbuild": "^0.25.5",
4651
"eslint": "^9.28.0",
4752
"globals": "^15.8.0",

pnpm-lock.yaml

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

prisma-mysql/schema.prisma

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
generator client {
2+
provider = "prisma-client-js"
3+
previewFeatures = ["views"]
4+
}
5+
6+
generator comments {
7+
provider = "node ./dist/generator.cjs"
8+
ignorePattern = "_view$"
9+
includeEnumInFieldComment = true
10+
}
11+
12+
datasource db {
13+
provider = "mysql"
14+
url = env("DATABASE_URL")
15+
}
16+
17+
/// Product type enumeration
18+
enum ProductType {
19+
BOOK
20+
TOY
21+
FASHION
22+
23+
@@map("enum_product_type")
24+
}
25+
26+
/// Customer
27+
model Customer {
28+
/// Customer ID
29+
customerId Int @id @default(autoincrement()) @map("customer_id")
30+
/// Customer Name
31+
name String
32+
/// e-mail
33+
email String @unique
34+
createdAt DateTime @default(now()) @map("created_at")
35+
sales Sale[]
36+
37+
@@map("customers")
38+
}
39+
40+
/// Product's information
41+
model Product {
42+
/// Product ID
43+
productId Int @id @default(autoincrement()) @map("product_id")
44+
/// Product Name
45+
name String
46+
/// Product Type
47+
type ProductType
48+
/// Product Description
49+
description String?
50+
/// Price
51+
price Float
52+
createdAt DateTime @default(now()) @map("created_at")
53+
sales Sale[]
54+
55+
@@map("products")
56+
}
57+
58+
model Sale {
59+
/// Sale ID
60+
saleId Int @id @default(autoincrement()) @map("sale_id")
61+
customer Customer @relation(fields: [customerId], references: [customerId])
62+
/// Customer ID
63+
customerId Int @map("customer_id")
64+
product Product @relation(fields: [productId], references: [productId])
65+
/// Product ID
66+
productId Int @map("product_id")
67+
/// Quantity
68+
quantity Int
69+
/// Total Price
70+
totalPrice Float @map("total_price")
71+
createdAt DateTime @default(now()) @map("created_at")
72+
73+
@@map("sales")
74+
}
75+
76+
/// Customer Sale Summary View
77+
view CustomerSaleSummaryView {
78+
/// Customer ID
79+
customerId Int @id @map("customer_id")
80+
/// Customer Name
81+
name String
82+
/// Total
83+
totalPurchasePrice Int @map("total_purchase_price")
84+
85+
@@map("customer_sale_summaries_view")
86+
}

0 commit comments

Comments
 (0)