volatiletech / sqlboiler

Generate a Go ORM tailored to your database schema.
BSD 3-Clause "New" or "Revised" License
6.73k stars 544 forks source link

Make column sorting optional #532

Closed zikaeroh closed 5 years ago

zikaeroh commented 5 years ago

If you're having a generation problem please answer these questions before submitting your issue. Thanks!

What version of SQLBoiler are you using (sqlboiler --version)?

SQLBoiler v3.3.0

If this happened at generation time what was the full SQLBoiler command you used to generate your models? (if not applicable leave blank)

gobin -m -run github.com/volatiletech/sqlboiler ./scripts/sqlboiler-psql --wipe --no-hooks --no-rows-affected --no-tests

(I use gobin plus a script which execs gobin again for sqlboiler-psql, pinning all my deps in go.mod; this isn't important to this issue.)

If this happened at runtime what code produced the issue? (if not applicable leave blank)

-

What is the output of the command above with the -d flag added to it? (Provided you are comfortable sharing this, it contains a blueprint of your schema)

Don't think this is needed.

Please provide a relevant database schema so we can replicate your issue (Provided you are comfortable sharing this)

CREATE TABLE channels (
    id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    created_at timestamptz DEFAULT NOW() NOT NULL,
    updated_at timestamptz DEFAULT NOW() NOT NULL,

    user_id bigint NOT NULL UNIQUE,
    name text NOT NULL,
    bot_name text NOT NULL,
    active boolean NOT NULL,
    prefix text NOT NULL,
    bullet text,

    custom_owners text[] DEFAULT '{}' NOT NULL,
    custom_mods text[] DEFAULT '{}' NOT NULL,
    custom_regulars text[] DEFAULT '{}' NOT NULL
);

Further information. What did you do, what did you expect?

I would have expected my generated Go types to have the same field order between SQLBoiler versions.

PR #475 made generation "idempotent". While probably a good change for table ordering, since columns are now sorted there are a few issues:

  1. Types generated by 3.3.0 are technically not backwards compatible. Since the struct field ordering has changed, if you haven't explicitly named the fields in a literal, then the order changing means that existing code will break when the models are regenerated (e.g. {123, "foo"} might be okay before, but maybe not after). Linters heavily warn against omitting the names for exactly this reason, so this isn't a big deal. However...

  2. The generated structs are a lot less "nice" looking. For my schema above, 3.2.0 generates the type:

// Channel is an object representing the database table.
type Channel struct {
    ID             int64             `boil:"id" json:"id" toml:"id" yaml:"id"`
    CreatedAt      time.Time         `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
    UpdatedAt      time.Time         `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
    UserID         int64             `boil:"user_id" json:"user_id" toml:"user_id" yaml:"user_id"`
    Name           string            `boil:"name" json:"name" toml:"name" yaml:"name"`
    BotName        string            `boil:"bot_name" json:"bot_name" toml:"bot_name" yaml:"bot_name"`
    Active         bool              `boil:"active" json:"active" toml:"active" yaml:"active"`
    Prefix         string            `boil:"prefix" json:"prefix" toml:"prefix" yaml:"prefix"`
    Bullet         null.String       `boil:"bullet" json:"bullet,omitempty" toml:"bullet" yaml:"bullet,omitempty"`
    CustomOwners   types.StringArray `boil:"custom_owners" json:"custom_owners" toml:"custom_owners" yaml:"custom_owners"`
    CustomMods     types.StringArray `boil:"custom_mods" json:"custom_mods" toml:"custom_mods" yaml:"custom_mods"`
    CustomRegulars types.StringArray `boil:"custom_regulars" json:"custom_regulars" toml:"custom_regulars" yaml:"custom_regulars"`

    R *channelR `boil:"-" json:"-" toml:"-" yaml:"-"`
    L channelL  `boil:"-" json:"-" toml:"-" yaml:"-"`
}

The ID field comes first, and the ordering is what I wrote in my schema. But 3.3.0 generates:

// Channel is an object representing the database table.
type Channel struct {
    Active         bool              `boil:"active" json:"active" toml:"active" yaml:"active"`
    BotName        string            `boil:"bot_name" json:"bot_name" toml:"bot_name" yaml:"bot_name"`
    Bullet         null.String       `boil:"bullet" json:"bullet,omitempty" toml:"bullet" yaml:"bullet,omitempty"`
    CreatedAt      time.Time         `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
    CustomMods     types.StringArray `boil:"custom_mods" json:"custom_mods" toml:"custom_mods" yaml:"custom_mods"`
    CustomOwners   types.StringArray `boil:"custom_owners" json:"custom_owners" toml:"custom_owners" yaml:"custom_owners"`
    CustomRegulars types.StringArray `boil:"custom_regulars" json:"custom_regulars" toml:"custom_regulars" yaml:"custom_regulars"`
    ID             int64             `boil:"id" json:"id" toml:"id" yaml:"id"`
    Name           string            `boil:"name" json:"name" toml:"name" yaml:"name"`
    Prefix         string            `boil:"prefix" json:"prefix" toml:"prefix" yaml:"prefix"`
    UpdatedAt      time.Time         `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
    UserID         int64             `boil:"user_id" json:"user_id" toml:"user_id" yaml:"user_id"`

    R *channelR `boil:"-" json:"-" toml:"-" yaml:"-"`
    L channelL  `boil:"-" json:"-" toml:"-" yaml:"-"`
}

As it's sorted, this ordering has nothing to do with how my table is constructed. The ID field is now somewhere in the middle (which is weird). I usually write my schemas in somewhat of an order of importance (before migrations change things up), so having this order changed on me doesn't feel very good.

I haven't yet seen a case where modifying a table changes the orders of the columns, which seems to have been a motivating factor of #475. The order in which the types and their methods are defined in the final output doesn't matter; godoc will sort them out, but the fields themselves are a core part of using the models and I believe take a bit more care.

Can the sorting of fields be optional so I can retain the old behavior? (Or maybe some solution in the middle where they'll be reordered back before being written?)

aarondl commented 5 years ago

I appreciate the concern. I didn't think this through too much. I do think a mini-reversion is necessary. This feature will either be hidden behind a flag, or more hopefully, information schema will have an ordinal type column we can use to derive consistent order from there and not have to rely on alpha sorting.

aarondl commented 5 years ago

This should order the columns in the way you expect as well as give us idempotency now. Done as I suggested (ordinal index in information_schema). Thanks for reporting the issue.

zikaeroh commented 5 years ago

Thank you!