mirror of
https://github.com/ent/ent.git
synced 2026-04-28 05:30:56 +03:00
201 lines
5.5 KiB
Go
201 lines
5.5 KiB
Go
// Copyright 2019-present Facebook Inc. All rights reserved.
|
|
// This source code is licensed under the Apache 2.0 license found
|
|
// in the LICENSE file in the root directory of this source tree.
|
|
|
|
package gen
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
|
|
"entgo.io/ent/dialect/gremlin/graph/dsl"
|
|
"entgo.io/ent/dialect/sql"
|
|
)
|
|
|
|
// A SchemaMode defines what type of schema feature a storage driver support.
|
|
type SchemaMode uint
|
|
|
|
const (
|
|
// Unique defines field and edge uniqueness support.
|
|
Unique SchemaMode = 1 << iota
|
|
|
|
// Indexes defines indexes support.
|
|
Indexes
|
|
|
|
// Cascade defines cascading operations (e.g. cascade deletion).
|
|
Cascade
|
|
|
|
// Migrate defines static schema and migration support (e.g. SQL-based).
|
|
Migrate
|
|
)
|
|
|
|
// Support reports whether m support the given mode.
|
|
func (m SchemaMode) Support(mode SchemaMode) bool { return m&mode != 0 }
|
|
|
|
// Storage driver type for codegen.
|
|
type Storage struct {
|
|
Name string // storage name.
|
|
Builder reflect.Type // query builder type.
|
|
Dialects []string // supported dialects.
|
|
IdentName string // identifier name (fields and funcs).
|
|
Imports []string // import packages needed.
|
|
SchemaMode SchemaMode // schema mode support.
|
|
Ops func(*Field) []Op // storage specific operations.
|
|
OpCode func(Op) string // operation code for predicates.
|
|
Init func(*Graph) error // optional init function.
|
|
}
|
|
|
|
// StorageDrivers holds the storage driver options for entc.
|
|
var drivers = []*Storage{
|
|
{
|
|
Name: "sql",
|
|
IdentName: "SQL",
|
|
Builder: reflect.TypeOf(&sql.Selector{}),
|
|
Dialects: []string{"dialect.SQLite", "dialect.MySQL", "dialect.Postgres"},
|
|
Imports: []string{
|
|
"database/sql/driver",
|
|
"entgo.io/ent/dialect/sql",
|
|
"entgo.io/ent/dialect/sql/sqlgraph",
|
|
"entgo.io/ent/dialect/sql/sqljson",
|
|
"entgo.io/ent/schema/field",
|
|
},
|
|
SchemaMode: Unique | Indexes | Cascade | Migrate,
|
|
Ops: func(f *Field) []Op {
|
|
if f.IsString() && f.ConvertedToBasic() {
|
|
return []Op{EqualFold, ContainsFold}
|
|
}
|
|
return nil
|
|
},
|
|
OpCode: opCodes(sqlCode[:]),
|
|
Init: func(g *Graph) error {
|
|
var with, without []string
|
|
for _, n := range g.Nodes {
|
|
if s, err := n.TableSchema(); err == nil && s != "" {
|
|
with = append(with, n.Name)
|
|
} else {
|
|
without = append(without, n.Name)
|
|
}
|
|
}
|
|
switch {
|
|
case len(with) == 0:
|
|
return nil
|
|
case len(without) > 0:
|
|
return fmt.Errorf("missing schema annotation for %s", strings.Join(without, ", "))
|
|
default:
|
|
if !g.featureEnabled(FeatureSchemaConfig) {
|
|
g.Features = append(g.Features, FeatureSchemaConfig)
|
|
}
|
|
if !g.featureEnabled(featureMultiSchema) {
|
|
g.Features = append(g.Features, featureMultiSchema)
|
|
}
|
|
return nil
|
|
}
|
|
},
|
|
},
|
|
{
|
|
Name: "gremlin",
|
|
IdentName: "Gremlin",
|
|
Builder: reflect.TypeOf(&dsl.Traversal{}),
|
|
Dialects: []string{"dialect.Gremlin"},
|
|
Imports: []string{
|
|
"entgo.io/ent/dialect/gremlin",
|
|
"entgo.io/ent/dialect/gremlin/graph/dsl",
|
|
"entgo.io/ent/dialect/gremlin/graph/dsl/__",
|
|
"entgo.io/ent/dialect/gremlin/graph/dsl/g",
|
|
"entgo.io/ent/dialect/gremlin/graph/dsl/p",
|
|
"entgo.io/ent/dialect/gremlin/encoding/graphson",
|
|
},
|
|
SchemaMode: Unique,
|
|
OpCode: opCodes(gremlinCode[:]),
|
|
Init: func(*Graph) error { return nil }, // Noop.
|
|
},
|
|
}
|
|
|
|
// NewStorage returns the storage driver type from the given string.
|
|
// It fails if the provided string is not a valid option. this function
|
|
// is here in order to remove the validation logic from entc command line.
|
|
func NewStorage(s string) (*Storage, error) {
|
|
for _, d := range drivers {
|
|
if s == d.Name {
|
|
return d, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("entc/gen: invalid storage driver %q", s)
|
|
}
|
|
|
|
// String implements the fmt.Stringer interface for template usage.
|
|
func (s *Storage) String() string { return s.Name }
|
|
|
|
var (
|
|
// exceptional operation names in sql.
|
|
sqlCode = [...]string{
|
|
IsNil: "IsNull",
|
|
NotNil: "NotNull",
|
|
}
|
|
// exceptional operation names in gremlin.
|
|
gremlinCode = [...]string{
|
|
IsNil: "HasNot",
|
|
NotNil: "Has",
|
|
In: "Within",
|
|
NotIn: "Without",
|
|
Contains: "Containing",
|
|
HasPrefix: "StartingWith",
|
|
HasSuffix: "EndingWith",
|
|
}
|
|
)
|
|
|
|
func opCodes(codes []string) func(Op) string {
|
|
return func(o Op) string {
|
|
if int(o) < len(codes) && codes[o] != "" {
|
|
return codes[o]
|
|
}
|
|
return o.Name()
|
|
}
|
|
}
|
|
|
|
// TableSchemas returns all table schemas in ent/schema (intentionally exported).
|
|
func (g *Graph) TableSchemas() ([]string, error) {
|
|
all := make(map[string]struct{})
|
|
for _, n := range g.Nodes {
|
|
s, err := n.TableSchema()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
all[s] = struct{}{}
|
|
for _, e := range n.Edges {
|
|
// {{- if and $e.M2M (not $e.Inverse) (not $e.Through) }}
|
|
if e.M2M() && !e.IsInverse() && e.Through == nil {
|
|
s, err := e.TableSchema()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
all[s] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
return slices.Sorted(maps.Keys(all)), nil
|
|
}
|
|
|
|
// TableSchema returns the schema name of where the type table resides (intentionally exported).
|
|
func (t *Type) TableSchema() (string, error) {
|
|
switch ant := t.EntSQL(); {
|
|
case ant == nil || ant.Schema == "":
|
|
return "", fmt.Errorf("atlas: missing schema annotation for node %q", t.Name)
|
|
default:
|
|
return ant.Schema, nil
|
|
}
|
|
}
|
|
|
|
// TableSchema returns the schema name of where the type table resides (intentionally exported).
|
|
func (e *Edge) TableSchema() (string, error) {
|
|
switch ant := e.EntSQL(); {
|
|
case ant == nil || ant.Schema == "":
|
|
return e.Owner.TableSchema()
|
|
default:
|
|
return ant.Schema, nil
|
|
}
|
|
}
|