Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for generated columns #3168

Open
carlos0202 opened this issue Dec 12, 2022 · 4 comments · May be fixed by #3984
Open

Support for generated columns #3168

carlos0202 opened this issue Dec 12, 2022 · 4 comments · May be fixed by #3984

Comments

@carlos0202
Copy link
Contributor

I'm wonder if entgo supports generated columns as stated here. If so, is there any place on the documentation to configure a column to be generated always?

Thanks in advance for the help!!

@a8m
Copy link
Member

a8m commented Dec 23, 2022

Atlas supports it, but Ent does not support it yet. It should be added to Ent, and suggestions for API are welcome.

@Ambroos
Copy link

Ambroos commented Apr 12, 2023

I think sticking close to Atlas API-wise would be nice. A GeneratedAs on any field type that just takes the "as" SQL string similar to Atlas should suffice. And then not generate any fields on any mutations.

A temporary way to use generated columns

With hooks / diff hooks and some cheating I've been able to use PostgreSQL generated columns in one of my ents with no issues so far, although it really is a bit janky. We're also not using any of the GraphQL functionality in Ent (and don't plan to), so it may cause problems with that.

In my schema, field:

		field.Enum("status").
			Values("draft", "published", "archived").
			Immutable().
			Optional() // to skip Ent's validation on create

Hook to avoid people manually setting anything in the generated column:

		hook.On(
			// Forbid setting status, since it is generated
			func(next ent.Mutator) ent.Mutator {
				return hook.MyEntFunc(func(ctx context.Context, m *gen.MyEntMutation) (ent.Value, error) {
					_, exists := m.Status()
					if exists {
						return nil, fmt.Errorf("status is generated, cannot be updated manually")
					}
					return next.Mutate(ctx, m)
				})
			},
			ent.OpCreate|ent.OpUpdate|ent.OpUpdateOne,
		),

And then in my migrate/main.go, which generates an Atlas versioned migration:

    ...
    schema.WithDiffHook(statusDiffHook),
    ...

func statusDiffHook(next schema.Differ) schema.Differ {
	return schema.DiffFunc(func(current, desired *atlasSchema.Schema) ([]atlasSchema.Change, error) {
		for _, table := range desired.Tables {
			if table.Name == "my_ent" {
				for _, column := range table.Columns {
					if column.Name == "status" {
						column.SetNull(false)
						column.SetGeneratedExpr(&atlasSchema.GeneratedExpr{
							Expr: `CASE WHEN "archived_at" IS NOT NULL THEN 'archived' WHEN "published_at" IS NOT NULL THEN 'published' ELSE 'draft' END`,
						})
					}
				}
			}
		}

		changes, err := next.Diff(current, desired)
		if err != nil {
			return nil, err
		}

		return changes, nil
	})
}

I'm not sure how safe/smart it is to mutate the Atlas schema in a diff hook, but it works.

@hut8
Copy link

hut8 commented Jul 11, 2023

@Ambroos thanks a bunch for that snippet! @a8m I think that having GeneratedAs("[some sql here]") on a field definition would make a lot of sense. This is one of the pains of a full-text search currently. Without this, the migration generation always wipes out my GENERATED AS definition for my tsvector column.

@owenthereal
Copy link

I'm getting the following error the second time I ran the diff:

2023/10/27 10:29:29 ERROR  error="failed generating migration file: changing the generation expression for a column \"terms\" is not supported"

Ref: https://github.com/ariga/atlas/blob/a1ae701025dcb97a9da2aef11cbfc5d3cf92227c/sql/postgres/diff.go#L123

@ivanvanderbyl ivanvanderbyl linked a pull request Mar 21, 2024 that will close this issue
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants