Repository navigation
Expand file tree
/
Copy pathsource.go
More file actions
97 lines (83 loc) · 3.1 KB
/
Copy pathsource.go
File metadata and controls
97 lines (83 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
package expr
import (
"strings"
"github.com/ubgo/sqlb/render"
)
// Source is anything that can stand in FROM or be joined: a table, a derived
// table, or an engine-specific source such as VALUES, a set-returning
// function or a LATERAL body.
//
// Every source has an alias, because columns are referenced through the alias
// and Build checks alias uniqueness across every source in a statement.
type Source interface {
SourceSQL(a *render.Args) string
SourceAlias() string
}
// SourceSQL renders "name AS alias". Table satisfies Source.
func (t Table) SourceSQL(a *render.Args) string { return t.SQL(a.Dialect()) }
// SourceAlias returns the name this source is known by in the statement: the
// alias when there is one, otherwise the table's own name. Table satisfies
// Source.
//
// The fallback is what makes the duplicate-alias check mean something for
// unaliased tables. Returning "" for every one of them made two different
// tables collide under the empty key, and made a genuine self-join with no
// aliases — which really is ambiguous — indistinguishable from it.
func (t Table) SourceAlias() string {
if t.Alias != "" {
return t.Alias
}
return t.Name
}
// Ref returns a Table value that references a source by its alias only, for
// making Col values against a derived table, a CTE or a VALUES list:
//
// t := sqlb.Derived(sub, "t")
// n := sqlb.Col[int64](sqlb.Ref(t), "n") // renders "t"."n"
//
// Name is the alias too, so Ref(t) used as a FROM source renders
// "alias" AS "alias", which is what a CTE reference needs.
func Ref(src Source) Table {
return Table{Name: src.SourceAlias(), Alias: src.SourceAlias()}
}
// Statement is any statement that renders into a parent's Args: a Select used
// as a subquery or CTE body, or a data-modifying statement in a CTE.
type Statement interface {
WriteSQL(b *strings.Builder, args *render.Args)
}
type derived struct {
body Statement
alias string
}
// Derived makes a subquery a FROM source: "(SELECT ...) AS alias".
func Derived(body Statement, alias string) Source { return derived{body, alias} }
func (d derived) SourceSQL(a *render.Args) string {
var b strings.Builder
b.WriteString("(")
d.body.WriteSQL(&b, a)
b.WriteString(") ")
b.WriteString(kwAs)
b.WriteString(sepSpace)
b.WriteString(a.Dialect().QuoteIdent(d.alias))
return b.String()
}
func (d derived) SourceAlias() string { return d.alias }
// SourceOf makes a Source from a render function and an alias. Engine
// packages build VALUES lists, function sources and LATERAL bodies with it.
func SourceOf(alias string, render func(a *render.Args) string) Source {
return sourceFunc{alias: alias, render: render}
}
type sourceFunc struct {
alias string
render func(a *render.Args) string
}
func (s sourceFunc) SourceSQL(a *render.Args) string { return s.render(a) }
func (s sourceFunc) SourceAlias() string { return s.alias }
// QuoteList renders identifiers comma-separated and quoted, for column lists.
func QuoteList(d render.Dialect, names []string) string {
out := make([]string, len(names))
for i, n := range names {
out[i] = d.QuoteIdent(n)
}
return strings.Join(out, sepComma)
}