* Fix pgsql reader double-quoting defaults: normalizePostgresDefault strips surrounding SQL string literal quotes from column_default before storing, matching the convention used by every other reader. * Add NullableTypes field to WriterOptions with NullableTypeResolveSpec (default) and NullableTypeStdlib constants. * Both bun and gorm TypeMappers now accept a typeStyle parameter. stdlib mode produces sql.NullString/NullInt32/NullTime etc. for nullable scalars, plain Go slices for arrays, and time.Time for NOT NULL timestamps. Default resolvespec behaviour is unchanged. * Add --types flag to convert and split commands. * Update bun/README.md and gorm/README.md with side-by-side generated code examples, updated type mapping tables, and Writer Options documentation.
212 lines
6.5 KiB
Markdown
212 lines
6.5 KiB
Markdown
# GORM Writer
|
|
|
|
Generates Go source files with GORM model definitions from database schema information.
|
|
|
|
## Overview
|
|
|
|
The GORM Writer converts RelSpec's internal database model representation into Go source code with GORM struct definitions, complete with proper tags, relationships, and methods.
|
|
|
|
## Features
|
|
|
|
- Generates GORM-compatible Go structs
|
|
- Creates proper `gorm` struct tags
|
|
- Generates `TableName()` methods
|
|
- Adds relationship fields (belongs-to, has-many)
|
|
- Supports both single-file and multi-file output
|
|
- Auto-generates helper methods (optional)
|
|
- Maps SQL types to Go types
|
|
- Handles nullable fields with custom sql_types
|
|
|
|
## Usage
|
|
|
|
### Basic Example
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/writers/gorm"
|
|
)
|
|
|
|
func main() {
|
|
// Assume db is a *models.Database from a reader
|
|
options := &writers.WriterOptions{
|
|
OutputPath: "models.go",
|
|
PackageName: "models",
|
|
}
|
|
|
|
writer := gorm.NewWriter(options)
|
|
err := writer.WriteDatabase(db)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
```
|
|
|
|
### CLI Examples
|
|
|
|
```bash
|
|
# Generate GORM models from a DBML schema (default: resolvespec types)
|
|
relspec convert --from dbml --from-path schema.dbml \
|
|
--to gorm --to-path models.go --package models
|
|
|
|
# Use standard library database/sql nullable types instead of resolvespec
|
|
relspec convert --from dbml --from-path schema.dbml \
|
|
--to gorm --to-path models.go --package models \
|
|
--types stdlib
|
|
|
|
# Explicitly select resolvespec types (same as omitting --types)
|
|
relspec convert --from pgsql --from-conn "postgres://localhost/mydb" \
|
|
--to gorm --to-path models.go --package models \
|
|
--types resolvespec
|
|
|
|
# Multi-file output (one file per table)
|
|
relspec convert --from json --from-path schema.json \
|
|
--to gorm --to-path models/ --package models
|
|
```
|
|
|
|
## Output Modes
|
|
|
|
### Single File Mode
|
|
|
|
Generates all models in one file:
|
|
|
|
```bash
|
|
relspec --input pgsql --conn "..." --output gorm --out-file models.go
|
|
```
|
|
|
|
### Multi-File Mode
|
|
|
|
Generates one file per table (auto-detected when output is a directory):
|
|
|
|
```bash
|
|
relspec --input pgsql --conn "..." --output gorm --out-file models/
|
|
```
|
|
|
|
Files are named: `sql_{schema}_{table}.go`
|
|
|
|
## Generated Code Examples
|
|
|
|
### Default — resolvespec types (`--types resolvespec`)
|
|
|
|
```go
|
|
package models
|
|
|
|
import (
|
|
sql_types "github.com/bitechdev/ResolveSpec/pkg/spectypes"
|
|
)
|
|
|
|
type ModelUser struct {
|
|
ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"`
|
|
Username string `gorm:"column:username;type:text;not null" json:"username"`
|
|
Email sql_types.SqlString `gorm:"column:email;type:text" json:"email,omitempty"`
|
|
Tags sql_types.SqlStringArray `gorm:"column:tags;type:text[];not null;default:'{}'" json:"tags"`
|
|
CreatedAt sql_types.SqlTimeStamp `gorm:"column:created_at;type:timestamptz;not null;default:now()" json:"created_at"`
|
|
}
|
|
|
|
func (ModelUser) TableName() string {
|
|
return "public.users"
|
|
}
|
|
```
|
|
|
|
### Standard library — `--types stdlib`
|
|
|
|
```go
|
|
package models
|
|
|
|
import (
|
|
"database/sql"
|
|
"time"
|
|
)
|
|
|
|
type ModelUser struct {
|
|
ID string `gorm:"column:id;type:uuid;primaryKey" json:"id"`
|
|
Username string `gorm:"column:username;type:text;not null" json:"username"`
|
|
Email sql.NullString `gorm:"column:email;type:text" json:"email,omitempty"`
|
|
Tags []string `gorm:"column:tags;type:text[];not null;default:'{}'" json:"tags"`
|
|
CreatedAt time.Time `gorm:"column:created_at;type:timestamptz;not null;default:now()" json:"created_at"`
|
|
}
|
|
|
|
func (ModelUser) TableName() string {
|
|
return "public.users"
|
|
}
|
|
```
|
|
|
|
## Writer Options
|
|
|
|
### NullableTypes
|
|
|
|
Controls which Go package is used for nullable column types. Set via the `--types` CLI flag or `WriterOptions.NullableTypes`:
|
|
|
|
```go
|
|
// Use resolvespec types (default — omit NullableTypes or set to "resolvespec")
|
|
options := &writers.WriterOptions{
|
|
OutputPath: "models.go",
|
|
PackageName: "models",
|
|
NullableTypes: writers.NullableTypeResolveSpec,
|
|
}
|
|
|
|
// Use standard library database/sql types
|
|
options := &writers.WriterOptions{
|
|
OutputPath: "models.go",
|
|
PackageName: "models",
|
|
NullableTypes: writers.NullableTypeStdlib,
|
|
}
|
|
```
|
|
|
|
### Metadata Options
|
|
|
|
Configure additional writer behavior using metadata in `WriterOptions`:
|
|
|
|
```go
|
|
options := &writers.WriterOptions{
|
|
OutputPath: "models.go",
|
|
PackageName: "models",
|
|
Metadata: map[string]any{
|
|
"multi_file": true, // Enable multi-file mode
|
|
"populate_refs": true, // Populate RefDatabase/RefSchema
|
|
"generate_get_id_str": true, // Generate GetIDStr() methods
|
|
},
|
|
}
|
|
```
|
|
|
|
## Type Mapping
|
|
|
|
The nullable type package is selected with `--types` (or `WriterOptions.NullableTypes`).
|
|
|
|
| SQL Type | NOT NULL — both | Nullable — resolvespec | Nullable — stdlib |
|
|
|---|---|---|---|
|
|
| `bigint` | `int64` | `SqlInt64` | `sql.NullInt64` |
|
|
| `integer` | `int32` | `SqlInt32` | `sql.NullInt32` |
|
|
| `smallint` | `int16` | `SqlInt16` | `sql.NullInt16` |
|
|
| `text`, `varchar` | `string` | `SqlString` | `sql.NullString` |
|
|
| `boolean` | `bool` | `SqlBool` | `sql.NullBool` |
|
|
| `timestamp`, `timestamptz` | `time.Time` | `SqlTimeStamp` | `sql.NullTime` |
|
|
| `numeric`, `decimal` | `float64` | `SqlFloat64` | `sql.NullFloat64` |
|
|
| `uuid` | `string` | `SqlUUID` | `sql.NullString` |
|
|
| `jsonb` | `string` | `SqlString` | `sql.NullString` |
|
|
| `text[]` | `SqlStringArray` | `SqlStringArray` | `[]string` |
|
|
| `integer[]` | `SqlInt32Array` | `SqlInt32Array` | `[]int32` |
|
|
| `uuid[]` | `SqlUUIDArray` | `SqlUUIDArray` | `[]string` |
|
|
| `vector` | `SqlVector` | `SqlVector` | `[]float32` |
|
|
|
|
## Relationship Generation
|
|
|
|
The writer automatically generates relationship fields:
|
|
|
|
- **Belongs-to**: Generated for tables with foreign keys
|
|
- **Has-many**: Generated for tables referenced by foreign keys
|
|
- Relationship field names use 3-letter prefixes
|
|
- Includes proper `gorm` tags with `foreignKey` and `references`
|
|
|
|
## Notes
|
|
|
|
- Model names are prefixed with "Model" (e.g., `ModelUser`)
|
|
- Nullable columns use `sql_types.SqlString`, `sql_types.SqlInt64`, etc. by default; pass `--types stdlib` to use `sql.NullString`, `sql.NullInt64`, etc. instead
|
|
- Array columns use `sql_types.SqlStringArray`, `sql_types.SqlInt32Array`, etc. by default; `--types stdlib` produces plain Go slices (`[]string`, `[]int32`, …)
|
|
- Generated code is auto-formatted with `go fmt`
|
|
- JSON tags are automatically added
|
|
- Supports schema-qualified table names in `TableName()` method
|