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

Blogpost #3

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0eaa78f
dialect/sql/sqljson: add option to evaluate json path inside a predic…
ronenlu Oct 2, 2022
35e950e
contributing: add sqljson to doc
a8m Oct 2, 2022
a8dde5e
dialect/sql/sqlgraph: move fields setters to method calls (#2995)
a8m Oct 8, 2022
713828b
Remove unused variable and type redundancy (#2996)
marwan-at-work Oct 8, 2022
a26e21f
doc: update sqlite migration section (#2999)
a8m Oct 9, 2022
cf137c6
dialect/sql/sqljson: cast marshaled args as json (#3008)
a8m Oct 11, 2022
96e1325
doc/website/blog: json append blogpost (#3006)
rotemtam Oct 11, 2022
0911420
doc/website/blog: fixes to json append post (#3009)
rotemtam Oct 11, 2022
a1e6f06
go: upgrade ariga.io/atlas (#3010)
a8m Oct 11, 2022
fe4b19a
dialect/sql: add Window function to be able to customize function cla…
timoha Oct 11, 2022
f674c72
build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.0 (#3011)
dependabot[bot] Oct 12, 2022
9ff209d
doc: remove executable flag for md, mdx, png, js, and .gitignore file…
CyrilBrulebois Oct 16, 2022
ac725a6
entc: add `BuildFlags` to schema loader configuration (#2801)
joewreschnig Oct 16, 2022
bbdfea3
dialect/sql: support the RETURNING clause in UPDATE (#3016)
a8m Oct 16, 2022
8d40edf
dialect/sql: add support for LIMIT in update statement (#3018)
a8m Oct 17, 2022
d17c28b
doc: update tutorial-todo-gql-field-collection.md (#3021)
shiroemons Oct 17, 2022
1bc4d48
dialect/sql: support scanning json fields (#3022)
a8m Oct 18, 2022
765ec09
entc/gen: add Aggregate to <T>Select and <T>Query
a8m Oct 22, 2022
1d95710
doc/aggregate: add examples for <T>Query.Aggregate
a8m Oct 22, 2022
3e596ce
build(deps): bump github.com/spf13/cobra from 1.6.0 to 1.6.1
dependabot[bot] Oct 25, 2022
2d22f7e
build(deps): bump golangci/golangci-lint-action from 3.2.0 to 3.3.0
dependabot[bot] Oct 24, 2022
fc9a4f9
dialect/sql: add example for OrderExpr on postgres
a8m Oct 26, 2022
3ddcb76
entc/gen: fix codegen typo
a8m Oct 26, 2022
f1223dc
build(deps): bump github.com/mattn/go-sqlite3 from 1.14.15 to 1.14.16
dependabot[bot] Oct 27, 2022
e7ce44a
dialect/sql: support mapping non-lowercased tags
a8m Oct 27, 2022
c063978
entc/gen: ensure edge.StorageKey and edge.Field do not conflict
a8m Oct 29, 2022
521f9b5
entc/gen: copy annotations to the Through meta-edge
a8m Oct 31, 2022
35d0d4c
dialect/sql/sqlgraph: handle edge-schema in M2O/O2Oi calls entql
a8m Oct 28, 2022
5d4f026
dialect/sql/sqljson: add ValueIsNotNull predicate (#3058)
ronenlu Nov 2, 2022
a91aabe
entc/gen: ensure foreign-key constraints are unique in codegen (#3066)
a8m Nov 5, 2022
57b9745
doc/website/blog: add new article
ronenlu Nov 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
with:
go-version: 1.19
- name: Run linters
uses: golangci/golangci-lint-action@v3.2.0
uses: golangci/golangci-lint-action@v3.3.0
with:
version: v1.48.0

Expand Down
4 changes: 2 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ linters-settings:
dupl:
threshold: 100
funlen:
lines: 115
statements: 115
lines: 120
statements: 120
goheader:
template: |-
Copyright 2019-present Facebook Inc. All rights reserved.
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ possible.

- `dialect` - Contains SQL and Gremlin code used by the generated code.
- `dialect/sql/schema` - Auto migration logic resides there.
- `dialect/sql/sqljson` - JSON extension for SQL.

- `schema` - User schema API.
- `schema/{field, edge, index, mixin}` - provides schema builders API.
Expand Down
172 changes: 118 additions & 54 deletions dialect/sql/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,8 @@ func (i *InsertBuilder) Default() *InsertBuilder {
return i
}

// Returning adds the `RETURNING` clause to the insert statement. PostgreSQL only.
// Returning adds the `RETURNING` clause to the insert statement.
// Supported by SQLite and PostgreSQL.
func (i *InsertBuilder) Returning(columns ...string) *InsertBuilder {
i.returning = columns
return i
Expand Down Expand Up @@ -948,92 +949,92 @@ func (u *UpdateSet) SetExcluded(name string) *UpdateSet {

// Query returns query representation of an `INSERT INTO` statement.
func (i *InsertBuilder) Query() (string, []any) {
i.WriteString("INSERT INTO ")
i.writeSchema(i.schema)
i.Ident(i.table).Pad()
b := i.Builder.clone()
b.WriteString("INSERT INTO ")
b.writeSchema(i.schema)
b.Ident(i.table).Pad()
if i.defaults && len(i.columns) == 0 {
i.writeDefault()
i.writeDefault(&b)
} else {
i.WriteByte('(').IdentComma(i.columns...).WriteByte(')')
i.WriteString(" VALUES ")
b.WriteByte('(').IdentComma(i.columns...).WriteByte(')')
b.WriteString(" VALUES ")
for j, v := range i.values {
if j > 0 {
i.Comma()
b.Comma()
}
i.WriteByte('(').Args(v...).WriteByte(')')
b.WriteByte('(').Args(v...).WriteByte(')')
}
}
if i.conflict != nil {
i.writeConflict()
}
if len(i.returning) > 0 && !i.mysql() {
i.WriteString(" RETURNING ")
i.IdentComma(i.returning...)
i.writeConflict(&b)
}
return i.String(), i.args
joinReturning(i.returning, &b)
return b.String(), b.args
}

func (i *InsertBuilder) writeDefault() {
func (i *InsertBuilder) writeDefault(b *Builder) {
switch i.Dialect() {
case dialect.MySQL:
i.WriteString("VALUES ()")
b.WriteString("VALUES ()")
case dialect.SQLite, dialect.Postgres:
i.WriteString("DEFAULT VALUES")
b.WriteString("DEFAULT VALUES")
}
}

func (i *InsertBuilder) writeConflict() {
func (i *InsertBuilder) writeConflict(b *Builder) {
switch i.Dialect() {
case dialect.MySQL:
i.WriteString(" ON DUPLICATE KEY UPDATE ")
b.WriteString(" ON DUPLICATE KEY UPDATE ")
if i.conflict.action.nothing {
i.AddError(fmt.Errorf("invalid CONFLICT action ('DO NOTHING')"))
b.AddError(fmt.Errorf("invalid CONFLICT action ('DO NOTHING')"))
}
case dialect.SQLite, dialect.Postgres:
i.WriteString(" ON CONFLICT")
b.WriteString(" ON CONFLICT")
switch t := i.conflict.target; {
case t.constraint != "" && len(t.columns) != 0:
i.AddError(fmt.Errorf("duplicate CONFLICT clauses: %q, %q", t.constraint, t.columns))
b.AddError(fmt.Errorf("duplicate CONFLICT clauses: %q, %q", t.constraint, t.columns))
case t.constraint != "":
i.WriteString(" ON CONSTRAINT ").Ident(t.constraint)
b.WriteString(" ON CONSTRAINT ").Ident(t.constraint)
case len(t.columns) != 0:
i.WriteString(" (").IdentComma(t.columns...).WriteByte(')')
b.WriteString(" (").IdentComma(t.columns...).WriteByte(')')
}
if p := i.conflict.target.where; p != nil {
i.WriteString(" WHERE ").Join(p)
b.WriteString(" WHERE ").Join(p)
}
if i.conflict.action.nothing {
i.WriteString(" DO NOTHING")
b.WriteString(" DO NOTHING")
return
}
i.WriteString(" DO UPDATE SET ")
b.WriteString(" DO UPDATE SET ")
}
if len(i.conflict.action.update) == 0 {
i.AddError(errors.New("missing action for 'DO UPDATE SET' clause"))
b.AddError(errors.New("missing action for 'DO UPDATE SET' clause"))
}
u := &UpdateSet{columns: i.columns, update: Dialect(i.dialect).Update(i.table)}
u.update.Builder = i.Builder
u.update.Builder = *b
for _, f := range i.conflict.action.update {
f(u)
}
u.update.writeSetter(&i.Builder)
u.update.writeSetter(b)
if p := i.conflict.action.where; p != nil {
p.qualifier = i.table
i.WriteString(" WHERE ").Join(p)
b.WriteString(" WHERE ").Join(p)
}
}

// UpdateBuilder is a builder for `UPDATE` statement.
type UpdateBuilder struct {
Builder
table string
schema string
where *Predicate
nulls []string
columns []string
values []any
order []any
prefix Queries
table string
schema string
where *Predicate
nulls []string
columns []string
returning []string
values []any
order []any
limit *int
prefix Queries
}

// Update creates a builder for the `UPDATE` statement.
Expand Down Expand Up @@ -1119,12 +1120,30 @@ func (u *UpdateBuilder) OrderBy(columns ...string) *UpdateBuilder {
return u
}

// Limit appends the `LIMIT` clause to the `UPDATE` statement.
// Supported by SQLite and MySQL.
func (u *UpdateBuilder) Limit(limit int) *UpdateBuilder {
if u.postgres() {
u.AddError(errors.New("LIMIT is not supported by PostgreSQL"))
return u
}
u.limit = &limit
return u
}

// Prefix prefixes the UPDATE statement with list of statements.
func (u *UpdateBuilder) Prefix(stmts ...Querier) *UpdateBuilder {
u.prefix = append(u.prefix, stmts...)
return u
}

// Returning adds the `RETURNING` clause to the insert statement.
// Supported by SQLite and PostgreSQL.
func (u *UpdateBuilder) Returning(columns ...string) *UpdateBuilder {
u.returning = columns
return u
}

// Query returns query representation of an `UPDATE` statement.
func (u *UpdateBuilder) Query() (string, []any) {
b := u.Builder.clone()
Expand All @@ -1140,7 +1159,12 @@ func (u *UpdateBuilder) Query() (string, []any) {
b.WriteString(" WHERE ")
b.Join(u.where)
}
joinReturning(u.returning, &b)
joinOrder(u.order, &b)
if u.limit != nil {
b.WriteString(" LIMIT ")
b.WriteString(strconv.Itoa(*u.limit))
}
return b.String(), b.args
}

Expand Down Expand Up @@ -2835,6 +2859,14 @@ func joinOrder(order []any, b *Builder) {
}
}

func joinReturning(columns []string, b *Builder) {
if len(columns) == 0 || (!b.postgres() && !b.sqlite()) {
return
}
b.WriteString(" RETURNING ")
b.IdentComma(columns...)
}

func (s *Selector) joinSelect(b *Builder) {
for i := range s.selection {
if i > 0 {
Expand Down Expand Up @@ -2951,7 +2983,7 @@ func (*WithBuilder) view() {}
// only to query rows-limited edges in pagination.
type WindowBuilder struct {
Builder
fn string // e.g. ROW_NUMBER(), RANK().
fn func(*Builder) // e.g. ROW_NUMBER(), RANK()
partition func(*Builder)
order []any
}
Expand All @@ -2960,7 +2992,19 @@ type WindowBuilder struct {
// Using this function will assign a each row a number, from 1 to N, in the
// order defined by the ORDER BY clause in the window spec.
func RowNumber() *WindowBuilder {
return &WindowBuilder{fn: "ROW_NUMBER"}
return Window(func(b *Builder) {
b.WriteString("ROW_NUMBER()")
})
}

// Window returns a new window clause with a custom selector allowing
// for custom windown functions.
//
// Window(func(b *Builder) {
// b.WriteString(Sum(posts.C("duration")))
// }).PartitionBy("author_id").OrderBy("id"), "duration").
func Window(fn func(*Builder)) *WindowBuilder {
return &WindowBuilder{fn: fn}
}

// PartitionBy indicates to divide the query rows into groups by the given columns.
Expand Down Expand Up @@ -3000,8 +3044,8 @@ func (w *WindowBuilder) OrderExpr(exprs ...Querier) *WindowBuilder {

// Query returns query representation of the window function.
func (w *WindowBuilder) Query() (string, []any) {
w.WriteString(w.fn)
w.WriteString("() OVER ")
w.fn(&w.Builder)
w.WriteString(" OVER ")
w.Wrap(func(b *Builder) {
if w.partition != nil {
b.WriteString("PARTITION BY ")
Expand Down Expand Up @@ -3331,22 +3375,19 @@ func (b *Builder) Arg(a any) *Builder {
b.Join(a)
return b
}
b.total++
b.args = append(b.args, a)
// Default placeholder param (MySQL and SQLite).
param := "?"
format := "?"
if b.postgres() {
// Postgres' arguments are referenced using the syntax $n.
// $1 refers to the 1st argument, $2 to the 2nd, and so on.
param = "$" + strconv.Itoa(b.total)
format = "$" + strconv.Itoa(b.total+1)
}
if f, ok := a.(ParamFormatter); ok {
param = f.FormatParam(param, &StmtInfo{
format = f.FormatParam(format, &StmtInfo{
Dialect: b.dialect,
})
}
b.WriteString(param)
return b
return b.Argf(format, a)
}

// Args appends a list of arguments to the builder.
Expand All @@ -3360,6 +3401,29 @@ func (b *Builder) Args(a ...any) *Builder {
return b
}

// Argf appends an input argument to the builder
// with the given format. For example:
//
// FormatArg("JSON(?)", b).
// FormatArg("ST_GeomFromText(?)", geom)
func (b *Builder) Argf(format string, a any) *Builder {
switch a := a.(type) {
case nil:
b.WriteString("NULL")
return b
case *raw:
b.WriteString(a.s)
return b
case Querier:
b.Join(a)
return b
}
b.total++
b.args = append(b.args, a)
b.WriteString(format)
return b
}

// Comma adds a comma to the query.
func (b *Builder) Comma() *Builder {
return b.WriteString(", ")
Expand Down Expand Up @@ -3466,9 +3530,9 @@ func (b Builder) postgres() bool {
return b.Dialect() == dialect.Postgres
}

// mysql reports if the builder dialect is MySQL.
func (b Builder) mysql() bool {
return b.Dialect() == dialect.MySQL
// sqlite reports if the builder dialect is SQLite.
func (b Builder) sqlite() bool {
return b.Dialect() == dialect.SQLite
}

// fromIdent sets the builder dialect from the identifier format.
Expand Down
Loading
Loading