feat(bun): generate native Go array slices for PostgreSQL array columns
Bun's pgdialect scans/appends native slices directly, so array columns
(text[], integer[], uuid[], ...) always generate as plain []string,
[]int32, etc. with an explicit "array" bun tag, regardless of --types
(sqltypes/stdlib/baselib). The SqlXxxArray wrapper types are no longer
used for Bun array columns (gorm is unaffected and keeps using them).
Adds --array-nullable pointer_slice to represent nullable array columns
as *[]T instead of []T, so callers can distinguish SQL NULL (nil) from
'{}' (pointer to an empty slice). Verified end-to-end against a live
PostgreSQL instance for NULL/{}/populated arrays in every --types mode.
Closes #13
This commit is contained in:
@@ -54,6 +54,7 @@ var (
|
||||
convertSchemaFilter string
|
||||
convertFlattenSchema bool
|
||||
convertNullableTypes string
|
||||
convertNullableArrays string
|
||||
convertContinueOnError bool
|
||||
convertExtraFields string
|
||||
)
|
||||
@@ -180,6 +181,7 @@ func init() {
|
||||
convertCmd.Flags().StringVar(&convertSchemaFilter, "schema", "", "Filter to a specific schema by name (required for formats like dctx that only support single schemas)")
|
||||
convertCmd.Flags().BoolVar(&convertFlattenSchema, "flatten-schema", false, "Flatten schema.table names to schema_table (useful for databases like SQLite that do not support schemas)")
|
||||
convertCmd.Flags().StringVar(&convertNullableTypes, "types", "", "Nullable type package for code-gen writers (bun/gorm): 'baselib' (default, Go pointer types), 'stdlib' (database/sql), or 'sqltypes'")
|
||||
convertCmd.Flags().StringVar(&convertNullableArrays, "array-nullable", "", "Nullable PostgreSQL array representation for the Bun writer in stdlib/baselib --types mode: 'slice' (default, plain slice) or 'pointer_slice' (*[]T, distinguishes NULL from '{}')")
|
||||
convertCmd.Flags().BoolVar(&convertContinueOnError, "continue-on-error", false, "Prepend \\set ON_ERROR_STOP off to generated SQL so psql continues past errors (pgsql output only)")
|
||||
convertCmd.Flags().StringVar(&convertExtraFields, "extra-fields", "", "Path to JSON file containing extra Bun model fields to inject (bun output only); fields support target_table, name, type, bun_tag, json_tag, comment")
|
||||
|
||||
@@ -248,7 +250,7 @@ func runConvert(cmd *cobra.Command, args []string) error {
|
||||
fmt.Fprintf(os.Stderr, " Schema: %s\n", convertSchemaFilter)
|
||||
}
|
||||
|
||||
if err := writeDatabase(db, convertTargetType, convertTargetPath, convertPackageName, convertSchemaFilter, convertFlattenSchema, convertNullableTypes, convertContinueOnError, convertExtraFields); err != nil {
|
||||
if err := writeDatabase(db, convertTargetType, convertTargetPath, convertPackageName, convertSchemaFilter, convertFlattenSchema, convertNullableTypes, convertNullableArrays, convertContinueOnError, convertExtraFields); err != nil {
|
||||
return fmt.Errorf("failed to write target: %w", err)
|
||||
}
|
||||
|
||||
@@ -388,10 +390,10 @@ func readDatabaseForConvert(dbType, filePath, connString string) (*models.Databa
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func writeDatabase(db *models.Database, dbType, outputPath, packageName, schemaFilter string, flattenSchema bool, nullableTypes string, continueOnError bool, extraFields string) error {
|
||||
func writeDatabase(db *models.Database, dbType, outputPath, packageName, schemaFilter string, flattenSchema bool, nullableTypes, nullableArrays string, continueOnError bool, extraFields string) error {
|
||||
var writer writers.Writer
|
||||
|
||||
writerOpts := newWriterOptions(outputPath, packageName, flattenSchema, nullableTypes, continueOnError)
|
||||
writerOpts := newWriterOptions(outputPath, packageName, flattenSchema, nullableTypes, nullableArrays, continueOnError)
|
||||
if extraFields != "" {
|
||||
if !strings.EqualFold(dbType, "bun") {
|
||||
return fmt.Errorf("--extra-fields is only supported for Bun output")
|
||||
|
||||
+13
-13
@@ -323,31 +323,31 @@ func writeDatabaseForEdit(dbType, filePath, connString string, db *models.Databa
|
||||
|
||||
switch strings.ToLower(dbType) {
|
||||
case "dbml":
|
||||
writer = wdbml.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wdbml.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "dctx":
|
||||
writer = wdctx.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wdctx.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "drawdb":
|
||||
writer = wdrawdb.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wdrawdb.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "graphql":
|
||||
writer = wgraphql.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wgraphql.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "json":
|
||||
writer = wjson.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wjson.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "yaml":
|
||||
writer = wyaml.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wyaml.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "gorm":
|
||||
writer = wgorm.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wgorm.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "bun":
|
||||
writer = wbun.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wbun.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "drizzle":
|
||||
writer = wdrizzle.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wdrizzle.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "prisma":
|
||||
writer = wprisma.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wprisma.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "typeorm":
|
||||
writer = wtypeorm.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wtypeorm.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "sqlite", "sqlite3":
|
||||
writer = wsqlite.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wsqlite.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
case "pgsql":
|
||||
writer = wpgsql.NewWriter(newWriterOptions(filePath, "", false, "", false))
|
||||
writer = wpgsql.NewWriter(newWriterOptions(filePath, "", false, "", "", false))
|
||||
default:
|
||||
return fmt.Errorf("%s: unsupported format: %s", label, dbType)
|
||||
}
|
||||
|
||||
+13
-13
@@ -375,61 +375,61 @@ func writeDatabaseForMerge(dbType, filePath, connString string, db *models.Datab
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for DBML format", label)
|
||||
}
|
||||
writer = wdbml.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wdbml.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "dctx":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for DCTX format", label)
|
||||
}
|
||||
writer = wdctx.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wdctx.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "drawdb":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for DrawDB format", label)
|
||||
}
|
||||
writer = wdrawdb.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wdrawdb.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "graphql":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for GraphQL format", label)
|
||||
}
|
||||
writer = wgraphql.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wgraphql.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "json":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for JSON format", label)
|
||||
}
|
||||
writer = wjson.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wjson.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "yaml":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for YAML format", label)
|
||||
}
|
||||
writer = wyaml.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wyaml.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "gorm":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for GORM format", label)
|
||||
}
|
||||
writer = wgorm.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wgorm.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "bun":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for Bun format", label)
|
||||
}
|
||||
writer = wbun.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wbun.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "drizzle":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for Drizzle format", label)
|
||||
}
|
||||
writer = wdrizzle.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wdrizzle.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "prisma":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for Prisma format", label)
|
||||
}
|
||||
writer = wprisma.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wprisma.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "typeorm":
|
||||
if filePath == "" {
|
||||
return fmt.Errorf("%s: file path is required for TypeORM format", label)
|
||||
}
|
||||
writer = wtypeorm.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wtypeorm.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "sqlite", "sqlite3":
|
||||
writer = wsqlite.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", false))
|
||||
writer = wsqlite.NewWriter(newWriterOptions(filePath, "", flattenSchema, "", "", false))
|
||||
case "pgsql":
|
||||
writerOpts := newWriterOptions(filePath, "", flattenSchema, "", false)
|
||||
writerOpts := newWriterOptions(filePath, "", flattenSchema, "", "", false)
|
||||
if connString != "" {
|
||||
writerOpts.Metadata = map[string]interface{}{
|
||||
"connection_string": connString,
|
||||
|
||||
@@ -13,12 +13,13 @@ func newReaderOptions(filePath, connString string) *readers.ReaderOptions {
|
||||
}
|
||||
}
|
||||
|
||||
func newWriterOptions(outputPath, packageName string, flattenSchema bool, nullableTypes string, continueOnError bool) *writers.WriterOptions {
|
||||
func newWriterOptions(outputPath, packageName string, flattenSchema bool, nullableTypes, nullableArrays string, continueOnError bool) *writers.WriterOptions {
|
||||
return &writers.WriterOptions{
|
||||
OutputPath: outputPath,
|
||||
PackageName: packageName,
|
||||
FlattenSchema: flattenSchema,
|
||||
NullableTypes: nullableTypes,
|
||||
NullableArrays: nullableArrays,
|
||||
Prisma7: prisma7,
|
||||
ContinueOnError: continueOnError,
|
||||
}
|
||||
|
||||
+15
-12
@@ -11,18 +11,19 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
splitSourceType string
|
||||
splitSourcePath string
|
||||
splitSourceConn string
|
||||
splitTargetType string
|
||||
splitTargetPath string
|
||||
splitSchemas string
|
||||
splitTables string
|
||||
splitPackageName string
|
||||
splitDatabaseName string
|
||||
splitExcludeSchema string
|
||||
splitExcludeTables string
|
||||
splitNullableTypes string
|
||||
splitSourceType string
|
||||
splitSourcePath string
|
||||
splitSourceConn string
|
||||
splitTargetType string
|
||||
splitTargetPath string
|
||||
splitSchemas string
|
||||
splitTables string
|
||||
splitPackageName string
|
||||
splitDatabaseName string
|
||||
splitExcludeSchema string
|
||||
splitExcludeTables string
|
||||
splitNullableTypes string
|
||||
splitNullableArrays string
|
||||
)
|
||||
|
||||
var splitCmd = &cobra.Command{
|
||||
@@ -112,6 +113,7 @@ func init() {
|
||||
splitCmd.Flags().StringVar(&splitExcludeSchema, "exclude-schema", "", "Comma-separated list of schema names to exclude")
|
||||
splitCmd.Flags().StringVar(&splitExcludeTables, "exclude-tables", "", "Comma-separated list of table names to exclude (case-insensitive)")
|
||||
splitCmd.Flags().StringVar(&splitNullableTypes, "types", "", "Nullable type package for code-gen writers (bun/gorm): 'baselib' (default, Go pointer types), 'stdlib' (database/sql), or 'sqltypes'")
|
||||
splitCmd.Flags().StringVar(&splitNullableArrays, "array-nullable", "", "Nullable PostgreSQL array representation for the Bun writer in stdlib/baselib --types mode: 'slice' (default, plain slice) or 'pointer_slice' (*[]T, distinguishes NULL from '{}')")
|
||||
|
||||
err := splitCmd.MarkFlagRequired("from")
|
||||
if err != nil {
|
||||
@@ -188,6 +190,7 @@ func runSplit(cmd *cobra.Command, args []string) error {
|
||||
"", // no schema filter for split
|
||||
false, // no flatten-schema for split
|
||||
splitNullableTypes,
|
||||
splitNullableArrays,
|
||||
false, // no continue-on-error for split
|
||||
"", // no extra fields for split
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user