Optimize the sql generator hot path

This commit is contained in:
Vikram Rangnekar 2019-06-07 20:53:08 -04:00
parent 5f6ea226a3
commit 0b7512cf94
6 changed files with 453 additions and 397 deletions

7
psql/bench.1 Normal file
View File

@ -0,0 +1,7 @@
goos: darwin
goarch: amd64
pkg: github.com/dosco/super-graph/psql
BenchmarkCompile-8 50000 27388 ns/op 4983 B/op 136 allocs/op
BenchmarkCompileParallel-8 200000 8864 ns/op 5046 B/op 136 allocs/op
PASS
ok github.com/dosco/super-graph/psql 3.528s

View File

@ -13,7 +13,8 @@ import (
) )
const ( const (
empty = "" empty = ""
closeBlock = 500
) )
type Config struct { type Config struct {
@ -44,91 +45,93 @@ func (c *Compiler) IDColumn(table string) string {
return t.PrimaryCol return t.PrimaryCol
} }
func (c *Compiler) CompileEx(qc *qcode.QCode) (uint32, []byte, error) { type compilerContext struct {
w *bytes.Buffer
s []qcode.Select
*Compiler
}
func (co *Compiler) CompileEx(qc *qcode.QCode) (uint32, []byte, error) {
w := &bytes.Buffer{} w := &bytes.Buffer{}
skipped, err := c.Compile(qc, w) skipped, err := co.Compile(qc, w)
return skipped, w.Bytes(), err return skipped, w.Bytes(), err
} }
func (c *Compiler) Compile(qc *qcode.QCode, w *bytes.Buffer) (uint32, error) { func (co *Compiler) Compile(qc *qcode.QCode, w *bytes.Buffer) (uint32, error) {
if len(qc.Query.Selects) == 0 { if len(qc.Query.Selects) == 0 {
return 0, errors.New("empty query") return 0, errors.New("empty query")
} }
c := &compilerContext{w, qc.Query.Selects, co}
root := &qc.Query.Selects[0] root := &qc.Query.Selects[0]
st := util.NewStack() st := NewStack()
ti, err := c.getTable(root) st.Push(root.ID + closeBlock)
if err != nil { st.Push(root.ID)
return 0, err
}
st.Push(&selectBlockClose{nil, root})
st.Push(&selectBlock{nil, root, qc, ti, c})
//fmt.Fprintf(w, `SELECT json_object_agg('%s', %s) FROM (`, //fmt.Fprintf(w, `SELECT json_object_agg('%s', %s) FROM (`,
//root.FieldName, root.Table) //root.FieldName, root.Table)
w.WriteString(`SELECT json_object_agg('`) c.w.WriteString(`SELECT json_object_agg('`)
w.WriteString(root.FieldName) c.w.WriteString(root.FieldName)
w.WriteString(`', `) c.w.WriteString(`', `)
w.WriteString(root.Table) c.w.WriteString(root.Table)
w.WriteString(`) FROM (`) c.w.WriteString(`) FROM (`)
var ignored uint32 var ignored uint32
var err error
for { for {
if st.Len() == 0 { if st.Len() == 0 {
break break
} }
intf := st.Pop() id := st.Pop()
switch v := intf.(type) { if id < closeBlock {
case *selectBlock: sel := &c.s[id]
skipped, err := v.render(w)
if sel.ID != 0 {
if err = c.renderJoin(sel); err != nil {
return 0, err
}
}
skipped, err := c.renderSelect(sel)
if err != nil { if err != nil {
return 0, err return 0, err
} }
ignored |= skipped ignored |= skipped
for _, id := range v.sel.Children { for _, cid := range sel.Children {
if hasBit(skipped, uint16(id)) { if hasBit(skipped, uint32(cid)) {
continue continue
} }
child := &qc.Query.Selects[id] child := &c.s[cid]
ti, err := c.getTable(child) st.Push(child.ID + closeBlock)
if err != nil { st.Push(child.ID)
}
} else {
sel := &c.s[(id - closeBlock)]
err = c.renderSelectClose(sel)
if sel.ID != 0 {
if err = c.renderJoinClose(sel); err != nil {
return 0, err return 0, err
} }
st.Push(&joinClose{child})
st.Push(&selectBlockClose{v.sel, child})
st.Push(&selectBlock{v.sel, child, qc, ti, c})
st.Push(&joinOpen{child})
} }
case *selectBlockClose:
err = v.render(w)
case *joinOpen:
err = v.render(w)
case *joinClose:
err = v.render(w)
}
if err != nil {
return 0, err
} }
} }
w.WriteString(`)`) c.w.WriteString(`)`)
alias(w, `done_1337`) alias(c.w, `done_1337`)
w.WriteString(`;`) c.w.WriteString(`;`)
return ignored, nil return ignored, nil
} }
func (c *Compiler) getTable(sel *qcode.Select) (*DBTableInfo, error) { func (c *compilerContext) getTable(sel *qcode.Select) (
*DBTableInfo, error) {
if tn, ok := c.tmap[sel.Table]; ok { if tn, ok := c.tmap[sel.Table]; ok {
return c.schema.GetTable(tn) return c.schema.GetTable(tn)
} }
@ -136,20 +139,20 @@ func (c *Compiler) getTable(sel *qcode.Select) (*DBTableInfo, error) {
return c.schema.GetTable(sel.Table) return c.schema.GetTable(sel.Table)
} }
func (v *selectBlock) processChildren() (uint32, []*qcode.Column) { func (c *compilerContext) processChildren(sel *qcode.Select) (uint32, []*qcode.Column) {
var skipped uint32 var skipped uint32
cols := make([]*qcode.Column, 0, len(v.sel.Cols)) cols := make([]*qcode.Column, 0, len(sel.Cols))
colmap := make(map[string]struct{}, len(v.sel.Cols)) colmap := make(map[string]struct{}, len(sel.Cols))
for i := range v.sel.Cols { for i := range sel.Cols {
colmap[v.sel.Cols[i].Name] = struct{}{} colmap[sel.Cols[i].Name] = struct{}{}
} }
for _, id := range v.sel.Children { for _, id := range sel.Children {
child := &v.qc.Query.Selects[id] child := &c.s[id]
rel, ok := v.schema.RelMap[child.RelID] rel, ok := c.schema.RelMap[child.RelID]
if !ok { if !ok {
skipped |= (1 << uint(id)) skipped |= (1 << uint(id))
continue continue
@ -160,15 +163,15 @@ func (v *selectBlock) processChildren() (uint32, []*qcode.Column) {
fallthrough fallthrough
case RelBelongTo: case RelBelongTo:
if _, ok := colmap[rel.Col2]; !ok { if _, ok := colmap[rel.Col2]; !ok {
cols = append(cols, &qcode.Column{v.sel.Table, rel.Col2, rel.Col2}) cols = append(cols, &qcode.Column{sel.Table, rel.Col2, rel.Col2})
} }
case RelOneToManyThrough: case RelOneToManyThrough:
if _, ok := colmap[rel.Col1]; !ok { if _, ok := colmap[rel.Col1]; !ok {
cols = append(cols, &qcode.Column{v.sel.Table, rel.Col1, rel.Col1}) cols = append(cols, &qcode.Column{sel.Table, rel.Col1, rel.Col1})
} }
case RelRemote: case RelRemote:
if _, ok := colmap[rel.Col1]; !ok { if _, ok := colmap[rel.Col1]; !ok {
cols = append(cols, &qcode.Column{v.sel.Table, rel.Col1, rel.Col2}) cols = append(cols, &qcode.Column{sel.Table, rel.Col1, rel.Col2})
} }
skipped |= (1 << uint(id)) skipped |= (1 << uint(id))
@ -180,78 +183,70 @@ func (v *selectBlock) processChildren() (uint32, []*qcode.Column) {
return skipped, cols return skipped, cols
} }
type selectBlock struct { func (c *compilerContext) renderSelect(sel *qcode.Select) (uint32, error) {
parent *qcode.Select skipped, childCols := c.processChildren(sel)
sel *qcode.Select hasOrder := len(sel.OrderBy) != 0
qc *qcode.QCode
ti *DBTableInfo
*Compiler
}
func (v *selectBlock) render(w *bytes.Buffer) (uint32, error) {
skipped, childCols := v.processChildren()
hasOrder := len(v.sel.OrderBy) != 0
// SELECT // SELECT
if v.sel.AsList { if sel.AsList {
//fmt.Fprintf(w, `SELECT coalesce(json_agg("%s"`, v.sel.Table) //fmt.Fprintf(w, `SELECT coalesce(json_agg("%s"`, c.sel.Table)
w.WriteString(`SELECT coalesce(json_agg("`) c.w.WriteString(`SELECT coalesce(json_agg("`)
w.WriteString(v.sel.Table) c.w.WriteString(sel.Table)
w.WriteString(`"`) c.w.WriteString(`"`)
if hasOrder { if hasOrder {
err := renderOrderBy(w, v.sel) err := c.renderOrderBy(sel)
if err != nil { if err != nil {
return skipped, err return skipped, err
} }
} }
//fmt.Fprintf(w, `), '[]') AS "%s" FROM (`, v.sel.Table) //fmt.Fprintf(w, `), '[]') AS "%s" FROM (`, c.sel.Table)
w.WriteString(`), '[]')`) c.w.WriteString(`), '[]')`)
alias(w, v.sel.Table) alias(c.w, sel.Table)
w.WriteString(` FROM (`) c.w.WriteString(` FROM (`)
} }
// ROW-TO-JSON // ROW-TO-JSON
w.WriteString(`SELECT `) c.w.WriteString(`SELECT `)
if len(v.sel.DistinctOn) != 0 { if len(sel.DistinctOn) != 0 {
v.renderDistinctOn(w) c.renderDistinctOn(sel)
} }
w.WriteString(`row_to_json((`) c.w.WriteString(`row_to_json((`)
//fmt.Fprintf(w, `SELECT "sel_%d" FROM (SELECT `, v.sel.ID) //fmt.Fprintf(w, `SELECT "sel_%d" FROM (SELECT `, c.sel.ID)
w.WriteString(`SELECT "sel_`) c.w.WriteString(`SELECT "sel_`)
int2string(w, v.sel.ID) int2string(c.w, sel.ID)
w.WriteString(`" FROM (SELECT `) c.w.WriteString(`" FROM (SELECT `)
// Combined column names // Combined column names
v.renderColumns(w) c.renderColumns(sel)
v.renderRemoteRelColumns(w) c.renderRemoteRelColumns(sel)
err := v.renderJoinedColumns(w, skipped) err := c.renderJoinedColumns(sel, skipped)
if err != nil { if err != nil {
return skipped, err return skipped, err
} }
//fmt.Fprintf(w, `) AS "sel_%d"`, v.sel.ID) //fmt.Fprintf(w, `) AS "sel_%d"`, c.sel.ID)
w.WriteString(`)`) c.w.WriteString(`)`)
aliasWithID(w, "sel", v.sel.ID) aliasWithID(c.w, "sel", sel.ID)
//fmt.Fprintf(w, `)) AS "%s"`, v.sel.Table) //fmt.Fprintf(w, `)) AS "%s"`, c.sel.Table)
w.WriteString(`))`) c.w.WriteString(`))`)
alias(w, v.sel.Table) alias(c.w, sel.Table)
// END-ROW-TO-JSON // END-ROW-TO-JSON
if hasOrder { if hasOrder {
v.renderOrderByColumns(w) c.renderOrderByColumns(sel)
} }
// END-SELECT // END-SELECT
// FROM (SELECT .... ) // FROM (SELECT .... )
err = v.renderBaseSelect(w, childCols, skipped) err = c.renderBaseSelect(sel, childCols, skipped)
if err != nil { if err != nil {
return skipped, err return skipped, err
} }
@ -260,69 +255,56 @@ func (v *selectBlock) render(w *bytes.Buffer) (uint32, error) {
return skipped, nil return skipped, nil
} }
type selectBlockClose struct { func (c *compilerContext) renderSelectClose(sel *qcode.Select) error {
parent *qcode.Select hasOrder := len(sel.OrderBy) != 0
sel *qcode.Select
}
func (v *selectBlockClose) render(w *bytes.Buffer) error {
hasOrder := len(v.sel.OrderBy) != 0
if hasOrder { if hasOrder {
err := renderOrderBy(w, v.sel) err := c.renderOrderBy(sel)
if err != nil { if err != nil {
return err return err
} }
} }
if len(v.sel.Paging.Limit) != 0 { if len(sel.Paging.Limit) != 0 {
//fmt.Fprintf(w, ` LIMIT ('%s') :: integer`, v.sel.Paging.Limit) //fmt.Fprintf(w, ` LIMIT ('%s') :: integer`, c.sel.Paging.Limit)
w.WriteString(` LIMIT ('`) c.w.WriteString(` LIMIT ('`)
w.WriteString(v.sel.Paging.Limit) c.w.WriteString(sel.Paging.Limit)
w.WriteString(`') :: integer`) c.w.WriteString(`') :: integer`)
} else { } else {
w.WriteString(` LIMIT ('20') :: integer`) c.w.WriteString(` LIMIT ('20') :: integer`)
} }
if len(v.sel.Paging.Offset) != 0 { if len(sel.Paging.Offset) != 0 {
//fmt.Fprintf(w, ` OFFSET ('%s') :: integer`, v.sel.Paging.Offset) //fmt.Fprintf(w, ` OFFSET ('%s') :: integer`, c.sel.Paging.Offset)
w.WriteString(`OFFSET ('`) c.w.WriteString(`OFFSET ('`)
w.WriteString(v.sel.Paging.Offset) c.w.WriteString(sel.Paging.Offset)
w.WriteString(`') :: integer`) c.w.WriteString(`') :: integer`)
} }
if v.sel.AsList { if sel.AsList {
//fmt.Fprintf(w, `) AS "%s_%d"`, v.sel.Table, v.sel.ID) //fmt.Fprintf(w, `) AS "%s_%d"`, c.sel.Table, c.sel.ID)
w.WriteString(`)`) c.w.WriteString(`)`)
aliasWithID(w, v.sel.Table, v.sel.ID) aliasWithID(c.w, sel.Table, sel.ID)
} }
return nil return nil
} }
type joinOpen struct { func (c *compilerContext) renderJoin(sel *qcode.Select) error {
sel *qcode.Select c.w.WriteString(` LEFT OUTER JOIN LATERAL (`)
}
func (v joinOpen) render(w *bytes.Buffer) error {
w.WriteString(` LEFT OUTER JOIN LATERAL (`)
return nil return nil
} }
type joinClose struct { func (c *compilerContext) renderJoinClose(sel *qcode.Select) error {
sel *qcode.Select //fmt.Fprintf(w, `) AS "%s_%d_join" ON ('true')`, c.sel.Table, c.sel.ID)
} c.w.WriteString(`)`)
aliasWithIDSuffix(c.w, sel.Table, sel.ID, "_join")
func (v *joinClose) render(w *bytes.Buffer) error { c.w.WriteString(` ON ('true')`)
//fmt.Fprintf(w, `) AS "%s_%d_join" ON ('true')`, v.sel.Table, v.sel.ID)
w.WriteString(`)`)
aliasWithIDSuffix(w, v.sel.Table, v.sel.ID, "_join")
w.WriteString(` ON ('true')`)
return nil return nil
} }
func (v *selectBlock) renderJoinTable(w *bytes.Buffer) { func (c *compilerContext) renderJoinTable(sel *qcode.Select) {
rel, ok := v.schema.RelMap[v.sel.RelID] rel, ok := c.schema.RelMap[sel.RelID]
if !ok { if !ok {
panic(errors.New("no relationship found")) panic(errors.New("no relationship found"))
} }
@ -331,287 +313,302 @@ func (v *selectBlock) renderJoinTable(w *bytes.Buffer) {
return return
} }
parent := &c.s[sel.ParentID]
//fmt.Fprintf(w, ` LEFT OUTER JOIN "%s" ON (("%s"."%s") = ("%s_%d"."%s"))`, //fmt.Fprintf(w, ` LEFT OUTER JOIN "%s" ON (("%s"."%s") = ("%s_%d"."%s"))`,
//rel.Through, rel.Through, rel.ColT, v.parent.Table, v.parent.ID, rel.Col1) //rel.Through, rel.Through, rel.ColT, c.parent.Table, c.parent.ID, rel.Col1)
w.WriteString(` LEFT OUTER JOIN "`) c.w.WriteString(` LEFT OUTER JOIN "`)
w.WriteString(rel.Through) c.w.WriteString(rel.Through)
w.WriteString(`" ON ((`) c.w.WriteString(`" ON ((`)
colWithTable(w, rel.Through, rel.ColT) colWithTable(c.w, rel.Through, rel.ColT)
w.WriteString(`) = (`) c.w.WriteString(`) = (`)
colWithTableID(w, v.parent.Table, v.parent.ID, rel.Col1) colWithTableID(c.w, parent.Table, parent.ID, rel.Col1)
w.WriteString(`))`) c.w.WriteString(`))`)
} }
func (v *selectBlock) renderColumns(w *bytes.Buffer) { func (c *compilerContext) renderColumns(sel *qcode.Select) {
for i, col := range v.sel.Cols { for i, col := range sel.Cols {
if i != 0 { if i != 0 {
io.WriteString(w, ", ") io.WriteString(c.w, ", ")
} }
//fmt.Fprintf(w, `"%s_%d"."%s" AS "%s"`, //fmt.Fprintf(w, `"%s_%d"."%s" AS "%s"`,
//v.sel.Table, v.sel.ID, col.Name, col.FieldName) //c.sel.Table, c.sel.ID, col.Name, col.FieldName)
colWithTableIDAlias(w, v.sel.Table, v.sel.ID, col.Name, col.FieldName) colWithTableIDAlias(c.w, sel.Table, sel.ID, col.Name, col.FieldName)
} }
} }
func (v *selectBlock) renderRemoteRelColumns(w *bytes.Buffer) { func (c *compilerContext) renderRemoteRelColumns(sel *qcode.Select) {
i := 0 i := 0
for _, id := range v.sel.Children { for _, id := range sel.Children {
child := &v.qc.Query.Selects[id] child := &c.s[id]
rel, ok := v.schema.RelMap[child.RelID] rel, ok := c.schema.RelMap[child.RelID]
if !ok || rel.Type != RelRemote { if !ok || rel.Type != RelRemote {
continue continue
} }
if i != 0 || len(v.sel.Cols) != 0 { if i != 0 || len(sel.Cols) != 0 {
io.WriteString(w, ", ") io.WriteString(c.w, ", ")
} }
//fmt.Fprintf(w, `"%s_%d"."%s" AS "%s"`, //fmt.Fprintf(w, `"%s_%d"."%s" AS "%s"`,
//v.sel.Table, v.sel.ID, rel.Col1, rel.Col2) //c.sel.Table, c.sel.ID, rel.Col1, rel.Col2)
colWithTableID(w, v.sel.Table, v.sel.ID, rel.Col1) colWithTableID(c.w, sel.Table, sel.ID, rel.Col1)
alias(w, rel.Col2) alias(c.w, rel.Col2)
i++ i++
} }
} }
func (v *selectBlock) renderJoinedColumns(w *bytes.Buffer, skipped uint32) error { func (c *compilerContext) renderJoinedColumns(sel *qcode.Select, skipped uint32) error {
colsRendered := len(v.sel.Cols) != 0 colsRendered := len(sel.Cols) != 0
for _, id := range v.sel.Children { for _, id := range sel.Children {
skipThis := hasBit(skipped, uint16(id)) skipThis := hasBit(skipped, uint32(id))
if colsRendered && !skipThis { if colsRendered && !skipThis {
io.WriteString(w, ", ") io.WriteString(c.w, ", ")
} }
if skipThis { if skipThis {
continue continue
} }
s := &v.qc.Query.Selects[id] sel := &c.s[id]
//fmt.Fprintf(w, `"%s_%d_join"."%s" AS "%s"`, //fmt.Fprintf(w, `"%s_%d_join"."%s" AS "%s"`,
//s.Table, s.ID, s.Table, s.FieldName) //s.Table, s.ID, s.Table, s.FieldName)
colWithTableIDSuffixAlias(w, s.Table, s.ID, "_join", s.Table, s.FieldName) colWithTableIDSuffixAlias(c.w, sel.Table, sel.ID, "_join", sel.Table, sel.FieldName)
} }
return nil return nil
} }
func (v *selectBlock) renderBaseSelect(w *bytes.Buffer, childCols []*qcode.Column, skipped uint32) error { func (c *compilerContext) renderBaseSelect(sel *qcode.Select,
childCols []*qcode.Column, skipped uint32) error {
var groupBy []int var groupBy []int
isRoot := v.parent == nil ti, err := c.getTable(sel)
isFil := v.sel.Where != nil if err != nil {
isSearch := v.sel.Args["search"] != nil return err
}
isRoot := sel.ID == 0
isFil := sel.Where != nil
isSearch := sel.Args["search"] != nil
isAgg := false isAgg := false
w.WriteString(` FROM (SELECT `) c.w.WriteString(` FROM (SELECT `)
for i, col := range v.sel.Cols { for i, col := range sel.Cols {
cn := col.Name cn := col.Name
_, isRealCol := v.ti.Columns[cn] _, isRealCol := ti.Columns[cn]
if !isRealCol { if !isRealCol {
if isSearch { if isSearch {
switch { switch {
case cn == "search_rank": case cn == "search_rank":
cn = v.ti.TSVCol cn = ti.TSVCol
arg := v.sel.Args["search"] arg := sel.Args["search"]
//fmt.Fprintf(w, `ts_rank("%s"."%s", to_tsquery('%s')) AS %s`, //fmt.Fprintf(w, `ts_rank("%s"."%s", to_tsquery('%s')) AS %s`,
//v.sel.Table, cn, arg.Val, col.Name) //c.sel.Table, cn, arg.Val, col.Name)
w.WriteString(`ts_rank(`) c.w.WriteString(`ts_rank(`)
colWithTable(w, v.sel.Table, cn) colWithTable(c.w, sel.Table, cn)
w.WriteString(`, to_tsquery('`) c.w.WriteString(`, to_tsquery('`)
w.WriteString(arg.Val) c.w.WriteString(arg.Val)
w.WriteString(`')`) c.w.WriteString(`')`)
alias(w, col.Name) alias(c.w, col.Name)
case strings.HasPrefix(cn, "search_headline_"): case strings.HasPrefix(cn, "search_headline_"):
cn = cn[16:] cn = cn[16:]
arg := v.sel.Args["search"] arg := sel.Args["search"]
//fmt.Fprintf(w, `ts_headline("%s"."%s", to_tsquery('%s')) AS %s`, //fmt.Fprintf(w, `ts_headline("%s"."%s", to_tsquery('%s')) AS %s`,
//v.sel.Table, cn, arg.Val, col.Name) //c.sel.Table, cn, arg.Val, col.Name)
w.WriteString(`ts_headlinek(`) c.w.WriteString(`ts_headlinek(`)
colWithTable(w, v.sel.Table, cn) colWithTable(c.w, sel.Table, cn)
w.WriteString(`, to_tsquery('`) c.w.WriteString(`, to_tsquery('`)
w.WriteString(arg.Val) c.w.WriteString(arg.Val)
w.WriteString(`')`) c.w.WriteString(`')`)
alias(w, col.Name) alias(c.w, col.Name)
} }
} else { } else {
pl := funcPrefixLen(cn) pl := funcPrefixLen(cn)
if pl == 0 { if pl == 0 {
//fmt.Fprintf(w, `'%s not defined' AS %s`, cn, col.Name) //fmt.Fprintf(w, `'%s not defined' AS %s`, cn, col.Name)
w.WriteString(`'`) c.w.WriteString(`'`)
w.WriteString(cn) c.w.WriteString(cn)
w.WriteString(` not defined'`) c.w.WriteString(` not defined'`)
alias(w, col.Name) alias(c.w, col.Name)
} else { } else {
isAgg = true isAgg = true
fn := cn[0 : pl-1] fn := cn[0 : pl-1]
cn := cn[pl:] cn := cn[pl:]
//fmt.Fprintf(w, `%s("%s"."%s") AS %s`, fn, v.sel.Table, cn, col.Name) //fmt.Fprintf(w, `%s("%s"."%s") AS %s`, fn, c.sel.Table, cn, col.Name)
w.WriteString(fn) c.w.WriteString(fn)
w.WriteString(`(`) c.w.WriteString(`(`)
colWithTable(w, v.sel.Table, cn) colWithTable(c.w, sel.Table, cn)
w.WriteString(`)`) c.w.WriteString(`)`)
alias(w, col.Name) alias(c.w, col.Name)
} }
} }
} else { } else {
groupBy = append(groupBy, i) groupBy = append(groupBy, i)
//fmt.Fprintf(w, `"%s"."%s"`, v.sel.Table, cn) //fmt.Fprintf(w, `"%s"."%s"`, c.sel.Table, cn)
colWithTable(w, v.sel.Table, cn) colWithTable(c.w, sel.Table, cn)
} }
if i < len(v.sel.Cols)-1 || len(childCols) != 0 { if i < len(sel.Cols)-1 || len(childCols) != 0 {
//io.WriteString(w, ", ") //io.WriteString(w, ", ")
w.WriteString(`, `) c.w.WriteString(`, `)
} }
} }
for i, col := range childCols { for i, col := range childCols {
if i != 0 { if i != 0 {
//io.WriteString(w, ", ") //io.WriteString(w, ", ")
w.WriteString(`, `) c.w.WriteString(`, `)
} }
//fmt.Fprintf(w, `"%s"."%s"`, col.Table, col.Name) //fmt.Fprintf(w, `"%s"."%s"`, col.Table, col.Name)
colWithTable(w, col.Table, col.Name) colWithTable(c.w, col.Table, col.Name)
} }
w.WriteString(` FROM `) c.w.WriteString(` FROM `)
if tn, ok := v.tmap[v.sel.Table]; ok { if tn, ok := c.tmap[sel.Table]; ok {
//fmt.Fprintf(w, ` FROM "%s" AS "%s"`, tn, v.sel.Table) //fmt.Fprintf(w, ` FROM "%s" AS "%s"`, tn, c.sel.Table)
colWithAlias(w, tn, v.sel.Table) colWithAlias(c.w, tn, sel.Table)
} else { } else {
//fmt.Fprintf(w, ` FROM "%s"`, v.sel.Table) //fmt.Fprintf(w, ` FROM "%s"`, c.sel.Table)
w.WriteString(`"`) c.w.WriteString(`"`)
w.WriteString(v.sel.Table) c.w.WriteString(sel.Table)
w.WriteString(`"`) c.w.WriteString(`"`)
} }
if isRoot && isFil { if isRoot && isFil {
w.WriteString(` WHERE (`) c.w.WriteString(` WHERE (`)
if err := v.renderWhere(w); err != nil { if err := c.renderWhere(sel); err != nil {
return err return err
} }
w.WriteString(`)`) c.w.WriteString(`)`)
} }
if !isRoot { if !isRoot {
v.renderJoinTable(w) c.renderJoinTable(sel)
w.WriteString(` WHERE (`) c.w.WriteString(` WHERE (`)
v.renderRelationship(w) c.renderRelationship(sel)
if isFil { if isFil {
w.WriteString(` AND `) c.w.WriteString(` AND `)
if err := v.renderWhere(w); err != nil { if err := c.renderWhere(sel); err != nil {
return err return err
} }
} }
w.WriteString(`)`) c.w.WriteString(`)`)
} }
if isAgg { if isAgg {
if len(groupBy) != 0 { if len(groupBy) != 0 {
w.WriteString(` GROUP BY `) c.w.WriteString(` GROUP BY `)
for i, id := range groupBy { for i, id := range groupBy {
if i != 0 { if i != 0 {
w.WriteString(`, `) c.w.WriteString(`, `)
} }
//fmt.Fprintf(w, `"%s"."%s"`, v.sel.Table, v.sel.Cols[id].Name) //fmt.Fprintf(w, `"%s"."%s"`, c.sel.Table, c.sel.Cols[id].Name)
colWithTable(w, v.sel.Table, v.sel.Cols[id].Name) colWithTable(c.w, sel.Table, sel.Cols[id].Name)
} }
} }
} }
if len(v.sel.Paging.Limit) != 0 { if len(sel.Paging.Limit) != 0 {
//fmt.Fprintf(w, ` LIMIT ('%s') :: integer`, v.sel.Paging.Limit) //fmt.Fprintf(w, ` LIMIT ('%s') :: integer`, c.sel.Paging.Limit)
w.WriteString(` LIMIT ('`) c.w.WriteString(` LIMIT ('`)
w.WriteString(v.sel.Paging.Limit) c.w.WriteString(sel.Paging.Limit)
w.WriteString(`') :: integer`) c.w.WriteString(`') :: integer`)
} else { } else {
w.WriteString(` LIMIT ('20') :: integer`) c.w.WriteString(` LIMIT ('20') :: integer`)
} }
if len(v.sel.Paging.Offset) != 0 { if len(sel.Paging.Offset) != 0 {
//fmt.Fprintf(w, ` OFFSET ('%s') :: integer`, v.sel.Paging.Offset) //fmt.Fprintf(w, ` OFFSET ('%s') :: integer`, c.sel.Paging.Offset)
w.WriteString(` OFFSET ('`) c.w.WriteString(` OFFSET ('`)
w.WriteString(v.sel.Paging.Offset) c.w.WriteString(sel.Paging.Offset)
w.WriteString(`') :: integer`) c.w.WriteString(`') :: integer`)
} }
//fmt.Fprintf(w, `) AS "%s_%d"`, v.sel.Table, v.sel.ID) //fmt.Fprintf(w, `) AS "%s_%d"`, c.sel.Table, c.sel.ID)
w.WriteString(`)`) c.w.WriteString(`)`)
aliasWithID(w, v.sel.Table, v.sel.ID) aliasWithID(c.w, sel.Table, sel.ID)
return nil return nil
} }
func (v *selectBlock) renderOrderByColumns(w *bytes.Buffer) { func (c *compilerContext) renderOrderByColumns(sel *qcode.Select) {
colsRendered := len(v.sel.Cols) != 0 colsRendered := len(sel.Cols) != 0
for i := range v.sel.OrderBy { for i := range sel.OrderBy {
if colsRendered { if colsRendered {
//io.WriteString(w, ", ") //io.WriteString(w, ", ")
w.WriteString(`, `) c.w.WriteString(`, `)
} }
c := v.sel.OrderBy[i].Col col := sel.OrderBy[i].Col
//fmt.Fprintf(w, `"%s_%d"."%s" AS "%s_%d_%s_ob"`, //fmt.Fprintf(w, `"%s_%d"."%s" AS "%s_%d_%s_ob"`,
//v.sel.Table, v.sel.ID, c, //c.sel.Table, c.sel.ID, c,
//v.sel.Table, v.sel.ID, c) //c.sel.Table, c.sel.ID, c)
colWithTableID(w, v.sel.Table, v.sel.ID, c) colWithTableID(c.w, sel.Table, sel.ID, col)
w.WriteString(` AS `) c.w.WriteString(` AS `)
tableIDColSuffix(w, v.sel.Table, v.sel.ID, c, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, col, "_ob")
} }
} }
func (v *selectBlock) renderRelationship(w *bytes.Buffer) { func (c *compilerContext) renderRelationship(sel *qcode.Select) {
rel, ok := v.schema.RelMap[v.sel.RelID] rel, ok := c.schema.RelMap[sel.RelID]
if !ok { if !ok {
panic(errors.New("no relationship found")) panic(errors.New("no relationship found"))
} }
parent := c.s[sel.ParentID]
switch rel.Type { switch rel.Type {
case RelBelongTo: case RelBelongTo:
//fmt.Fprintf(w, `(("%s"."%s") = ("%s_%d"."%s"))`, //fmt.Fprintf(w, `(("%s"."%s") = ("%s_%d"."%s"))`,
//v.sel.Table, rel.Col1, v.parent.Table, v.parent.ID, rel.Col2) //c.sel.Table, rel.Col1, c.parent.Table, c.parent.ID, rel.Col2)
w.WriteString(`((`) c.w.WriteString(`((`)
colWithTable(w, v.sel.Table, rel.Col1) colWithTable(c.w, sel.Table, rel.Col1)
w.WriteString(`) = (`) c.w.WriteString(`) = (`)
colWithTableID(w, v.parent.Table, v.parent.ID, rel.Col2) colWithTableID(c.w, parent.Table, parent.ID, rel.Col2)
w.WriteString(`))`) c.w.WriteString(`))`)
case RelOneToMany: case RelOneToMany:
//fmt.Fprintf(w, `(("%s"."%s") = ("%s_%d"."%s"))`, //fmt.Fprintf(w, `(("%s"."%s") = ("%s_%d"."%s"))`,
//v.sel.Table, rel.Col1, v.parent.Table, v.parent.ID, rel.Col2) //c.sel.Table, rel.Col1, c.parent.Table, c.parent.ID, rel.Col2)
w.WriteString(`((`) c.w.WriteString(`((`)
colWithTable(w, v.sel.Table, rel.Col1) colWithTable(c.w, sel.Table, rel.Col1)
w.WriteString(`) = (`) c.w.WriteString(`) = (`)
colWithTableID(w, v.parent.Table, v.parent.ID, rel.Col2) colWithTableID(c.w, parent.Table, parent.ID, rel.Col2)
w.WriteString(`))`) c.w.WriteString(`))`)
case RelOneToManyThrough: case RelOneToManyThrough:
//fmt.Fprintf(w, `(("%s"."%s") = ("%s"."%s"))`, //fmt.Fprintf(w, `(("%s"."%s") = ("%s"."%s"))`,
//v.sel.Table, rel.Col1, rel.Through, rel.Col2) //c.sel.Table, rel.Col1, rel.Through, rel.Col2)
w.WriteString(`((`) c.w.WriteString(`((`)
colWithTable(w, v.sel.Table, rel.Col1) colWithTable(c.w, sel.Table, rel.Col1)
w.WriteString(`) = (`) c.w.WriteString(`) = (`)
colWithTable(w, rel.Through, rel.Col2) colWithTable(c.w, rel.Through, rel.Col2)
w.WriteString(`))`) c.w.WriteString(`))`)
} }
} }
func (v *selectBlock) renderWhere(w *bytes.Buffer) error { func (c *compilerContext) renderWhere(sel *qcode.Select) error {
st := util.NewStack() st := util.NewStack()
if v.sel.Where != nil { if sel.Where != nil {
st.Push(v.sel.Where) st.Push(sel.Where)
}
ti, err := c.getTable(sel)
if err != nil {
return err
} }
for { for {
@ -625,11 +622,11 @@ func (v *selectBlock) renderWhere(w *bytes.Buffer) error {
case qcode.ExpOp: case qcode.ExpOp:
switch val { switch val {
case qcode.OpAnd: case qcode.OpAnd:
w.WriteString(` AND `) c.w.WriteString(` AND `)
case qcode.OpOr: case qcode.OpOr:
w.WriteString(` OR `) c.w.WriteString(` OR `)
case qcode.OpNot: case qcode.OpNot:
w.WriteString(`NOT `) c.w.WriteString(`NOT `)
default: default:
return fmt.Errorf("11: unexpected value %v (%t)", intf, intf) return fmt.Errorf("11: unexpected value %v (%t)", intf, intf)
} }
@ -651,82 +648,82 @@ func (v *selectBlock) renderWhere(w *bytes.Buffer) error {
if val.NestedCol { if val.NestedCol {
//fmt.Fprintf(w, `(("%s") `, val.Col) //fmt.Fprintf(w, `(("%s") `, val.Col)
w.WriteString(`(("`) c.w.WriteString(`(("`)
w.WriteString(val.Col) c.w.WriteString(val.Col)
w.WriteString(`") `) c.w.WriteString(`") `)
} else if len(val.Col) != 0 { } else if len(val.Col) != 0 {
//fmt.Fprintf(w, `(("%s"."%s") `, v.sel.Table, val.Col) //fmt.Fprintf(w, `(("%s"."%s") `, c.sel.Table, val.Col)
w.WriteString(`((`) c.w.WriteString(`((`)
colWithTable(w, v.sel.Table, val.Col) colWithTable(c.w, sel.Table, val.Col)
w.WriteString(`) `) c.w.WriteString(`) `)
} }
valExists := true valExists := true
switch val.Op { switch val.Op {
case qcode.OpEquals: case qcode.OpEquals:
w.WriteString(`=`) c.w.WriteString(`=`)
case qcode.OpNotEquals: case qcode.OpNotEquals:
w.WriteString(`!=`) c.w.WriteString(`!=`)
case qcode.OpGreaterOrEquals: case qcode.OpGreaterOrEquals:
w.WriteString(`>=`) c.w.WriteString(`>=`)
case qcode.OpLesserOrEquals: case qcode.OpLesserOrEquals:
w.WriteString(`<=`) c.w.WriteString(`<=`)
case qcode.OpGreaterThan: case qcode.OpGreaterThan:
w.WriteString(`>`) c.w.WriteString(`>`)
case qcode.OpLesserThan: case qcode.OpLesserThan:
w.WriteString(`<`) c.w.WriteString(`<`)
case qcode.OpIn: case qcode.OpIn:
w.WriteString(`IN`) c.w.WriteString(`IN`)
case qcode.OpNotIn: case qcode.OpNotIn:
w.WriteString(`NOT IN`) c.w.WriteString(`NOT IN`)
case qcode.OpLike: case qcode.OpLike:
w.WriteString(`LIKE`) c.w.WriteString(`LIKE`)
case qcode.OpNotLike: case qcode.OpNotLike:
w.WriteString(`NOT LIKE`) c.w.WriteString(`NOT LIKE`)
case qcode.OpILike: case qcode.OpILike:
w.WriteString(`ILIKE`) c.w.WriteString(`ILIKE`)
case qcode.OpNotILike: case qcode.OpNotILike:
w.WriteString(`NOT ILIKE`) c.w.WriteString(`NOT ILIKE`)
case qcode.OpSimilar: case qcode.OpSimilar:
w.WriteString(`SIMILAR TO`) c.w.WriteString(`SIMILAR TO`)
case qcode.OpNotSimilar: case qcode.OpNotSimilar:
w.WriteString(`NOT SIMILAR TO`) c.w.WriteString(`NOT SIMILAR TO`)
case qcode.OpContains: case qcode.OpContains:
w.WriteString(`@>`) c.w.WriteString(`@>`)
case qcode.OpContainedIn: case qcode.OpContainedIn:
w.WriteString(`<@`) c.w.WriteString(`<@`)
case qcode.OpHasKey: case qcode.OpHasKey:
w.WriteString(`?`) c.w.WriteString(`?`)
case qcode.OpHasKeyAny: case qcode.OpHasKeyAny:
w.WriteString(`?|`) c.w.WriteString(`?|`)
case qcode.OpHasKeyAll: case qcode.OpHasKeyAll:
w.WriteString(`?&`) c.w.WriteString(`?&`)
case qcode.OpIsNull: case qcode.OpIsNull:
if strings.EqualFold(val.Val, "true") { if strings.EqualFold(val.Val, "true") {
w.WriteString(`IS NULL)`) c.w.WriteString(`IS NULL)`)
} else { } else {
w.WriteString(`IS NOT NULL)`) c.w.WriteString(`IS NOT NULL)`)
} }
valExists = false valExists = false
case qcode.OpEqID: case qcode.OpEqID:
if len(v.ti.PrimaryCol) == 0 { if len(ti.PrimaryCol) == 0 {
return fmt.Errorf("no primary key column defined for %s", v.sel.Table) return fmt.Errorf("no primary key column defined for %s", sel.Table)
} }
//fmt.Fprintf(w, `(("%s") =`, v.ti.PrimaryCol) //fmt.Fprintf(w, `(("%s") =`, c.ti.PrimaryCol)
w.WriteString(`(("`) c.w.WriteString(`(("`)
w.WriteString(v.ti.PrimaryCol) c.w.WriteString(ti.PrimaryCol)
w.WriteString(`") =`) c.w.WriteString(`") =`)
case qcode.OpTsQuery: case qcode.OpTsQuery:
if len(v.ti.TSVCol) == 0 { if len(ti.TSVCol) == 0 {
return fmt.Errorf("no tsv column defined for %s", v.sel.Table) return fmt.Errorf("no tsv column defined for %s", sel.Table)
} }
//fmt.Fprintf(w, `(("%s") @@ to_tsquery('%s'))`, v.ti.TSVCol, val.Val) //fmt.Fprintf(w, `(("%s") @@ to_tsquery('%s'))`, c.ti.TSVCol, val.Val)
w.WriteString(`(("`) c.w.WriteString(`(("`)
w.WriteString(v.ti.TSVCol) c.w.WriteString(ti.TSVCol)
w.WriteString(`") @@ to_tsquery('`) c.w.WriteString(`") @@ to_tsquery('`)
w.WriteString(val.Val) c.w.WriteString(val.Val)
w.WriteString(`'))`) c.w.WriteString(`'))`)
valExists = false valExists = false
default: default:
@ -735,11 +732,11 @@ func (v *selectBlock) renderWhere(w *bytes.Buffer) error {
if valExists { if valExists {
if val.Type == qcode.ValList { if val.Type == qcode.ValList {
renderList(w, val) c.renderList(val)
} else { } else {
renderVal(w, val, v.vars) c.renderVal(val, c.vars)
} }
w.WriteString(`)`) c.w.WriteString(`)`)
} }
default: default:
@ -750,39 +747,39 @@ func (v *selectBlock) renderWhere(w *bytes.Buffer) error {
return nil return nil
} }
func renderOrderBy(w *bytes.Buffer, sel *qcode.Select) error { func (c *compilerContext) renderOrderBy(sel *qcode.Select) error {
w.WriteString(` ORDER BY `) c.w.WriteString(` ORDER BY `)
for i := range sel.OrderBy { for i := range sel.OrderBy {
if i != 0 { if i != 0 {
w.WriteString(`, `) c.w.WriteString(`, `)
} }
ob := sel.OrderBy[i] ob := sel.OrderBy[i]
switch ob.Order { switch ob.Order {
case qcode.OrderAsc: case qcode.OrderAsc:
//fmt.Fprintf(w, `"%s_%d.ob.%s" ASC`, sel.Table, sel.ID, ob.Col) //fmt.Fprintf(w, `"%s_%d.ob.%s" ASC`, sel.Table, sel.ID, ob.Col)
tableIDColSuffix(w, sel.Table, sel.ID, ob.Col, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, ob.Col, "_ob")
w.WriteString(` ASC`) c.w.WriteString(` ASC`)
case qcode.OrderDesc: case qcode.OrderDesc:
//fmt.Fprintf(w, `"%s_%d.ob.%s" DESC`, sel.Table, sel.ID, ob.Col) //fmt.Fprintf(w, `"%s_%d.ob.%s" DESC`, sel.Table, sel.ID, ob.Col)
tableIDColSuffix(w, sel.Table, sel.ID, ob.Col, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, ob.Col, "_ob")
w.WriteString(` DESC`) c.w.WriteString(` DESC`)
case qcode.OrderAscNullsFirst: case qcode.OrderAscNullsFirst:
//fmt.Fprintf(w, `"%s_%d.ob.%s" ASC NULLS FIRST`, sel.Table, sel.ID, ob.Col) //fmt.Fprintf(w, `"%s_%d.ob.%s" ASC NULLS FIRST`, sel.Table, sel.ID, ob.Col)
tableIDColSuffix(w, sel.Table, sel.ID, ob.Col, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, ob.Col, "_ob")
w.WriteString(` ASC NULLS FIRST`) c.w.WriteString(` ASC NULLS FIRST`)
case qcode.OrderDescNullsFirst: case qcode.OrderDescNullsFirst:
//fmt.Fprintf(w, `%s_%d.ob.%s DESC NULLS FIRST`, sel.Table, sel.ID, ob.Col) //fmt.Fprintf(w, `%s_%d.ob.%s DESC NULLS FIRST`, sel.Table, sel.ID, ob.Col)
tableIDColSuffix(w, sel.Table, sel.ID, ob.Col, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, ob.Col, "_ob")
w.WriteString(` DESC NULLLS FIRST`) c.w.WriteString(` DESC NULLLS FIRST`)
case qcode.OrderAscNullsLast: case qcode.OrderAscNullsLast:
//fmt.Fprintf(w, `"%s_%d.ob.%s ASC NULLS LAST`, sel.Table, sel.ID, ob.Col) //fmt.Fprintf(w, `"%s_%d.ob.%s ASC NULLS LAST`, sel.Table, sel.ID, ob.Col)
tableIDColSuffix(w, sel.Table, sel.ID, ob.Col, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, ob.Col, "_ob")
w.WriteString(` ASC NULLS LAST`) c.w.WriteString(` ASC NULLS LAST`)
case qcode.OrderDescNullsLast: case qcode.OrderDescNullsLast:
//fmt.Fprintf(w, `%s_%d.ob.%s DESC NULLS LAST`, sel.Table, sel.ID, ob.Col) //fmt.Fprintf(w, `%s_%d.ob.%s DESC NULLS LAST`, sel.Table, sel.ID, ob.Col)
tableIDColSuffix(w, sel.Table, sel.ID, ob.Col, "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, ob.Col, "_ob")
w.WriteString(` DESC NULLS LAST`) c.w.WriteString(` DESC NULLS LAST`)
default: default:
return fmt.Errorf("13: unexpected value %v", ob.Order) return fmt.Errorf("13: unexpected value %v", ob.Order)
} }
@ -790,60 +787,62 @@ func renderOrderBy(w *bytes.Buffer, sel *qcode.Select) error {
return nil return nil
} }
func (v selectBlock) renderDistinctOn(w *bytes.Buffer) { func (c *compilerContext) renderDistinctOn(sel *qcode.Select) {
io.WriteString(w, `DISTINCT ON (`) io.WriteString(c.w, `DISTINCT ON (`)
for i := range v.sel.DistinctOn { for i := range sel.DistinctOn {
if i != 0 { if i != 0 {
w.WriteString(`, `) c.w.WriteString(`, `)
} }
//fmt.Fprintf(w, `"%s_%d.ob.%s"`, v.sel.Table, v.sel.ID, v.sel.DistinctOn[i]) //fmt.Fprintf(w, `"%s_%d.ob.%s"`, c.sel.Table, c.sel.ID, c.sel.DistinctOn[i])
tableIDColSuffix(w, v.sel.Table, v.sel.ID, v.sel.DistinctOn[i], "_ob") tableIDColSuffix(c.w, sel.Table, sel.ID, sel.DistinctOn[i], "_ob")
} }
w.WriteString(`) `) c.w.WriteString(`) `)
} }
func renderList(w *bytes.Buffer, ex *qcode.Exp) { func (c *compilerContext) renderList(ex *qcode.Exp) {
io.WriteString(w, ` (`) io.WriteString(c.w, ` (`)
for i := range ex.ListVal { for i := range ex.ListVal {
if i != 0 { if i != 0 {
w.WriteString(`, `) c.w.WriteString(`, `)
} }
switch ex.ListType { switch ex.ListType {
case qcode.ValBool, qcode.ValInt, qcode.ValFloat: case qcode.ValBool, qcode.ValInt, qcode.ValFloat:
w.WriteString(ex.ListVal[i]) c.w.WriteString(ex.ListVal[i])
case qcode.ValStr: case qcode.ValStr:
w.WriteString(`'`) c.w.WriteString(`'`)
w.WriteString(ex.ListVal[i]) c.w.WriteString(ex.ListVal[i])
w.WriteString(`'`) c.w.WriteString(`'`)
} }
} }
w.WriteString(`)`) c.w.WriteString(`)`)
} }
func renderVal(w *bytes.Buffer, ex *qcode.Exp, vars map[string]string) { func (c *compilerContext) renderVal(ex *qcode.Exp,
io.WriteString(w, ` (`) vars map[string]string) {
io.WriteString(c.w, ` (`)
switch ex.Type { switch ex.Type {
case qcode.ValBool, qcode.ValInt, qcode.ValFloat: case qcode.ValBool, qcode.ValInt, qcode.ValFloat:
if len(ex.Val) != 0 { if len(ex.Val) != 0 {
w.WriteString(ex.Val) c.w.WriteString(ex.Val)
} else { } else {
w.WriteString(`''`) c.w.WriteString(`''`)
} }
case qcode.ValStr: case qcode.ValStr:
w.WriteString(`'`) c.w.WriteString(`'`)
w.WriteString(ex.Val) c.w.WriteString(ex.Val)
w.WriteString(`'`) c.w.WriteString(`'`)
case qcode.ValVar: case qcode.ValVar:
if val, ok := vars[ex.Val]; ok { if val, ok := vars[ex.Val]; ok {
w.WriteString(val) c.w.WriteString(val)
} else { } else {
//fmt.Fprintf(w, `'{{%s}}'`, ex.Val) //fmt.Fprintf(w, `'{{%s}}'`, ex.Val)
w.WriteString(`'{{`) c.w.WriteString(`'{{`)
w.WriteString(ex.Val) c.w.WriteString(ex.Val)
w.WriteString(`}}'`) c.w.WriteString(`}}'`)
} }
} }
w.WriteString(`)`) c.w.WriteString(`)`)
} }
func funcPrefixLen(fn string) int { func funcPrefixLen(fn string) int {
@ -874,7 +873,7 @@ func funcPrefixLen(fn string) int {
return 0 return 0
} }
func hasBit(n uint32, pos uint16) bool { func hasBit(n uint32, pos uint32) bool {
val := n & (1 << pos) val := n & (1 << pos)
return (val > 0) return (val > 0)
} }
@ -885,7 +884,7 @@ func alias(w *bytes.Buffer, alias string) {
w.WriteString(`"`) w.WriteString(`"`)
} }
func aliasWithID(w *bytes.Buffer, alias string, id int16) { func aliasWithID(w *bytes.Buffer, alias string, id int32) {
w.WriteString(` AS "`) w.WriteString(` AS "`)
w.WriteString(alias) w.WriteString(alias)
w.WriteString(`_`) w.WriteString(`_`)
@ -893,7 +892,7 @@ func aliasWithID(w *bytes.Buffer, alias string, id int16) {
w.WriteString(`"`) w.WriteString(`"`)
} }
func aliasWithIDSuffix(w *bytes.Buffer, alias string, id int16, suffix string) { func aliasWithIDSuffix(w *bytes.Buffer, alias string, id int32, suffix string) {
w.WriteString(` AS "`) w.WriteString(` AS "`)
w.WriteString(alias) w.WriteString(alias)
w.WriteString(`_`) w.WriteString(`_`)
@ -918,7 +917,7 @@ func colWithTable(w *bytes.Buffer, table, col string) {
w.WriteString(`"`) w.WriteString(`"`)
} }
func colWithTableID(w *bytes.Buffer, table string, id int16, col string) { func colWithTableID(w *bytes.Buffer, table string, id int32, col string) {
w.WriteString(`"`) w.WriteString(`"`)
w.WriteString(table) w.WriteString(table)
w.WriteString(`_`) w.WriteString(`_`)
@ -928,7 +927,7 @@ func colWithTableID(w *bytes.Buffer, table string, id int16, col string) {
w.WriteString(`"`) w.WriteString(`"`)
} }
func colWithTableIDAlias(w *bytes.Buffer, table string, id int16, col, alias string) { func colWithTableIDAlias(w *bytes.Buffer, table string, id int32, col, alias string) {
w.WriteString(`"`) w.WriteString(`"`)
w.WriteString(table) w.WriteString(table)
w.WriteString(`_`) w.WriteString(`_`)
@ -940,7 +939,7 @@ func colWithTableIDAlias(w *bytes.Buffer, table string, id int16, col, alias str
w.WriteString(`"`) w.WriteString(`"`)
} }
func colWithTableIDSuffixAlias(w *bytes.Buffer, table string, id int16, func colWithTableIDSuffixAlias(w *bytes.Buffer, table string, id int32,
suffix, col, alias string) { suffix, col, alias string) {
w.WriteString(`"`) w.WriteString(`"`)
w.WriteString(table) w.WriteString(table)
@ -954,7 +953,7 @@ func colWithTableIDSuffixAlias(w *bytes.Buffer, table string, id int16,
w.WriteString(`"`) w.WriteString(`"`)
} }
func tableIDColSuffix(w *bytes.Buffer, table string, id int16, col, suffix string) { func tableIDColSuffix(w *bytes.Buffer, table string, id int32, col, suffix string) {
w.WriteString(`"`) w.WriteString(`"`)
w.WriteString(table) w.WriteString(table)
w.WriteString(`_`) w.WriteString(`_`)
@ -967,18 +966,18 @@ func tableIDColSuffix(w *bytes.Buffer, table string, id int16, col, suffix strin
const charset = "0123456789" const charset = "0123456789"
func int2string(w *bytes.Buffer, val int16) { func int2string(w *bytes.Buffer, val int32) {
if val < 10 { if val < 10 {
w.WriteByte(charset[val]) w.WriteByte(charset[val])
return return
} }
temp := int16(0) temp := int32(0)
val2 := val val2 := val
for val2 > 0 { for val2 > 0 {
temp *= 10 temp *= 10
temp += val2 % 10 temp += val2 % 10
val2 = int16(math.Floor(float64(val2 / 10))) val2 = int32(math.Floor(float64(val2 / 10)))
} }
val3 := temp val3 := temp

47
psql/stack.go Normal file
View File

@ -0,0 +1,47 @@
package psql
type Stack struct {
stA [20]int32
st []int32
top int
}
// Create a new Stack
func NewStack() *Stack {
s := &Stack{top: -1}
s.st = s.stA[:0]
return s
}
// Return the number of items in the Stack
func (s *Stack) Len() int {
return (s.top + 1)
}
// View the top item on the Stack
func (s *Stack) Peek() int32 {
if s.top == -1 {
return -1
}
return s.st[s.top]
}
// Pop the top item of the Stack and return it
func (s *Stack) Pop() int32 {
if s.top == -1 {
return -1
}
s.top--
return s.st[(s.top + 1)]
}
// Push a value onto the top of the Stack
func (s *Stack) Push(value int32) {
s.top++
if len(s.st) <= s.top {
s.st = append(s.st, value)
} else {
s.st[s.top] = value
}
}

View File

@ -12,7 +12,7 @@ var (
errEOT = errors.New("end of tokens") errEOT = errors.New("end of tokens")
) )
type parserType int16 type parserType int32
const ( const (
maxFields = 100 maxFields = 100
@ -48,14 +48,14 @@ func (o *Operation) Reset() {
} }
type Field struct { type Field struct {
ID int16 ID int32
ParentID int32
Name string Name string
Alias string Alias string
Args []Arg Args []Arg
argsA [10]Arg argsA [10]Arg
ParentID int16 Children []int32
Children []int16 childrenA [10]int32
childrenA [10]int16
} }
type Arg struct { type Arg struct {
@ -277,7 +277,7 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
return nil, errors.New("expecting an alias or field name") return nil, errors.New("expecting an alias or field name")
} }
fields = append(fields, Field{ID: int16(len(fields))}) fields = append(fields, Field{ID: int32(len(fields))})
f := &fields[(len(fields) - 1)] f := &fields[(len(fields) - 1)]
f.Args = f.argsA[:0] f.Args = f.argsA[:0]
f.Children = f.childrenA[:0] f.Children = f.childrenA[:0]
@ -288,7 +288,7 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
if f.ID != 0 { if f.ID != 0 {
intf := st.Peek() intf := st.Peek()
pid, ok := intf.(int16) pid, ok := intf.(int32)
if !ok { if !ok {
return nil, fmt.Errorf("14: unexpected value %v (%t)", intf, intf) return nil, fmt.Errorf("14: unexpected value %v (%t)", intf, intf)

View File

@ -29,8 +29,8 @@ type Column struct {
} }
type Select struct { type Select struct {
ID int16 ID int32
ParentID int16 ParentID int32
RelID uint64 RelID uint64
Args map[string]*Node Args map[string]*Node
AsList bool AsList bool
@ -42,7 +42,7 @@ type Select struct {
OrderBy []*OrderBy OrderBy []*OrderBy
DistinctOn []string DistinctOn []string
Paging Paging Paging Paging
Children []int16 Children []int32
} }
type Exp struct { type Exp struct {
@ -197,8 +197,8 @@ func (com *Compiler) CompileQuery(query string) (*QCode, error) {
} }
func (com *Compiler) compileQuery(op *Operation) (*Query, error) { func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
id := int16(0) id := int32(0)
parentID := int16(-1) parentID := int32(0)
selects := make([]Select, 0, 5) selects := make([]Select, 0, 5)
st := util.NewStack() st := util.NewStack()
@ -219,7 +219,7 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
} }
intf := st.Pop() intf := st.Pop()
fid, ok := intf.(int16) fid, ok := intf.(int32)
if !ok { if !ok {
return nil, fmt.Errorf("15: unexpected value %v (%t)", intf, intf) return nil, fmt.Errorf("15: unexpected value %v (%t)", intf, intf)
@ -236,7 +236,7 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
ID: id, ID: id,
ParentID: parentID, ParentID: parentID,
Table: tn, Table: tn,
Children: make([]int16, 0, 5), Children: make([]int32, 0, 5),
} }
if s.ID != 0 { if s.ID != 0 {

View File

@ -321,7 +321,7 @@ func (c *coreContext) render(w io.Writer, data []byte) error {
return json.NewEncoder(w).Encode(c.res) return json.NewEncoder(w).Encode(c.res)
} }
func (c *coreContext) addTrace(sel []qcode.Select, id int16, st time.Time) { func (c *coreContext) addTrace(sel []qcode.Select, id int32, st time.Time) {
et := time.Now() et := time.Now()
du := et.Sub(st) du := et.Sub(st)
@ -336,15 +336,18 @@ func (c *coreContext) addTrace(sel []qcode.Select, id int16, st time.Time) {
c.res.Extensions.Tracing.EndTime = et c.res.Extensions.Tracing.EndTime = et
c.res.Extensions.Tracing.Duration = du c.res.Extensions.Tracing.Duration = du
n := 0 n := 1
for i := id; i != -1; i = sel[i].ParentID { for i := id; i != 0; i = sel[i].ParentID {
n++ n++
} }
path := make([]string, n) path := make([]string, n)
n-- n--
for i := id; i != -1; i = sel[i].ParentID { for i := id; ; i = sel[i].ParentID {
path[n] = sel[i].Table path[n] = sel[i].Table
if sel[i].ID == 0 {
break
}
n-- n--
} }
@ -368,7 +371,7 @@ func parentFieldIds(h *xxhash.Digest, sel []qcode.Select, skipped uint32) (
c := 0 c := 0
for i := range sel { for i := range sel {
s := &sel[i] s := &sel[i]
if isSkipped(skipped, uint16(s.ID)) { if isSkipped(skipped, uint32(s.ID)) {
c++ c++
} }
} }
@ -385,7 +388,7 @@ func parentFieldIds(h *xxhash.Digest, sel []qcode.Select, skipped uint32) (
for i := range sel { for i := range sel {
s := &sel[i] s := &sel[i]
if isSkipped(skipped, uint16(s.ID)) == false { if isSkipped(skipped, uint32(s.ID)) == false {
continue continue
} }
@ -404,7 +407,7 @@ func parentFieldIds(h *xxhash.Digest, sel []qcode.Select, skipped uint32) (
return fm, sm return fm, sm
} }
func isSkipped(n uint32, pos uint16) bool { func isSkipped(n uint32, pos uint32) bool {
return ((n & (1 << pos)) != 0) return ((n & (1 << pos)) != 0)
} }