super-graph/psql/query.go

1198 lines
27 KiB
Go
Raw Permalink Normal View History

2019-03-24 14:57:29 +01:00
package psql
import (
"bytes"
2019-09-05 06:09:56 +02:00
"encoding/json"
2019-03-24 14:57:29 +01:00
"errors"
"fmt"
"io"
"math"
"strings"
2019-03-24 14:57:29 +01:00
"github.com/cespare/xxhash/v2"
2019-03-24 14:57:29 +01:00
"github.com/dosco/super-graph/qcode"
"github.com/dosco/super-graph/util"
)
2019-05-13 01:27:26 +02:00
const (
2019-06-08 02:53:08 +02:00
empty = ""
closeBlock = 500
2019-05-13 01:27:26 +02:00
)
2019-09-05 06:09:56 +02:00
type Variables map[string]json.RawMessage
2019-04-08 08:47:59 +02:00
type Config struct {
Schema *DBSchema
Vars map[string]string
2019-04-08 08:47:59 +02:00
}
2019-03-24 14:57:29 +01:00
type Compiler struct {
schema *DBSchema
2019-04-08 08:47:59 +02:00
vars map[string]string
2019-03-24 14:57:29 +01:00
}
2019-04-08 08:47:59 +02:00
func NewCompiler(conf Config) *Compiler {
return &Compiler{conf.Schema, conf.Vars}
2019-03-24 14:57:29 +01:00
}
func (c *Compiler) AddRelationship(child, parent string, rel *DBRel) error {
return c.schema.SetRel(child, parent, rel)
2019-05-13 01:27:26 +02:00
}
func (c *Compiler) IDColumn(table string) (string, error) {
t, err := c.schema.GetTable(table)
if err != nil {
return empty, err
2019-05-13 01:27:26 +02:00
}
return t.PrimaryCol, nil
2019-05-13 01:27:26 +02:00
}
2019-06-08 02:53:08 +02:00
type compilerContext struct {
w io.Writer
2019-06-08 02:53:08 +02:00
s []qcode.Select
*Compiler
}
2019-09-05 06:09:56 +02:00
func (co *Compiler) CompileEx(qc *qcode.QCode, vars Variables) (uint32, []byte, error) {
2019-06-02 01:48:42 +02:00
w := &bytes.Buffer{}
2019-09-05 06:09:56 +02:00
skipped, err := co.Compile(qc, w, vars)
2019-06-02 01:48:42 +02:00
return skipped, w.Bytes(), err
}
func (co *Compiler) Compile(qc *qcode.QCode, w io.Writer, vars Variables) (uint32, error) {
2019-09-05 06:09:56 +02:00
switch qc.Type {
case qcode.QTQuery:
return co.compileQuery(qc, w)
2019-10-14 08:51:36 +02:00
case qcode.QTInsert, qcode.QTUpdate, qcode.QTDelete, qcode.QTUpsert:
2019-09-05 06:09:56 +02:00
return co.compileMutation(qc, w, vars)
}
2019-10-14 08:51:36 +02:00
return 0, fmt.Errorf("Unknown operation type %d", qc.Type)
2019-09-05 06:09:56 +02:00
}
func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer) (uint32, error) {
2019-09-05 06:09:56 +02:00
if len(qc.Selects) == 0 {
2019-06-02 01:48:42 +02:00
return 0, errors.New("empty query")
2019-05-13 01:27:26 +02:00
}
2019-09-05 06:09:56 +02:00
c := &compilerContext{w, qc.Selects, co}
root := &qc.Selects[0]
2019-03-24 14:57:29 +01:00
2019-09-20 06:19:11 +02:00
ti, err := c.schema.GetTable(root.Table)
if err != nil {
return 0, err
}
2019-06-08 02:53:08 +02:00
st := NewStack()
st.Push(root.ID + closeBlock)
st.Push(root.ID)
2019-03-24 14:57:29 +01:00
//fmt.Fprintf(w, `SELECT json_object_agg('%s', %s) FROM (`,
//root.FieldName, root.Table)
io.WriteString(c.w, `SELECT json_object_agg('`)
io.WriteString(c.w, root.FieldName)
io.WriteString(c.w, `', `)
2019-09-20 06:19:11 +02:00
if ti.Singular == false {
io.WriteString(c.w, root.Table)
2019-09-20 06:19:11 +02:00
} else {
io.WriteString(c.w, "sel_json_")
2019-09-20 06:19:11 +02:00
int2string(c.w, root.ID)
}
io.WriteString(c.w, `) FROM (`)
2019-05-13 01:27:26 +02:00
var ignored uint32
2019-03-24 14:57:29 +01:00
for {
if st.Len() == 0 {
break
}
2019-06-08 02:53:08 +02:00
id := st.Pop()
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
if id < closeBlock {
sel := &c.s[id]
ti, err := c.schema.GetTable(sel.Table)
if err != nil {
return 0, err
}
2019-06-08 02:53:08 +02:00
if sel.ID != 0 {
if err = c.renderLateralJoin(sel); err != nil {
2019-06-08 02:53:08 +02:00
return 0, err
}
}
skipped, err := c.renderSelect(sel, ti)
2019-05-13 01:27:26 +02:00
if err != nil {
2019-06-02 01:48:42 +02:00
return 0, err
2019-05-13 01:27:26 +02:00
}
ignored |= skipped
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
for _, cid := range sel.Children {
if hasBit(skipped, uint32(cid)) {
2019-05-13 01:27:26 +02:00
continue
}
2019-06-08 02:53:08 +02:00
child := &c.s[cid]
2019-06-08 02:53:08 +02:00
st.Push(child.ID + closeBlock)
st.Push(child.ID)
2019-03-24 14:57:29 +01:00
}
2019-06-08 02:53:08 +02:00
} else {
sel := &c.s[(id - closeBlock)]
ti, err := c.schema.GetTable(sel.Table)
if err != nil {
return 0, err
}
err = c.renderSelectClose(sel, ti)
if err != nil {
return 0, err
}
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
if sel.ID != 0 {
if err = c.renderLateralJoinClose(sel); err != nil {
2019-06-08 02:53:08 +02:00
return 0, err
}
}
2019-03-24 14:57:29 +01:00
}
}
io.WriteString(c.w, `)`)
2019-06-08 02:53:08 +02:00
alias(c.w, `done_1337`)
2019-03-24 14:57:29 +01:00
2019-06-02 01:48:42 +02:00
return ignored, nil
2019-03-24 14:57:29 +01:00
}
func (c *compilerContext) processChildren(sel *qcode.Select, ti *DBTableInfo) (uint32, []*qcode.Column) {
2019-05-13 01:27:26 +02:00
var skipped uint32
2019-06-08 02:53:08 +02:00
cols := make([]*qcode.Column, 0, len(sel.Cols))
colmap := make(map[string]struct{}, len(sel.Cols))
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
for i := range sel.Cols {
colmap[sel.Cols[i].Name] = struct{}{}
2019-03-24 14:57:29 +01:00
}
2019-06-08 02:53:08 +02:00
for _, id := range sel.Children {
child := &c.s[id]
2019-03-24 14:57:29 +01:00
rel, err := c.schema.GetRel(child.Table, ti.Name)
if err != nil {
2019-05-13 01:27:26 +02:00
skipped |= (1 << uint(id))
2019-03-24 14:57:29 +01:00
continue
}
2019-05-13 01:27:26 +02:00
switch rel.Type {
case RelOneToMany:
fallthrough
case RelBelongTo:
2019-03-24 14:57:29 +01:00
if _, ok := colmap[rel.Col2]; !ok {
2019-10-03 09:08:01 +02:00
cols = append(cols, &qcode.Column{ti.Name, rel.Col2, rel.Col2})
2019-03-24 14:57:29 +01:00
}
2019-05-13 01:27:26 +02:00
case RelOneToManyThrough:
2019-03-24 14:57:29 +01:00
if _, ok := colmap[rel.Col1]; !ok {
2019-10-03 09:08:01 +02:00
cols = append(cols, &qcode.Column{ti.Name, rel.Col1, rel.Col1})
2019-03-24 14:57:29 +01:00
}
2019-05-13 01:27:26 +02:00
case RelRemote:
if _, ok := colmap[rel.Col1]; !ok {
2019-10-03 09:08:01 +02:00
cols = append(cols, &qcode.Column{ti.Name, rel.Col1, rel.Col2})
2019-05-13 01:27:26 +02:00
}
skipped |= (1 << uint(id))
default:
skipped |= (1 << uint(id))
2019-03-24 14:57:29 +01:00
}
}
2019-05-13 01:27:26 +02:00
return skipped, cols
2019-03-24 14:57:29 +01:00
}
func (c *compilerContext) renderSelect(sel *qcode.Select, ti *DBTableInfo) (uint32, error) {
skipped, childCols := c.processChildren(sel, ti)
2019-06-08 02:53:08 +02:00
hasOrder := len(sel.OrderBy) != 0
2019-03-24 14:57:29 +01:00
// SELECT
if ti.Singular == false {
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `SELECT coalesce(json_agg("%s"`, c.sel.Table)
io.WriteString(c.w, `SELECT coalesce(json_agg("`)
io.WriteString(c.w, "sel_json_")
2019-09-20 06:19:11 +02:00
int2string(c.w, sel.ID)
io.WriteString(c.w, `"`)
2019-03-24 14:57:29 +01:00
if hasOrder {
2019-10-03 09:08:01 +02:00
err := c.renderOrderBy(sel, ti)
2019-03-24 14:57:29 +01:00
if err != nil {
2019-05-13 01:27:26 +02:00
return skipped, err
2019-03-24 14:57:29 +01:00
}
}
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `), '[]') AS "%s" FROM (`, c.sel.Table)
io.WriteString(c.w, `), '[]')`)
2019-06-08 02:53:08 +02:00
alias(c.w, sel.Table)
io.WriteString(c.w, ` FROM (`)
2019-03-24 14:57:29 +01:00
}
// ROW-TO-JSON
io.WriteString(c.w, `SELECT `)
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
if len(sel.DistinctOn) != 0 {
2019-10-03 09:08:01 +02:00
c.renderDistinctOn(sel, ti)
2019-03-24 14:57:29 +01:00
}
io.WriteString(c.w, `row_to_json((`)
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `SELECT "sel_%d" FROM (SELECT `, c.sel.ID)
io.WriteString(c.w, `SELECT "sel_`)
2019-06-08 02:53:08 +02:00
int2string(c.w, sel.ID)
io.WriteString(c.w, `" FROM (SELECT `)
2019-03-24 14:57:29 +01:00
// Combined column names
2019-10-03 09:08:01 +02:00
c.renderColumns(sel, ti)
2019-03-24 14:57:29 +01:00
2019-10-03 09:08:01 +02:00
c.renderRemoteRelColumns(sel, ti)
2019-05-13 01:27:26 +02:00
2019-10-03 09:08:01 +02:00
err := c.renderJoinedColumns(sel, ti, skipped)
2019-03-24 14:57:29 +01:00
if err != nil {
2019-05-13 01:27:26 +02:00
return skipped, err
2019-03-24 14:57:29 +01:00
}
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `) AS "sel_%d"`, c.sel.ID)
io.WriteString(c.w, `)`)
2019-06-08 02:53:08 +02:00
aliasWithID(c.w, "sel", sel.ID)
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `)) AS "%s"`, c.sel.Table)
io.WriteString(c.w, `))`)
2019-09-20 06:19:11 +02:00
aliasWithID(c.w, "sel_json", sel.ID)
2019-03-24 14:57:29 +01:00
// END-ROW-TO-JSON
if hasOrder {
2019-10-03 09:08:01 +02:00
c.renderOrderByColumns(sel, ti)
2019-03-24 14:57:29 +01:00
}
// END-SELECT
// FROM (SELECT .... )
err = c.renderBaseSelect(sel, ti, childCols, skipped)
if err != nil {
2019-05-13 01:27:26 +02:00
return skipped, err
2019-03-24 14:57:29 +01:00
}
// END-FROM
2019-05-13 01:27:26 +02:00
return skipped, nil
2019-03-24 14:57:29 +01:00
}
func (c *compilerContext) renderSelectClose(sel *qcode.Select, ti *DBTableInfo) error {
2019-06-08 02:53:08 +02:00
hasOrder := len(sel.OrderBy) != 0
2019-03-24 14:57:29 +01:00
if hasOrder {
2019-10-03 09:08:01 +02:00
err := c.renderOrderBy(sel, ti)
2019-03-24 14:57:29 +01:00
if err != nil {
return err
}
}
2019-10-14 08:51:36 +02:00
switch {
case sel.Paging.NoLimit:
break
2019-10-14 08:51:36 +02:00
case len(sel.Paging.Limit) != 0:
//fmt.Fprintf(w, ` LIMIT ('%s') :: integer`, c.sel.Paging.Limit)
io.WriteString(c.w, ` LIMIT ('`)
io.WriteString(c.w, sel.Paging.Limit)
io.WriteString(c.w, `') :: integer`)
2019-10-14 08:51:36 +02:00
case ti.Singular:
io.WriteString(c.w, ` LIMIT ('1') :: integer`)
2019-10-14 08:51:36 +02:00
default:
io.WriteString(c.w, ` LIMIT ('20') :: integer`)
2019-03-24 14:57:29 +01:00
}
2019-06-08 02:53:08 +02:00
if len(sel.Paging.Offset) != 0 {
//fmt.Fprintf(w, ` OFFSET ('%s') :: integer`, c.sel.Paging.Offset)
io.WriteString(c.w, `OFFSET ('`)
io.WriteString(c.w, sel.Paging.Offset)
io.WriteString(c.w, `') :: integer`)
2019-03-24 14:57:29 +01:00
}
if ti.Singular == false {
2019-09-20 06:19:11 +02:00
//fmt.Fprintf(w, `) AS "sel_json_agg_%d"`, c.sel.ID)
io.WriteString(c.w, `)`)
2019-09-20 06:19:11 +02:00
aliasWithID(c.w, "sel_json_agg", sel.ID)
2019-03-24 14:57:29 +01:00
}
return nil
}
func (c *compilerContext) renderLateralJoin(sel *qcode.Select) error {
io.WriteString(c.w, ` LEFT OUTER JOIN LATERAL (`)
2019-03-24 14:57:29 +01:00
return nil
}
func (c *compilerContext) renderLateralJoinClose(sel *qcode.Select) error {
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `) AS "%s_%d_join" ON ('true')`, c.sel.Table, c.sel.ID)
io.WriteString(c.w, `)`)
2019-06-08 02:53:08 +02:00
aliasWithIDSuffix(c.w, sel.Table, sel.ID, "_join")
io.WriteString(c.w, ` ON ('true')`)
2019-03-24 14:57:29 +01:00
return nil
}
func (c *compilerContext) renderJoin(sel *qcode.Select) error {
parent := &c.s[sel.ParentID]
return c.renderJoinByName(sel.Table, parent.Table, parent.ID)
}
func (c *compilerContext) renderJoinByName(table, parent string, id int32) error {
rel, err := c.schema.GetRel(table, parent)
if err != nil {
return err
2019-03-24 14:57:29 +01:00
}
// This join is only required for one-to-many relations since
// these make use of join tables that need to be pulled in.
2019-03-24 14:57:29 +01:00
if rel.Type != RelOneToManyThrough {
return err
2019-03-24 14:57:29 +01:00
}
pt, err := c.schema.GetTable(parent)
if err != nil {
return err
}
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, ` LEFT OUTER JOIN "%s" ON (("%s"."%s") = ("%s_%d"."%s"))`,
2019-06-08 02:53:08 +02:00
//rel.Through, rel.Through, rel.ColT, c.parent.Table, c.parent.ID, rel.Col1)
io.WriteString(c.w, ` LEFT OUTER JOIN "`)
io.WriteString(c.w, rel.Through)
io.WriteString(c.w, `" ON ((`)
2019-06-08 02:53:08 +02:00
colWithTable(c.w, rel.Through, rel.ColT)
io.WriteString(c.w, `) = (`)
if id != -1 {
colWithTableID(c.w, pt.Name, id, rel.Col1)
} else {
colWithTable(c.w, pt.Name, rel.Col1)
}
io.WriteString(c.w, `))`)
return nil
2019-06-08 02:53:08 +02:00
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderColumns(sel *qcode.Select, ti *DBTableInfo) {
2019-10-14 08:51:36 +02:00
i := 0
for _, col := range sel.Cols {
n := funcPrefixLen(col.Name)
if n != 0 {
if sel.Functions == false {
continue
}
if len(sel.Allowed) != 0 {
2019-10-14 08:51:36 +02:00
if _, ok := sel.Allowed[col.Name[n:]]; !ok {
continue
}
}
} else {
if len(sel.Allowed) != 0 {
2019-10-14 08:51:36 +02:00
if _, ok := sel.Allowed[col.Name]; !ok {
continue
}
}
}
2019-05-13 01:27:26 +02:00
if i != 0 {
2019-06-08 02:53:08 +02:00
io.WriteString(c.w, ", ")
2019-05-13 01:27:26 +02:00
}
//fmt.Fprintf(w, `"%s_%d"."%s" AS "%s"`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, c.sel.ID, col.Name, col.FieldName)
2019-10-03 09:08:01 +02:00
colWithTableIDAlias(c.w, ti.Name, sel.ID, col.Name, col.FieldName)
2019-10-14 08:51:36 +02:00
i++
2019-05-13 01:27:26 +02:00
}
}
2019-03-24 14:57:29 +01:00
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderRemoteRelColumns(sel *qcode.Select, ti *DBTableInfo) {
2019-05-13 01:27:26 +02:00
i := 0
2019-06-08 02:53:08 +02:00
for _, id := range sel.Children {
child := &c.s[id]
2019-05-13 01:27:26 +02:00
rel, err := c.schema.GetRel(child.Table, sel.Table)
if err != nil || rel.Type != RelRemote {
2019-05-13 01:27:26 +02:00
continue
}
2019-06-08 02:53:08 +02:00
if i != 0 || len(sel.Cols) != 0 {
io.WriteString(c.w, ", ")
2019-03-24 14:57:29 +01:00
}
//fmt.Fprintf(w, `"%s_%d"."%s" AS "%s"`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, c.sel.ID, rel.Col1, rel.Col2)
2019-10-03 09:08:01 +02:00
colWithTableID(c.w, ti.Name, sel.ID, rel.Col1)
2019-06-08 02:53:08 +02:00
alias(c.w, rel.Col2)
2019-05-13 01:27:26 +02:00
i++
2019-03-24 14:57:29 +01:00
}
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderJoinedColumns(sel *qcode.Select, ti *DBTableInfo, skipped uint32) error {
2019-06-08 02:53:08 +02:00
colsRendered := len(sel.Cols) != 0
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
for _, id := range sel.Children {
skipThis := hasBit(skipped, uint32(id))
2019-03-24 14:57:29 +01:00
2019-05-13 01:27:26 +02:00
if colsRendered && !skipThis {
2019-06-08 02:53:08 +02:00
io.WriteString(c.w, ", ")
2019-03-24 14:57:29 +01:00
}
2019-05-13 01:27:26 +02:00
if skipThis {
continue
}
2019-10-03 09:08:01 +02:00
childSel := &c.s[id]
2019-05-13 01:27:26 +02:00
2019-10-15 08:30:19 +02:00
cti, err := c.schema.GetTable(childSel.Table)
if err != nil {
return err
2019-10-15 08:30:19 +02:00
}
//fmt.Fprintf(w, `"%s_%d_join"."%s" AS "%s"`,
//s.Table, s.ID, s.Table, s.FieldName)
2019-10-15 08:30:19 +02:00
if cti.Singular {
io.WriteString(c.w, `"sel_json_`)
2019-10-15 08:30:19 +02:00
int2string(c.w, childSel.ID)
io.WriteString(c.w, `" AS "`)
io.WriteString(c.w, childSel.FieldName)
io.WriteString(c.w, `"`)
2019-10-15 08:30:19 +02:00
} else {
colWithTableIDSuffixAlias(c.w, childSel.Table, childSel.ID,
"_join", childSel.Table, childSel.FieldName)
}
2019-03-24 14:57:29 +01:00
}
return nil
}
func (c *compilerContext) renderBaseSelect(sel *qcode.Select, ti *DBTableInfo,
2019-06-08 02:53:08 +02:00
childCols []*qcode.Column, skipped uint32) error {
var groupBy []int
2019-06-08 02:53:08 +02:00
isRoot := sel.ID == 0
isFil := sel.Where != nil
isSearch := sel.Args["search"] != nil
isAgg := false
io.WriteString(c.w, ` FROM (SELECT `)
2019-10-14 08:51:36 +02:00
i := 0
for n, col := range sel.Cols {
cn := col.Name
2019-06-08 02:53:08 +02:00
_, isRealCol := ti.Columns[cn]
2019-04-07 07:12:11 +02:00
2019-04-09 03:24:29 +02:00
if !isRealCol {
if isSearch {
switch {
case cn == "search_rank":
2019-06-08 02:53:08 +02:00
cn = ti.TSVCol
arg := sel.Args["search"]
2019-10-14 08:51:36 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-10-14 08:51:36 +02:00
}
//fmt.Fprintf(w, `ts_rank("%s"."%s", to_tsquery('%s')) AS %s`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, cn, arg.Val, col.Name)
io.WriteString(c.w, `ts_rank(`)
2019-10-03 09:08:01 +02:00
colWithTable(c.w, ti.Name, cn)
io.WriteString(c.w, `, to_tsquery('`)
io.WriteString(c.w, arg.Val)
io.WriteString(c.w, `')`)
2019-06-08 02:53:08 +02:00
alias(c.w, col.Name)
2019-10-14 08:51:36 +02:00
i++
2019-04-07 07:12:11 +02:00
2019-04-09 03:24:29 +02:00
case strings.HasPrefix(cn, "search_headline_"):
cn = cn[16:]
2019-06-08 02:53:08 +02:00
arg := sel.Args["search"]
2019-10-14 08:51:36 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-10-14 08:51:36 +02:00
}
//fmt.Fprintf(w, `ts_headline("%s"."%s", to_tsquery('%s')) AS %s`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, cn, arg.Val, col.Name)
io.WriteString(c.w, `ts_headlinek(`)
2019-10-03 09:08:01 +02:00
colWithTable(c.w, ti.Name, cn)
io.WriteString(c.w, `, to_tsquery('`)
io.WriteString(c.w, arg.Val)
io.WriteString(c.w, `')`)
2019-06-08 02:53:08 +02:00
alias(c.w, col.Name)
2019-10-14 08:51:36 +02:00
i++
2019-04-09 03:24:29 +02:00
}
} else {
pl := funcPrefixLen(cn)
if pl == 0 {
2019-10-14 08:51:36 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-10-14 08:51:36 +02:00
}
//fmt.Fprintf(w, `'%s not defined' AS %s`, cn, col.Name)
io.WriteString(c.w, `'`)
io.WriteString(c.w, cn)
io.WriteString(c.w, ` not defined'`)
2019-06-08 02:53:08 +02:00
alias(c.w, col.Name)
2019-10-14 08:51:36 +02:00
i++
} else if sel.Functions {
cn1 := cn[pl:]
if len(sel.Allowed) != 0 {
if _, ok := sel.Allowed[cn1]; !ok {
continue
}
2019-10-14 08:51:36 +02:00
}
if i != 0 {
io.WriteString(c.w, `, `)
2019-10-14 08:51:36 +02:00
}
fn := cn[0 : pl-1]
2019-10-14 08:51:36 +02:00
isAgg = true
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `%s("%s"."%s") AS %s`, fn, c.sel.Table, cn, col.Name)
io.WriteString(c.w, fn)
io.WriteString(c.w, `(`)
2019-10-14 08:51:36 +02:00
colWithTable(c.w, ti.Name, cn1)
io.WriteString(c.w, `)`)
2019-06-08 02:53:08 +02:00
alias(c.w, col.Name)
2019-10-14 08:51:36 +02:00
i++
}
}
2019-03-24 14:57:29 +01:00
} else {
2019-10-14 08:51:36 +02:00
groupBy = append(groupBy, n)
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `"%s"."%s"`, c.sel.Table, cn)
2019-10-14 08:51:36 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-10-14 08:51:36 +02:00
}
2019-10-03 09:08:01 +02:00
colWithTable(c.w, ti.Name, cn)
2019-10-14 08:51:36 +02:00
i++
2019-03-24 14:57:29 +01:00
}
}
2019-10-14 08:51:36 +02:00
for _, col := range childCols {
2019-05-13 01:27:26 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
}
2019-05-13 01:27:26 +02:00
//fmt.Fprintf(w, `"%s"."%s"`, col.Table, col.Name)
2019-06-08 02:53:08 +02:00
colWithTable(c.w, col.Table, col.Name)
2019-10-14 08:51:36 +02:00
i++
}
io.WriteString(c.w, ` FROM `)
2019-10-03 09:08:01 +02:00
//fmt.Fprintf(w, ` FROM "%s"`, c.sel.Table)
io.WriteString(c.w, `"`)
io.WriteString(c.w, ti.Name)
io.WriteString(c.w, `"`)
// if tn, ok := c.tmap[sel.Table]; ok {
// //fmt.Fprintf(w, ` FROM "%s" AS "%s"`, tn, c.sel.Table)
// tableWithAlias(c.w, ti.Name, sel.Table)
// } else {
// //fmt.Fprintf(w, ` FROM "%s"`, c.sel.Table)
// io.WriteString(c.w, `"`)
// io.WriteString(c.w, sel.Table)
// io.WriteString(c.w, `"`)
// }
2019-04-04 06:53:24 +02:00
if isRoot && isFil {
io.WriteString(c.w, ` WHERE (`)
if err := c.renderWhere(sel, ti); err != nil {
2019-04-04 06:53:24 +02:00
return err
}
io.WriteString(c.w, `)`)
}
2019-04-04 06:53:24 +02:00
if !isRoot {
if err := c.renderJoin(sel); err != nil {
return err
}
2019-04-04 06:53:24 +02:00
io.WriteString(c.w, ` WHERE (`)
2019-10-03 09:08:01 +02:00
if err := c.renderRelationship(sel, ti); err != nil {
return err
}
2019-04-04 06:53:24 +02:00
if isFil {
io.WriteString(c.w, ` AND `)
if err := c.renderWhere(sel, ti); err != nil {
return err
}
}
io.WriteString(c.w, `)`)
}
2019-04-04 06:53:24 +02:00
if isAgg {
if len(groupBy) != 0 {
io.WriteString(c.w, ` GROUP BY `)
2019-04-04 06:53:24 +02:00
for i, id := range groupBy {
2019-05-13 01:27:26 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-04-04 06:53:24 +02:00
}
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `"%s"."%s"`, c.sel.Table, c.sel.Cols[id].Name)
2019-10-03 09:08:01 +02:00
colWithTable(c.w, ti.Name, sel.Cols[id].Name)
}
}
}
2019-10-14 08:51:36 +02:00
switch {
case sel.Paging.NoLimit:
break
2019-10-14 08:51:36 +02:00
case len(sel.Paging.Limit) != 0:
//fmt.Fprintf(w, ` LIMIT ('%s') :: integer`, c.sel.Paging.Limit)
io.WriteString(c.w, ` LIMIT ('`)
io.WriteString(c.w, sel.Paging.Limit)
io.WriteString(c.w, `') :: integer`)
2019-10-14 08:51:36 +02:00
case ti.Singular:
io.WriteString(c.w, ` LIMIT ('1') :: integer`)
2019-10-14 08:51:36 +02:00
default:
io.WriteString(c.w, ` LIMIT ('20') :: integer`)
}
2019-06-08 02:53:08 +02:00
if len(sel.Paging.Offset) != 0 {
//fmt.Fprintf(w, ` OFFSET ('%s') :: integer`, c.sel.Paging.Offset)
io.WriteString(c.w, ` OFFSET ('`)
io.WriteString(c.w, sel.Paging.Offset)
io.WriteString(c.w, `') :: integer`)
}
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `) AS "%s_%d"`, c.sel.Table, c.sel.ID)
io.WriteString(c.w, `)`)
2019-10-03 09:08:01 +02:00
aliasWithID(c.w, ti.Name, sel.ID)
return nil
2019-03-24 14:57:29 +01:00
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderOrderByColumns(sel *qcode.Select, ti *DBTableInfo) {
2019-06-08 02:53:08 +02:00
colsRendered := len(sel.Cols) != 0
2019-03-24 14:57:29 +01:00
2019-06-08 02:53:08 +02:00
for i := range sel.OrderBy {
2019-05-13 01:27:26 +02:00
if colsRendered {
//io.WriteString(w, ", ")
io.WriteString(c.w, `, `)
2019-05-13 01:27:26 +02:00
}
2019-06-08 02:53:08 +02:00
col := sel.OrderBy[i].Col
//fmt.Fprintf(w, `"%s_%d"."%s" AS "%s_%d_%s_ob"`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, c.sel.ID, c,
//c.sel.Table, c.sel.ID, c)
2019-10-03 09:08:01 +02:00
colWithTableID(c.w, ti.Name, sel.ID, col)
io.WriteString(c.w, ` AS `)
2019-06-08 02:53:08 +02:00
tableIDColSuffix(c.w, sel.Table, sel.ID, col, "_ob")
2019-03-24 14:57:29 +01:00
}
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderRelationship(sel *qcode.Select, ti *DBTableInfo) error {
2019-06-08 02:53:08 +02:00
parent := c.s[sel.ParentID]
return c.renderRelationshipByName(sel.Table, parent.Table, parent.ID)
}
2019-06-08 02:53:08 +02:00
func (c *compilerContext) renderRelationshipByName(table, parent string, id int32) error {
rel, err := c.schema.GetRel(table, parent)
if err != nil {
return err
}
2019-03-24 14:57:29 +01:00
switch rel.Type {
case RelBelongTo:
//fmt.Fprintf(w, `(("%s"."%s") = ("%s_%d"."%s"))`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, rel.Col1, c.parent.Table, c.parent.ID, rel.Col2)
io.WriteString(c.w, `((`)
colWithTable(c.w, table, rel.Col1)
io.WriteString(c.w, `) = (`)
if id != -1 {
colWithTableID(c.w, parent, id, rel.Col2)
} else {
colWithTable(c.w, parent, rel.Col2)
}
io.WriteString(c.w, `))`)
2019-03-24 14:57:29 +01:00
case RelOneToMany:
//fmt.Fprintf(w, `(("%s"."%s") = ("%s_%d"."%s"))`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, rel.Col1, c.parent.Table, c.parent.ID, rel.Col2)
io.WriteString(c.w, `((`)
colWithTable(c.w, table, rel.Col1)
io.WriteString(c.w, `) = (`)
if id != -1 {
colWithTableID(c.w, parent, id, rel.Col2)
} else {
colWithTable(c.w, parent, rel.Col2)
}
io.WriteString(c.w, `))`)
2019-03-24 14:57:29 +01:00
case RelOneToManyThrough:
// This requires the through table to be joined onto this select
//fmt.Fprintf(w, `(("%s"."%s") = ("%s"."%s"))`,
2019-06-08 02:53:08 +02:00
//c.sel.Table, rel.Col1, rel.Through, rel.Col2)
io.WriteString(c.w, `((`)
colWithTable(c.w, table, rel.Col1)
io.WriteString(c.w, `) = (`)
2019-06-08 02:53:08 +02:00
colWithTable(c.w, rel.Through, rel.Col2)
io.WriteString(c.w, `))`)
2019-03-24 14:57:29 +01:00
}
return nil
2019-03-24 14:57:29 +01:00
}
func (c *compilerContext) renderWhere(sel *qcode.Select, ti *DBTableInfo) error {
2019-03-24 14:57:29 +01:00
st := util.NewStack()
2019-06-08 02:53:08 +02:00
if sel.Where != nil {
st.Push(sel.Where)
}
2019-03-24 14:57:29 +01:00
for {
if st.Len() == 0 {
break
}
intf := st.Pop()
switch val := intf.(type) {
case qcode.ExpOp:
switch val {
case qcode.OpAnd:
io.WriteString(c.w, ` AND `)
2019-03-24 14:57:29 +01:00
case qcode.OpOr:
io.WriteString(c.w, ` OR `)
2019-03-24 14:57:29 +01:00
case qcode.OpNot:
io.WriteString(c.w, `NOT `)
case qcode.OpFalse:
io.WriteString(c.w, `false`)
2019-03-24 14:57:29 +01:00
default:
2019-05-13 01:27:26 +02:00
return fmt.Errorf("11: unexpected value %v (%t)", intf, intf)
2019-03-24 14:57:29 +01:00
}
2019-10-03 09:08:01 +02:00
2019-03-24 14:57:29 +01:00
case *qcode.Exp:
switch val.Op {
case qcode.OpFalse:
st.Push(val.Op)
qcode.FreeExp(val)
case qcode.OpAnd, qcode.OpOr:
for i := len(val.Children) - 1; i >= 0; i-- {
st.Push(val.Children[i])
if i > 0 {
st.Push(val.Op)
}
}
qcode.FreeExp(val)
2019-10-03 09:08:01 +02:00
2019-03-24 14:57:29 +01:00
case qcode.OpNot:
st.Push(val.Children[0])
st.Push(qcode.OpNot)
qcode.FreeExp(val)
2019-03-24 14:57:29 +01:00
2019-10-03 09:08:01 +02:00
default:
if len(val.NestedCols) != 0 {
io.WriteString(c.w, `EXISTS `)
2019-10-03 09:08:01 +02:00
if err := c.renderNestedWhere(val, sel, ti); err != nil {
return err
2019-10-03 09:08:01 +02:00
}
} else {
//fmt.Fprintf(w, `(("%s"."%s") `, c.sel.Table, val.Col)
if err := c.renderOp(val, sel, ti); err != nil {
return err
2019-10-03 09:08:01 +02:00
}
qcode.FreeExp(val)
}
2019-10-03 09:08:01 +02:00
}
2019-03-24 14:57:29 +01:00
default:
2019-05-13 01:27:26 +02:00
return fmt.Errorf("12: unexpected value %v (%t)", intf, intf)
2019-03-24 14:57:29 +01:00
}
2019-03-24 14:57:29 +01:00
}
return nil
}
func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, sel *qcode.Select, ti *DBTableInfo) error {
for i := 0; i < len(ex.NestedCols)-1; i++ {
cti, err := c.schema.GetTable(ex.NestedCols[i])
if err != nil {
return err
}
if i != 0 {
io.WriteString(c.w, ` AND `)
}
io.WriteString(c.w, `(SELECT 1 FROM `)
io.WriteString(c.w, cti.Name)
if err := c.renderJoinByName(cti.Name, ti.Name, -1); err != nil {
return err
}
io.WriteString(c.w, ` WHERE `)
if err := c.renderRelationshipByName(cti.Name, ti.Name, -1); err != nil {
return err
}
}
for i := 0; i < len(ex.NestedCols)-1; i++ {
io.WriteString(c.w, `)`)
}
return nil
}
func (c *compilerContext) renderOp(ex *qcode.Exp, sel *qcode.Select, ti *DBTableInfo) error {
if len(ex.Col) != 0 {
io.WriteString(c.w, `((`)
colWithTable(c.w, ti.Name, ex.Col)
io.WriteString(c.w, `) `)
}
switch ex.Op {
case qcode.OpEquals:
io.WriteString(c.w, `=`)
case qcode.OpNotEquals:
io.WriteString(c.w, `!=`)
case qcode.OpGreaterOrEquals:
io.WriteString(c.w, `>=`)
case qcode.OpLesserOrEquals:
io.WriteString(c.w, `<=`)
case qcode.OpGreaterThan:
io.WriteString(c.w, `>`)
case qcode.OpLesserThan:
io.WriteString(c.w, `<`)
case qcode.OpIn:
io.WriteString(c.w, `IN`)
case qcode.OpNotIn:
io.WriteString(c.w, `NOT IN`)
case qcode.OpLike:
io.WriteString(c.w, `LIKE`)
case qcode.OpNotLike:
io.WriteString(c.w, `NOT LIKE`)
case qcode.OpILike:
io.WriteString(c.w, `ILIKE`)
case qcode.OpNotILike:
io.WriteString(c.w, `NOT ILIKE`)
case qcode.OpSimilar:
io.WriteString(c.w, `SIMILAR TO`)
case qcode.OpNotSimilar:
io.WriteString(c.w, `NOT SIMILAR TO`)
case qcode.OpContains:
io.WriteString(c.w, `@>`)
case qcode.OpContainedIn:
io.WriteString(c.w, `<@`)
case qcode.OpHasKey:
io.WriteString(c.w, `?`)
case qcode.OpHasKeyAny:
io.WriteString(c.w, `?|`)
case qcode.OpHasKeyAll:
io.WriteString(c.w, `?&`)
case qcode.OpIsNull:
if strings.EqualFold(ex.Val, "true") {
io.WriteString(c.w, `IS NULL)`)
} else {
io.WriteString(c.w, `IS NOT NULL)`)
}
return nil
case qcode.OpEqID:
if len(ti.PrimaryCol) == 0 {
return fmt.Errorf("no primary key column defined for %s", ti.Name)
}
//fmt.Fprintf(w, `(("%s") =`, c.ti.PrimaryCol)
io.WriteString(c.w, `((`)
colWithTable(c.w, ti.Name, ti.PrimaryCol)
//io.WriteString(c.w, ti.PrimaryCol)
io.WriteString(c.w, `) =`)
case qcode.OpTsQuery:
if len(ti.TSVCol) == 0 {
return fmt.Errorf("no tsv column defined for %s", ti.Name)
}
//fmt.Fprintf(w, `(("%s") @@ to_tsquery('%s'))`, c.ti.TSVCol, val.Val)
io.WriteString(c.w, `(("`)
io.WriteString(c.w, ti.TSVCol)
io.WriteString(c.w, `") @@ to_tsquery('`)
io.WriteString(c.w, ex.Val)
io.WriteString(c.w, `'))`)
return nil
default:
return fmt.Errorf("[Where] unexpected op code %d", ex.Op)
}
if ex.Type == qcode.ValList {
c.renderList(ex)
} else {
c.renderVal(ex, c.vars)
}
io.WriteString(c.w, `)`)
return nil
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderOrderBy(sel *qcode.Select, ti *DBTableInfo) error {
io.WriteString(c.w, ` ORDER BY `)
2019-03-24 14:57:29 +01:00
for i := range sel.OrderBy {
2019-05-13 01:27:26 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-05-13 01:27:26 +02:00
}
2019-03-24 14:57:29 +01:00
ob := sel.OrderBy[i]
switch ob.Order {
case qcode.OrderAsc:
//fmt.Fprintf(w, `"%s_%d.ob.%s" ASC`, sel.Table, sel.ID, ob.Col)
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, ob.Col, "_ob")
io.WriteString(c.w, ` ASC`)
2019-03-24 14:57:29 +01:00
case qcode.OrderDesc:
//fmt.Fprintf(w, `"%s_%d.ob.%s" DESC`, sel.Table, sel.ID, ob.Col)
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, ob.Col, "_ob")
io.WriteString(c.w, ` DESC`)
2019-03-24 14:57:29 +01:00
case qcode.OrderAscNullsFirst:
//fmt.Fprintf(w, `"%s_%d.ob.%s" ASC NULLS FIRST`, sel.Table, sel.ID, ob.Col)
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, ob.Col, "_ob")
io.WriteString(c.w, ` ASC NULLS FIRST`)
2019-03-24 14:57:29 +01:00
case qcode.OrderDescNullsFirst:
//fmt.Fprintf(w, `%s_%d.ob.%s DESC NULLS FIRST`, sel.Table, sel.ID, ob.Col)
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, ob.Col, "_ob")
io.WriteString(c.w, ` DESC NULLLS FIRST`)
2019-03-24 14:57:29 +01:00
case qcode.OrderAscNullsLast:
//fmt.Fprintf(w, `"%s_%d.ob.%s ASC NULLS LAST`, sel.Table, sel.ID, ob.Col)
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, ob.Col, "_ob")
io.WriteString(c.w, ` ASC NULLS LAST`)
2019-03-24 14:57:29 +01:00
case qcode.OrderDescNullsLast:
//fmt.Fprintf(w, `%s_%d.ob.%s DESC NULLS LAST`, sel.Table, sel.ID, ob.Col)
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, ob.Col, "_ob")
io.WriteString(c.w, ` DESC NULLS LAST`)
2019-03-24 14:57:29 +01:00
default:
2019-05-13 06:05:08 +02:00
return fmt.Errorf("13: unexpected value %v", ob.Order)
2019-03-24 14:57:29 +01:00
}
}
return nil
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderDistinctOn(sel *qcode.Select, ti *DBTableInfo) {
2019-06-08 02:53:08 +02:00
io.WriteString(c.w, `DISTINCT ON (`)
for i := range sel.DistinctOn {
2019-05-13 01:27:26 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-03-24 14:57:29 +01:00
}
2019-06-08 02:53:08 +02:00
//fmt.Fprintf(w, `"%s_%d.ob.%s"`, c.sel.Table, c.sel.ID, c.sel.DistinctOn[i])
2019-10-03 09:08:01 +02:00
tableIDColSuffix(c.w, ti.Name, sel.ID, sel.DistinctOn[i], "_ob")
2019-03-24 14:57:29 +01:00
}
io.WriteString(c.w, `) `)
2019-03-24 14:57:29 +01:00
}
2019-06-08 02:53:08 +02:00
func (c *compilerContext) renderList(ex *qcode.Exp) {
io.WriteString(c.w, ` (`)
2019-03-24 14:57:29 +01:00
for i := range ex.ListVal {
2019-05-13 01:27:26 +02:00
if i != 0 {
io.WriteString(c.w, `, `)
2019-05-13 01:27:26 +02:00
}
2019-03-24 14:57:29 +01:00
switch ex.ListType {
case qcode.ValBool, qcode.ValInt, qcode.ValFloat:
io.WriteString(c.w, ex.ListVal[i])
2019-03-24 14:57:29 +01:00
case qcode.ValStr:
io.WriteString(c.w, `'`)
io.WriteString(c.w, ex.ListVal[i])
io.WriteString(c.w, `'`)
2019-03-24 14:57:29 +01:00
}
}
io.WriteString(c.w, `)`)
2019-03-24 14:57:29 +01:00
}
2019-10-03 09:08:01 +02:00
func (c *compilerContext) renderVal(ex *qcode.Exp, vars map[string]string) {
2019-09-05 06:40:52 +02:00
io.WriteString(c.w, ` `)
2019-10-03 09:08:01 +02:00
2019-03-24 14:57:29 +01:00
switch ex.Type {
case qcode.ValBool, qcode.ValInt, qcode.ValFloat:
2019-04-19 07:55:03 +02:00
if len(ex.Val) != 0 {
io.WriteString(c.w, ex.Val)
2019-04-19 07:55:03 +02:00
} else {
io.WriteString(c.w, `''`)
2019-04-19 07:55:03 +02:00
}
2019-09-05 06:09:56 +02:00
2019-03-24 14:57:29 +01:00
case qcode.ValStr:
io.WriteString(c.w, `'`)
io.WriteString(c.w, ex.Val)
io.WriteString(c.w, `'`)
2019-09-05 06:09:56 +02:00
2019-03-24 14:57:29 +01:00
case qcode.ValVar:
if val, ok := vars[ex.Val]; ok {
io.WriteString(c.w, val)
2019-03-24 14:57:29 +01:00
} else {
//fmt.Fprintf(w, `'{{%s}}'`, ex.Val)
io.WriteString(c.w, `{{`)
io.WriteString(c.w, ex.Val)
io.WriteString(c.w, `}}`)
2019-03-24 14:57:29 +01:00
}
}
//io.WriteString(c.w, `)`)
2019-03-24 14:57:29 +01:00
}
func funcPrefixLen(fn string) int {
switch {
case strings.HasPrefix(fn, "avg_"):
return 4
case strings.HasPrefix(fn, "count_"):
return 6
case strings.HasPrefix(fn, "max_"):
return 4
case strings.HasPrefix(fn, "min_"):
return 4
case strings.HasPrefix(fn, "sum_"):
return 4
case strings.HasPrefix(fn, "stddev_"):
return 7
case strings.HasPrefix(fn, "stddev_pop_"):
return 11
case strings.HasPrefix(fn, "stddev_samp_"):
return 12
case strings.HasPrefix(fn, "variance_"):
return 9
case strings.HasPrefix(fn, "var_pop_"):
return 8
case strings.HasPrefix(fn, "var_samp_"):
return 9
}
return 0
}
2019-05-13 01:27:26 +02:00
2019-06-08 02:53:08 +02:00
func hasBit(n uint32, pos uint32) bool {
2019-05-13 01:27:26 +02:00
val := n & (1 << pos)
return (val > 0)
}
func alias(w io.Writer, alias string) {
io.WriteString(w, ` AS "`)
io.WriteString(w, alias)
io.WriteString(w, `"`)
}
func aliasWithID(w io.Writer, alias string, id int32) {
io.WriteString(w, ` AS "`)
io.WriteString(w, alias)
io.WriteString(w, `_`)
int2string(w, id)
io.WriteString(w, `"`)
}
func aliasWithIDSuffix(w io.Writer, alias string, id int32, suffix string) {
io.WriteString(w, ` AS "`)
io.WriteString(w, alias)
io.WriteString(w, `_`)
int2string(w, id)
io.WriteString(w, suffix)
io.WriteString(w, `"`)
}
func colWithAlias(w io.Writer, col, alias string) {
io.WriteString(w, `"`)
io.WriteString(w, col)
io.WriteString(w, `" AS "`)
io.WriteString(w, alias)
io.WriteString(w, `"`)
}
func tableWithAlias(w io.Writer, table, alias string) {
io.WriteString(w, `"`)
io.WriteString(w, table)
io.WriteString(w, `" AS "`)
io.WriteString(w, alias)
io.WriteString(w, `"`)
}
func colWithTable(w io.Writer, table, col string) {
io.WriteString(w, `"`)
io.WriteString(w, table)
io.WriteString(w, `"."`)
io.WriteString(w, col)
io.WriteString(w, `"`)
}
func colWithTableID(w io.Writer, table string, id int32, col string) {
io.WriteString(w, `"`)
io.WriteString(w, table)
io.WriteString(w, `_`)
int2string(w, id)
io.WriteString(w, `"."`)
io.WriteString(w, col)
io.WriteString(w, `"`)
}
func colWithTableIDAlias(w io.Writer, table string, id int32, col, alias string) {
io.WriteString(w, `"`)
io.WriteString(w, table)
io.WriteString(w, `_`)
int2string(w, id)
io.WriteString(w, `"."`)
io.WriteString(w, col)
io.WriteString(w, `" AS "`)
io.WriteString(w, alias)
io.WriteString(w, `"`)
}
func colWithTableIDSuffixAlias(w io.Writer, table string, id int32,
suffix, col, alias string) {
io.WriteString(w, `"`)
io.WriteString(w, table)
io.WriteString(w, `_`)
int2string(w, id)
io.WriteString(w, suffix)
io.WriteString(w, `"."`)
io.WriteString(w, col)
io.WriteString(w, `" AS "`)
io.WriteString(w, alias)
io.WriteString(w, `"`)
}
func tableIDColSuffix(w io.Writer, table string, id int32, col, suffix string) {
io.WriteString(w, `"`)
io.WriteString(w, table)
io.WriteString(w, `_`)
int2string(w, id)
io.WriteString(w, `_`)
io.WriteString(w, col)
io.WriteString(w, suffix)
io.WriteString(w, `"`)
}
const charset = "0123456789"
func int2string(w io.Writer, val int32) {
if val < 10 {
w.Write([]byte{charset[val]})
return
}
2019-06-08 02:53:08 +02:00
temp := int32(0)
val2 := val
for val2 > 0 {
temp *= 10
temp += val2 % 10
2019-06-08 02:53:08 +02:00
val2 = int32(math.Floor(float64(val2 / 10)))
}
val3 := temp
for val3 > 0 {
d := val3 % 10
val3 /= 10
w.Write([]byte{charset[d]})
}
}
func relID(h *xxhash.Digest, child, parent string) uint64 {
h.WriteString(child)
h.WriteString(parent)
v := h.Sum64()
h.Reset()
return v
}