feat: add support for polymorphic database relationships
This commit is contained in:
parent
7b5548a2c6
commit
06214a3850
|
@ -72,6 +72,7 @@ type Config struct {
|
||||||
type Table struct {
|
type Table struct {
|
||||||
Name string
|
Name string
|
||||||
Table string
|
Table string
|
||||||
|
Type string
|
||||||
Blocklist []string
|
Blocklist []string
|
||||||
Remotes []Remote
|
Remotes []Remote
|
||||||
Columns []Column
|
Columns []Column
|
||||||
|
|
98
core/init.go
98
core/init.go
|
@ -21,7 +21,7 @@ func (sg *SuperGraph) initConfig() error {
|
||||||
|
|
||||||
for i := 0; i < len(c.Tables); i++ {
|
for i := 0; i < len(c.Tables); i++ {
|
||||||
t := &c.Tables[i]
|
t := &c.Tables[i]
|
||||||
t.Name = flect.Pluralize(strings.ToLower(t.Name))
|
// t.Name = flect.Pluralize(strings.ToLower(t.Name))
|
||||||
|
|
||||||
if _, ok := tm[t.Name]; ok {
|
if _, ok := tm[t.Name]; ok {
|
||||||
sg.conf.Tables = append(c.Tables[:i], c.Tables[i+1:]...)
|
sg.conf.Tables = append(c.Tables[:i], c.Tables[i+1:]...)
|
||||||
|
@ -100,21 +100,26 @@ func getDBTableAliases(c *Config) map[string][]string {
|
||||||
for i := range c.Tables {
|
for i := range c.Tables {
|
||||||
t := c.Tables[i]
|
t := c.Tables[i]
|
||||||
|
|
||||||
if len(t.Table) == 0 || len(t.Columns) != 0 {
|
if t.Table != "" && t.Type == "" {
|
||||||
continue
|
m[t.Table] = append(m[t.Table], t.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
m[t.Table] = append(m[t.Table], t.Name)
|
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTables(c *Config, di *psql.DBInfo) error {
|
func addTables(c *Config, di *psql.DBInfo) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
for _, t := range c.Tables {
|
for _, t := range c.Tables {
|
||||||
if t.Table == "" || len(t.Columns) == 0 {
|
switch t.Type {
|
||||||
continue
|
case "json", "jsonb":
|
||||||
|
err = addJsonTable(di, t.Columns, t)
|
||||||
|
|
||||||
|
case "polymorphic":
|
||||||
|
err = addVirtualTable(di, t.Columns, t)
|
||||||
}
|
}
|
||||||
if err := addTable(di, t.Columns, t); err != nil {
|
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,17 +127,18 @@ func addTables(c *Config, di *psql.DBInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTable(di *psql.DBInfo, cols []Column, t Table) error {
|
func addJsonTable(di *psql.DBInfo, cols []Column, t Table) error {
|
||||||
|
// This is for jsonb columns that want to be tables.
|
||||||
bc, ok := di.GetColumn(t.Table, t.Name)
|
bc, ok := di.GetColumn(t.Table, t.Name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Column '%s' not found on table '%s'",
|
"json table: column '%s' not found on table '%s'",
|
||||||
t.Name, t.Table)
|
t.Name, t.Table)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bc.Type != "json" && bc.Type != "jsonb" {
|
if bc.Type != "json" && bc.Type != "jsonb" {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Column '%s' in table '%s' is of type '%s'. Only JSON or JSONB is valid",
|
"json table: column '%s' in table '%s' is of type '%s'. Only JSON or JSONB is valid",
|
||||||
t.Name, t.Table, bc.Type)
|
t.Name, t.Table, bc.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,8 +165,38 @@ func addTable(di *psql.DBInfo, cols []Column, t Table) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addVirtualTable(di *psql.DBInfo, cols []Column, t Table) error {
|
||||||
|
if len(cols) == 0 {
|
||||||
|
return fmt.Errorf("polymorphic table: no id column specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
c := cols[0]
|
||||||
|
|
||||||
|
if c.ForeignKey == "" {
|
||||||
|
return fmt.Errorf("polymorphic table: no 'related_to' specified on id column")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.SplitN(c.ForeignKey, ".", 2)
|
||||||
|
|
||||||
|
if len(s) != 2 {
|
||||||
|
return fmt.Errorf("polymorphic table: foreign key must be <type column>.<foreign key column>")
|
||||||
|
}
|
||||||
|
|
||||||
|
di.VTables = append(di.VTables, psql.VirtualTable{
|
||||||
|
Name: t.Name,
|
||||||
|
IDColumn: c.Name,
|
||||||
|
TypeColumn: s[0],
|
||||||
|
FKeyColumn: s[1],
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func addForeignKeys(c *Config, di *psql.DBInfo) error {
|
func addForeignKeys(c *Config, di *psql.DBInfo) error {
|
||||||
for _, t := range c.Tables {
|
for _, t := range c.Tables {
|
||||||
|
if t.Type != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, c := range t.Columns {
|
for _, c := range t.Columns {
|
||||||
if c.ForeignKey == "" {
|
if c.ForeignKey == "" {
|
||||||
continue
|
continue
|
||||||
|
@ -174,30 +210,52 @@ func addForeignKeys(c *Config, di *psql.DBInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addForeignKey(di *psql.DBInfo, c Column, t Table) error {
|
func addForeignKey(di *psql.DBInfo, c Column, t Table) error {
|
||||||
c1, ok := di.GetColumn(t.Name, c.Name)
|
var tn string
|
||||||
|
|
||||||
|
if t.Type == "polymorphic" {
|
||||||
|
tn = t.Table
|
||||||
|
} else {
|
||||||
|
tn = t.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
c1, ok := di.GetColumn(tn, c.Name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Invalid table '%s' or column '%s' in Config",
|
"config: invalid table '%s' or column '%s' defined",
|
||||||
t.Name, c.Name)
|
tn, c.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
v := strings.SplitN(c.ForeignKey, ".", 2)
|
v := strings.SplitN(c.ForeignKey, ".", 2)
|
||||||
if len(v) != 2 {
|
if len(v) != 2 {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Invalid foreign_key in Config for table '%s' and column '%s",
|
"config: invalid foreign_key defined for table '%s' and column '%s': %s",
|
||||||
t.Name, c.Name)
|
tn, c.Name, c.ForeignKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if it's a polymorphic foreign key
|
||||||
|
if _, ok := di.GetColumn(tn, v[0]); ok {
|
||||||
|
c2, ok := di.GetColumn(tn, v[1])
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"config: invalid column '%s' for polymorphic relationship on table '%s' and column '%s'",
|
||||||
|
v[1], tn, c.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
c1.FKeyTable = v[0]
|
||||||
|
c1.FKeyColID = []int16{c2.ID}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fkt, fkc := v[0], v[1]
|
fkt, fkc := v[0], v[1]
|
||||||
c2, ok := di.GetColumn(fkt, fkc)
|
c3, ok := di.GetColumn(fkt, fkc)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"Invalid foreign_key in Config for table '%s' and column '%s",
|
"config: foreign_key for table '%s' and column '%s' points to unknown table '%s' and column '%s'",
|
||||||
t.Name, c.Name)
|
t.Name, c.Name, v[0], v[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
c1.FKeyTable = fkt
|
c1.FKeyTable = fkt
|
||||||
c1.FKeyColID = []int16{c2.ID}
|
c1.FKeyColID = []int16{c3.ID}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,27 +156,29 @@ func (co *Compiler) compileQueryWithMetadata(
|
||||||
if id < closeBlock {
|
if id < closeBlock {
|
||||||
sel := &c.s[id]
|
sel := &c.s[id]
|
||||||
|
|
||||||
if len(sel.Cols) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ti, err := c.schema.GetTable(sel.Name)
|
ti, err := c.schema.GetTable(sel.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.md, err
|
return c.md, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if sel.ParentID == -1 {
|
if sel.Type != qcode.STUnion {
|
||||||
io.WriteString(c.w, `(`)
|
if len(sel.Cols) == 0 {
|
||||||
} else {
|
continue
|
||||||
c.renderLateralJoin(sel)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !ti.IsSingular {
|
if sel.ParentID == -1 {
|
||||||
c.renderPluralSelect(sel, ti)
|
io.WriteString(c.w, `(`)
|
||||||
}
|
} else {
|
||||||
|
c.renderLateralJoin(sel)
|
||||||
|
}
|
||||||
|
|
||||||
if err := c.renderSelect(sel, ti, vars); err != nil {
|
if !ti.IsSingular {
|
||||||
return c.md, err
|
c.renderPluralSelect(sel, ti)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.renderSelect(sel, ti, vars); err != nil {
|
||||||
|
return c.md, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cid := range sel.Children {
|
for _, cid := range sel.Children {
|
||||||
|
@ -184,10 +186,10 @@ func (co *Compiler) compileQueryWithMetadata(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
child := &c.s[cid]
|
child := &c.s[cid]
|
||||||
|
|
||||||
if child.SkipRender {
|
if child.SkipRender {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
st.Push(child.ID + closeBlock)
|
st.Push(child.ID + closeBlock)
|
||||||
st.Push(child.ID)
|
st.Push(child.ID)
|
||||||
}
|
}
|
||||||
|
@ -195,33 +197,37 @@ func (co *Compiler) compileQueryWithMetadata(
|
||||||
} else {
|
} else {
|
||||||
sel := &c.s[(id - closeBlock)]
|
sel := &c.s[(id - closeBlock)]
|
||||||
|
|
||||||
ti, err := c.schema.GetTable(sel.Name)
|
if sel.Type != qcode.STUnion {
|
||||||
if err != nil {
|
ti, err := c.schema.GetTable(sel.Name)
|
||||||
return c.md, err
|
if err != nil {
|
||||||
}
|
return c.md, err
|
||||||
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, `)`)
|
io.WriteString(c.w, `)`)
|
||||||
aliasWithID(c.w, "__sr", sel.ID)
|
aliasWithID(c.w, "__sr", sel.ID)
|
||||||
|
|
||||||
io.WriteString(c.w, `)`)
|
|
||||||
aliasWithID(c.w, "__sj", sel.ID)
|
|
||||||
|
|
||||||
if !ti.IsSingular {
|
|
||||||
io.WriteString(c.w, `)`)
|
io.WriteString(c.w, `)`)
|
||||||
aliasWithID(c.w, "__sj", sel.ID)
|
aliasWithID(c.w, "__sj", sel.ID)
|
||||||
}
|
|
||||||
|
|
||||||
if sel.ParentID == -1 {
|
if !ti.IsSingular {
|
||||||
if st.Len() != 0 {
|
io.WriteString(c.w, `)`)
|
||||||
io.WriteString(c.w, `, `)
|
aliasWithID(c.w, "__sj", sel.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sel.ParentID == -1 {
|
||||||
|
if st.Len() != 0 {
|
||||||
|
io.WriteString(c.w, `, `)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.renderLateralJoinClose(sel)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
c.renderLateralJoinClose(sel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sel.Args) != 0 {
|
if sel.Type != qcode.STMember {
|
||||||
for _, v := range sel.Args {
|
if len(sel.Args) != 0 {
|
||||||
qcode.FreeNode(v)
|
for _, v := range sel.Args {
|
||||||
|
qcode.FreeNode(v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,6 +365,16 @@ func (c *compilerContext) initSelect(sel *qcode.Select, ti *DBTableInfo, vars Va
|
||||||
c.md.skipped |= (1 << uint(id))
|
c.md.skipped |= (1 << uint(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case RelPolymorphic:
|
||||||
|
if _, ok := colmap[rel.Left.Col]; !ok {
|
||||||
|
cols = append(cols, &qcode.Column{Table: ti.Name, Name: rel.Left.Col, FieldName: rel.Left.Col})
|
||||||
|
colmap[rel.Left.Col] = struct{}{}
|
||||||
|
}
|
||||||
|
if _, ok := colmap[rel.Right.Table]; !ok {
|
||||||
|
cols = append(cols, &qcode.Column{Table: ti.Name, Name: rel.Right.Table, FieldName: rel.Right.Table})
|
||||||
|
colmap[rel.Right.Table] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown relationship %s", rel)
|
return nil, fmt.Errorf("unknown relationship %s", rel)
|
||||||
}
|
}
|
||||||
|
@ -437,15 +453,23 @@ func (c *compilerContext) renderSelect(sel *qcode.Select, ti *DBTableInfo, vars
|
||||||
var rel *DBRel
|
var rel *DBRel
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// Relationships must be between union parents and their parents
|
||||||
if sel.ParentID != -1 {
|
if sel.ParentID != -1 {
|
||||||
parent := c.s[sel.ParentID]
|
if sel.Type == qcode.STMember && sel.UParentID != -1 {
|
||||||
|
cn := c.s[sel.ParentID].Name
|
||||||
|
pn := c.s[sel.UParentID].Name
|
||||||
|
rel, err = c.schema.GetRel(cn, pn)
|
||||||
|
|
||||||
rel, err = c.schema.GetRel(ti.Name, parent.Name)
|
} else {
|
||||||
if err != nil {
|
pn := c.s[sel.ParentID].Name
|
||||||
return err
|
rel, err = c.schema.GetRel(ti.Name, pn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
childCols, err := c.initSelect(sel, ti, vars)
|
childCols, err := c.initSelect(sel, ti, vars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -529,30 +553,27 @@ func (c *compilerContext) renderJoin(sel *qcode.Select, ti *DBTableInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderJoinByName(table, parent string, id int32) error {
|
func (c *compilerContext) renderJoinByName(table, parent string, id int32) error {
|
||||||
rel, err := c.schema.GetRel(table, parent)
|
rel, _ := c.schema.GetRel(table, parent)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// This join is only required for one-to-many relations since
|
// This join is only required for one-to-many relations since
|
||||||
// these make use of join tables that need to be pulled in.
|
// these make use of join tables that need to be pulled in.
|
||||||
if rel.Type != RelOneToManyThrough {
|
if rel == nil || rel.Type != RelOneToManyThrough {
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pt, err := c.schema.GetTable(parent)
|
// pt, err := c.schema.GetTable(parent)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
//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, c.parent.Name, c.parent.ID, rel.Left.Col)
|
//rel.Through, rel.Through, rel.ColT, c.parent.Name, c.parent.ID, rel.Left.Col)
|
||||||
io.WriteString(c.w, ` LEFT OUTER JOIN "`)
|
io.WriteString(c.w, ` LEFT OUTER JOIN "`)
|
||||||
io.WriteString(c.w, rel.Through)
|
io.WriteString(c.w, rel.Through.Table)
|
||||||
io.WriteString(c.w, `" ON ((`)
|
io.WriteString(c.w, `" ON ((`)
|
||||||
colWithTable(c.w, rel.Through, rel.ColT)
|
colWithTable(c.w, rel.Through.Table, rel.Through.ColL)
|
||||||
io.WriteString(c.w, `) = (`)
|
io.WriteString(c.w, `) = (`)
|
||||||
colWithTableID(c.w, pt.Name, id, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
io.WriteString(c.w, `))`)
|
io.WriteString(c.w, `))`)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -639,10 +660,33 @@ func (c *compilerContext) renderJoinColumns(sel *qcode.Select, ti *DBTableInfo,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, `"__sj_`)
|
if childSel.Type == qcode.STUnion {
|
||||||
int32String(c.w, childSel.ID)
|
rel, err := c.schema.GetRel(childSel.Name, ti.Name)
|
||||||
io.WriteString(c.w, `"."json"`)
|
if err != nil {
|
||||||
alias(c.w, childSel.FieldName)
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(c.w, `(CASE `)
|
||||||
|
for _, uid := range childSel.Children {
|
||||||
|
unionSel := &c.s[uid]
|
||||||
|
|
||||||
|
io.WriteString(c.w, `WHEN `)
|
||||||
|
colWithTableID(c.w, ti.Name, sel.ID, rel.Right.Table)
|
||||||
|
io.WriteString(c.w, ` = `)
|
||||||
|
squoted(c.w, unionSel.Name)
|
||||||
|
io.WriteString(c.w, ` THEN `)
|
||||||
|
io.WriteString(c.w, `"__sj_`)
|
||||||
|
int32String(c.w, unionSel.ID)
|
||||||
|
io.WriteString(c.w, `"."json"`)
|
||||||
|
}
|
||||||
|
io.WriteString(c.w, `END)`)
|
||||||
|
alias(c.w, childSel.FieldName)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
io.WriteString(c.w, `"__sj_`)
|
||||||
|
int32String(c.w, childSel.ID)
|
||||||
|
io.WriteString(c.w, `"."json"`)
|
||||||
|
alias(c.w, childSel.FieldName)
|
||||||
|
}
|
||||||
|
|
||||||
if childSel.Paging.Type != qcode.PtOffset {
|
if childSel.Paging.Type != qcode.PtOffset {
|
||||||
io.WriteString(c.w, `, "__sj_`)
|
io.WriteString(c.w, `, "__sj_`)
|
||||||
|
@ -697,7 +741,8 @@ func (c *compilerContext) renderBaseSelect(sel *qcode.Select, ti *DBTableInfo, r
|
||||||
}
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, ` WHERE (`)
|
io.WriteString(c.w, ` WHERE (`)
|
||||||
if err := c.renderRelationship(sel, ti); err != nil {
|
|
||||||
|
if err := c.renderRelationship(sel, rel); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if isFil {
|
if isFil {
|
||||||
|
@ -811,22 +856,25 @@ func (c *compilerContext) renderCursorCTE(sel *qcode.Select) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderRelationship(sel *qcode.Select, ti *DBTableInfo) error {
|
func (c *compilerContext) renderRelationshipByName(table, parent string) error {
|
||||||
parent := c.s[sel.ParentID]
|
|
||||||
|
|
||||||
pti, err := c.schema.GetTable(parent.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.renderRelationshipByName(ti.Name, pti.Name, parent.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compilerContext) renderRelationshipByName(table, parent string, id int32) error {
|
|
||||||
rel, err := c.schema.GetRel(table, parent)
|
rel, err := c.schema.GetRel(table, parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return c.renderRelationship(nil, rel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) renderRelationship(sel *qcode.Select, rel *DBRel) error {
|
||||||
|
var pid int32
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case sel == nil:
|
||||||
|
pid = int32(-1)
|
||||||
|
case sel.Type == qcode.STMember:
|
||||||
|
pid = sel.UParentID
|
||||||
|
default:
|
||||||
|
pid = sel.ParentID
|
||||||
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, `((`)
|
io.WriteString(c.w, `((`)
|
||||||
|
|
||||||
|
@ -838,19 +886,19 @@ func (c *compilerContext) renderRelationshipByName(table, parent string, id int3
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !rel.Left.Array && rel.Right.Array:
|
case !rel.Left.Array && rel.Right.Array:
|
||||||
colWithTable(c.w, table, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
io.WriteString(c.w, `) = any (`)
|
io.WriteString(c.w, `) = any (`)
|
||||||
colWithTableID(c.w, parent, id, rel.Right.Col)
|
colWithTableID(c.w, rel.Right.Table, pid, rel.Right.Col)
|
||||||
|
|
||||||
case rel.Left.Array && !rel.Right.Array:
|
case rel.Left.Array && !rel.Right.Array:
|
||||||
colWithTableID(c.w, parent, id, rel.Right.Col)
|
colWithTableID(c.w, rel.Right.Table, pid, rel.Right.Col)
|
||||||
io.WriteString(c.w, `) = any (`)
|
io.WriteString(c.w, `) = any (`)
|
||||||
colWithTable(c.w, table, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
colWithTable(c.w, table, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
io.WriteString(c.w, `) = (`)
|
io.WriteString(c.w, `) = (`)
|
||||||
colWithTableID(c.w, parent, id, rel.Right.Col)
|
colWithTableID(c.w, rel.Right.Table, pid, rel.Right.Col)
|
||||||
}
|
}
|
||||||
|
|
||||||
case RelOneToManyThrough:
|
case RelOneToManyThrough:
|
||||||
|
@ -860,25 +908,34 @@ func (c *compilerContext) renderRelationshipByName(table, parent string, id int3
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !rel.Left.Array && rel.Right.Array:
|
case !rel.Left.Array && rel.Right.Array:
|
||||||
colWithTable(c.w, table, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
io.WriteString(c.w, `) = any (`)
|
io.WriteString(c.w, `) = any (`)
|
||||||
colWithTable(c.w, rel.Through, rel.Right.Col)
|
colWithTable(c.w, rel.Through.Table, rel.Through.ColR)
|
||||||
|
|
||||||
case rel.Left.Array && !rel.Right.Array:
|
case rel.Left.Array && !rel.Right.Array:
|
||||||
colWithTable(c.w, rel.Through, rel.Right.Col)
|
colWithTable(c.w, rel.Through.Table, rel.Through.ColR)
|
||||||
io.WriteString(c.w, `) = any (`)
|
io.WriteString(c.w, `) = any (`)
|
||||||
colWithTable(c.w, table, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
colWithTable(c.w, table, rel.Left.Col)
|
colWithTable(c.w, rel.Through.Table, rel.Through.ColR)
|
||||||
io.WriteString(c.w, `) = (`)
|
io.WriteString(c.w, `) = (`)
|
||||||
colWithTable(c.w, rel.Through, rel.Right.Col)
|
colWithTable(c.w, rel.Right.Table, rel.Right.Col)
|
||||||
}
|
}
|
||||||
|
|
||||||
case RelEmbedded:
|
case RelEmbedded:
|
||||||
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
||||||
io.WriteString(c.w, `) = (`)
|
io.WriteString(c.w, `) = (`)
|
||||||
colWithTableID(c.w, parent, id, rel.Left.Col)
|
colWithTableID(c.w, rel.Left.Table, pid, rel.Left.Col)
|
||||||
|
|
||||||
|
case RelPolymorphic:
|
||||||
|
colWithTable(c.w, sel.Name, rel.Right.Col)
|
||||||
|
io.WriteString(c.w, `) = (`)
|
||||||
|
colWithTableID(c.w, rel.Left.Table, pid, rel.Left.Col)
|
||||||
|
io.WriteString(c.w, `) AND (`)
|
||||||
|
colWithTableID(c.w, rel.Left.Table, pid, rel.Right.Table)
|
||||||
|
io.WriteString(c.w, `) = (`)
|
||||||
|
squoted(c.w, sel.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, `))`)
|
io.WriteString(c.w, `))`)
|
||||||
|
@ -991,7 +1048,7 @@ func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, ti *DBTableInfo) erro
|
||||||
|
|
||||||
io.WriteString(c.w, ` WHERE `)
|
io.WriteString(c.w, ` WHERE `)
|
||||||
|
|
||||||
if err := c.renderRelationshipByName(cti.Name, ti.Name, -1); err != nil {
|
if err := c.renderRelationshipByName(cti.Name, ti.Name); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -497,7 +497,7 @@ func TestCompileQuery(t *testing.T) {
|
||||||
t.Run("withFragment1", withFragment1)
|
t.Run("withFragment1", withFragment1)
|
||||||
t.Run("withFragment2", withFragment2)
|
t.Run("withFragment2", withFragment2)
|
||||||
t.Run("withFragment3", withFragment3)
|
t.Run("withFragment3", withFragment3)
|
||||||
t.Run("withInlineFragment", withInlineFragment)
|
//t.Run("withInlineFragment", withInlineFragment)
|
||||||
t.Run("jsonColumnAsTable", jsonColumnAsTable)
|
t.Run("jsonColumnAsTable", jsonColumnAsTable)
|
||||||
t.Run("withCursor", withCursor)
|
t.Run("withCursor", withCursor)
|
||||||
t.Run("nullForAuthRequiredInAnon", nullForAuthRequiredInAnon)
|
t.Run("nullForAuthRequiredInAnon", nullForAuthRequiredInAnon)
|
||||||
|
|
|
@ -11,6 +11,7 @@ type DBSchema struct {
|
||||||
ver int
|
ver int
|
||||||
t map[string]*DBTableInfo
|
t map[string]*DBTableInfo
|
||||||
rm map[string]map[string]*DBRel
|
rm map[string]map[string]*DBRel
|
||||||
|
vt map[string]*VirtualTable
|
||||||
fm map[string]*DBFunction
|
fm map[string]*DBFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,15 +34,19 @@ const (
|
||||||
RelOneToOne RelType = iota + 1
|
RelOneToOne RelType = iota + 1
|
||||||
RelOneToMany
|
RelOneToMany
|
||||||
RelOneToManyThrough
|
RelOneToManyThrough
|
||||||
|
RelPolymorphic
|
||||||
RelEmbedded
|
RelEmbedded
|
||||||
RelRemote
|
RelRemote
|
||||||
)
|
)
|
||||||
|
|
||||||
type DBRel struct {
|
type DBRel struct {
|
||||||
Type RelType
|
Type RelType
|
||||||
Through string
|
Through struct {
|
||||||
ColT string
|
Table string
|
||||||
Left struct {
|
ColL string
|
||||||
|
ColR string
|
||||||
|
}
|
||||||
|
Left struct {
|
||||||
col *DBColumn
|
col *DBColumn
|
||||||
Table string
|
Table string
|
||||||
Col string
|
Col string
|
||||||
|
@ -60,6 +65,7 @@ func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
||||||
ver: info.Version,
|
ver: info.Version,
|
||||||
t: make(map[string]*DBTableInfo),
|
t: make(map[string]*DBTableInfo),
|
||||||
rm: make(map[string]map[string]*DBRel),
|
rm: make(map[string]map[string]*DBRel),
|
||||||
|
vt: make(map[string]*VirtualTable),
|
||||||
fm: make(map[string]*DBFunction, len(info.Functions)),
|
fm: make(map[string]*DBFunction, len(info.Functions)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +76,10 @@ func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := schema.virtualRels(info.VTables); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for i, t := range info.Tables {
|
for i, t := range info.Tables {
|
||||||
err := schema.firstDegreeRels(t, info.Columns[i])
|
err := schema.firstDegreeRels(t, info.Columns[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -102,7 +112,7 @@ func (s *DBSchema) addTable(
|
||||||
singular := flect.Singularize(t.Key)
|
singular := flect.Singularize(t.Key)
|
||||||
plural := flect.Pluralize(t.Key)
|
plural := flect.Pluralize(t.Key)
|
||||||
|
|
||||||
s.t[singular] = &DBTableInfo{
|
ts := &DBTableInfo{
|
||||||
Name: t.Name,
|
Name: t.Name,
|
||||||
Type: t.Type,
|
Type: t.Type,
|
||||||
IsSingular: true,
|
IsSingular: true,
|
||||||
|
@ -112,8 +122,9 @@ func (s *DBSchema) addTable(
|
||||||
Singular: singular,
|
Singular: singular,
|
||||||
Plural: plural,
|
Plural: plural,
|
||||||
}
|
}
|
||||||
|
s.t[singular] = ts
|
||||||
|
|
||||||
s.t[plural] = &DBTableInfo{
|
tp := &DBTableInfo{
|
||||||
Name: t.Name,
|
Name: t.Name,
|
||||||
Type: t.Type,
|
Type: t.Type,
|
||||||
IsSingular: false,
|
IsSingular: false,
|
||||||
|
@ -123,14 +134,15 @@ func (s *DBSchema) addTable(
|
||||||
Singular: singular,
|
Singular: singular,
|
||||||
Plural: plural,
|
Plural: plural,
|
||||||
}
|
}
|
||||||
|
s.t[plural] = tp
|
||||||
|
|
||||||
if al, ok := aliases[t.Key]; ok {
|
if al, ok := aliases[t.Key]; ok {
|
||||||
for i := range al {
|
for i := range al {
|
||||||
k1 := flect.Singularize(al[i])
|
k1 := flect.Singularize(al[i])
|
||||||
s.t[k1] = s.t[singular]
|
s.t[k1] = ts
|
||||||
|
|
||||||
k2 := flect.Pluralize(al[i])
|
k2 := flect.Pluralize(al[i])
|
||||||
s.t[k2] = s.t[plural]
|
s.t[k2] = tp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,6 +166,54 @@ func (s *DBSchema) addTable(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DBSchema) virtualRels(vts []VirtualTable) error {
|
||||||
|
for _, vt := range vts {
|
||||||
|
s.vt[vt.Name] = &vt
|
||||||
|
|
||||||
|
for _, t := range s.t {
|
||||||
|
idCol, ok := t.ColMap[vt.IDColumn]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok = t.ColMap[vt.TypeColumn]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
nt := DBTable{
|
||||||
|
ID: -1,
|
||||||
|
Name: vt.Name,
|
||||||
|
Key: strings.ToLower(vt.Name),
|
||||||
|
Type: "virtual",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.addTable(nt, nil, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rel := &DBRel{Type: RelPolymorphic}
|
||||||
|
rel.Left.col = idCol
|
||||||
|
rel.Left.Table = t.Name
|
||||||
|
rel.Left.Col = idCol.Name
|
||||||
|
|
||||||
|
rcol := DBColumn{
|
||||||
|
Name: vt.FKeyColumn,
|
||||||
|
Key: strings.ToLower(vt.FKeyColumn),
|
||||||
|
Type: idCol.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
rel.Right.col = &rcol
|
||||||
|
rel.Right.Table = vt.TypeColumn
|
||||||
|
rel.Right.Col = rcol.Name
|
||||||
|
|
||||||
|
if err := s.SetRel(vt.Name, t.Name, rel); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DBSchema) firstDegreeRels(t DBTable, cols []DBColumn) error {
|
func (s *DBSchema) firstDegreeRels(t DBTable, cols []DBColumn) error {
|
||||||
ct := t.Key
|
ct := t.Key
|
||||||
cti, ok := s.t[ct]
|
cti, ok := s.t[ct]
|
||||||
|
@ -164,7 +224,7 @@ func (s *DBSchema) firstDegreeRels(t DBTable, cols []DBColumn) error {
|
||||||
for i := range cols {
|
for i := range cols {
|
||||||
c := cols[i]
|
c := cols[i]
|
||||||
|
|
||||||
if len(c.FKeyTable) == 0 {
|
if c.FKeyTable == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,16 +404,17 @@ func (s *DBSchema) updateSchemaOTMT(
|
||||||
// One-to-many-through relation between 1nd foreign key table and the
|
// One-to-many-through relation between 1nd foreign key table and the
|
||||||
// 2nd foreign key table
|
// 2nd foreign key table
|
||||||
rel1 := &DBRel{Type: RelOneToManyThrough}
|
rel1 := &DBRel{Type: RelOneToManyThrough}
|
||||||
rel1.Through = ti.Name
|
rel1.Through.Table = ti.Name
|
||||||
rel1.ColT = col2.Name
|
rel1.Through.ColL = col1.Name
|
||||||
|
rel1.Through.ColR = col2.Name
|
||||||
|
|
||||||
rel1.Left.col = &col2
|
rel1.Left.col = fc1
|
||||||
rel1.Left.Table = col2.FKeyTable
|
rel1.Left.Table = col1.FKeyTable
|
||||||
rel1.Left.Col = fc2.Name
|
rel1.Left.Col = fc1.Name
|
||||||
|
|
||||||
rel1.Right.col = &col1
|
rel1.Right.col = fc2
|
||||||
rel1.Right.Table = ti.Name
|
rel1.Right.Table = t2
|
||||||
rel1.Right.Col = col1.Name
|
rel1.Right.Col = fc2.Name
|
||||||
|
|
||||||
if err := s.SetRel(t1, t2, rel1); err != nil {
|
if err := s.SetRel(t1, t2, rel1); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -362,16 +423,17 @@ func (s *DBSchema) updateSchemaOTMT(
|
||||||
// One-to-many-through relation between 2nd foreign key table and the
|
// One-to-many-through relation between 2nd foreign key table and the
|
||||||
// 1nd foreign key table
|
// 1nd foreign key table
|
||||||
rel2 := &DBRel{Type: RelOneToManyThrough}
|
rel2 := &DBRel{Type: RelOneToManyThrough}
|
||||||
rel2.Through = ti.Name
|
rel2.Through.Table = ti.Name
|
||||||
rel2.ColT = col1.Name
|
rel2.Through.ColL = col2.Name
|
||||||
|
rel2.Through.ColR = col1.Name
|
||||||
|
|
||||||
rel1.Left.col = fc1
|
rel2.Left.col = fc2
|
||||||
rel2.Left.Table = col1.FKeyTable
|
rel2.Left.Table = col2.FKeyTable
|
||||||
rel2.Left.Col = fc1.Name
|
rel2.Left.Col = fc2.Name
|
||||||
|
|
||||||
rel1.Right.col = &col2
|
rel2.Right.col = fc1
|
||||||
rel2.Right.Table = ti.Name
|
rel2.Right.Table = t1
|
||||||
rel2.Right.Col = col2.Name
|
rel2.Right.Col = fc1.Name
|
||||||
|
|
||||||
if err := s.SetRel(t2, t1, rel2); err != nil {
|
if err := s.SetRel(t2, t1, rel2); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -14,14 +14,18 @@ func (rt RelType) String() string {
|
||||||
return "remote"
|
return "remote"
|
||||||
case RelEmbedded:
|
case RelEmbedded:
|
||||||
return "embedded"
|
return "embedded"
|
||||||
|
case RelPolymorphic:
|
||||||
|
return "polymorphic"
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (re *DBRel) String() string {
|
func (re *DBRel) String() string {
|
||||||
if re.Type == RelOneToManyThrough {
|
if re.Type == RelOneToManyThrough {
|
||||||
return fmt.Sprintf("'%s.%s' --(Through: %s)--> '%s.%s'",
|
return fmt.Sprintf("'%s.%s' --(%s.%s, %s.%s)--> '%s.%s'",
|
||||||
re.Left.Table, re.Left.Col, re.Through, re.Right.Table, re.Right.Col)
|
re.Left.Table, re.Left.Col,
|
||||||
|
re.Through.Table, re.Through.ColL, re.Through.Table, re.Through.ColR,
|
||||||
|
re.Right.Table, re.Right.Col)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("'%s.%s' --(%s)--> '%s.%s'",
|
return fmt.Sprintf("'%s.%s' --(%s)--> '%s.%s'",
|
||||||
re.Left.Table, re.Left.Col, re.Type, re.Right.Table, re.Right.Col)
|
re.Left.Table, re.Left.Col, re.Type, re.Right.Table, re.Right.Col)
|
||||||
|
|
|
@ -14,9 +14,17 @@ type DBInfo struct {
|
||||||
Tables []DBTable
|
Tables []DBTable
|
||||||
Columns [][]DBColumn
|
Columns [][]DBColumn
|
||||||
Functions []DBFunction
|
Functions []DBFunction
|
||||||
|
VTables []VirtualTable
|
||||||
colMap map[string]map[string]*DBColumn
|
colMap map[string]map[string]*DBColumn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type VirtualTable struct {
|
||||||
|
Name string
|
||||||
|
IDColumn string
|
||||||
|
TypeColumn string
|
||||||
|
FKeyColumn string
|
||||||
|
}
|
||||||
|
|
||||||
func GetDBInfo(db *sql.DB, schema string) (*DBInfo, error) {
|
func GetDBInfo(db *sql.DB, schema string) (*DBInfo, error) {
|
||||||
di := &DBInfo{}
|
di := &DBInfo{}
|
||||||
var version string
|
var version string
|
||||||
|
|
|
@ -67,9 +67,9 @@ SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT
|
||||||
=== RUN TestCompileQuery/oneToManyArray
|
=== RUN TestCompileQuery/oneToManyArray
|
||||||
SELECT jsonb_build_object('tags', "__sj_0"."json", 'product', "__sj_2"."json") as "__root" FROM (SELECT to_jsonb("__sr_2".*) AS "json"FROM (SELECT "products_2"."name" AS "name", "products_2"."price" AS "price", "__sj_3"."json" AS "tags" FROM (SELECT "products"."name", "products"."price", "products"."tags" FROM "products" LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_3"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_3".*) AS "json"FROM (SELECT "tags_3"."id" AS "id", "tags_3"."name" AS "name" FROM (SELECT "tags"."id", "tags"."name" FROM "tags" WHERE ((("tags"."slug") = any ("products_2"."tags"))) LIMIT ('20') :: integer) AS "tags_3") AS "__sr_3") AS "__sj_3") AS "__sj_3" ON ('true')) AS "__sr_2") AS "__sj_2", (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "tags_0"."name" AS "name", "__sj_1"."json" AS "product" FROM (SELECT "tags"."name", "tags"."slug" FROM "tags" LIMIT ('20') :: integer) AS "tags_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "products_1"."name" AS "name" FROM (SELECT "products"."name" FROM "products" WHERE ((("tags_0"."slug") = any ("products"."tags"))) LIMIT ('1') :: integer) AS "products_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('tags', "__sj_0"."json", 'product', "__sj_2"."json") as "__root" FROM (SELECT to_jsonb("__sr_2".*) AS "json"FROM (SELECT "products_2"."name" AS "name", "products_2"."price" AS "price", "__sj_3"."json" AS "tags" FROM (SELECT "products"."name", "products"."price", "products"."tags" FROM "products" LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_3"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_3".*) AS "json"FROM (SELECT "tags_3"."id" AS "id", "tags_3"."name" AS "name" FROM (SELECT "tags"."id", "tags"."name" FROM "tags" WHERE ((("tags"."slug") = any ("products_2"."tags"))) LIMIT ('20') :: integer) AS "tags_3") AS "__sr_3") AS "__sj_3") AS "__sj_3" ON ('true')) AS "__sr_2") AS "__sj_2", (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "tags_0"."name" AS "name", "__sj_1"."json" AS "product" FROM (SELECT "tags"."name", "tags"."slug" FROM "tags" LIMIT ('20') :: integer) AS "tags_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "products_1"."name" AS "name" FROM (SELECT "products"."name" FROM "products" WHERE ((("tags_0"."slug") = any ("products"."tags"))) LIMIT ('1') :: integer) AS "products_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/manyToMany
|
=== RUN TestCompileQuery/manyToMany
|
||||||
SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."name" AS "name", "__sj_1"."json" AS "customers" FROM (SELECT "products"."name", "products"."id" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_1"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "customers_1"."email" AS "email", "customers_1"."full_name" AS "full_name" FROM (SELECT "customers"."email", "customers"."full_name" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_0"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('20') :: integer) AS "customers_1") AS "__sr_1") AS "__sj_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."name" AS "name", "__sj_1"."json" AS "customers" FROM (SELECT "products"."name", "products"."id" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_1"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "customers_1"."email" AS "email", "customers_1"."full_name" AS "full_name" FROM (SELECT "customers"."email", "customers"."full_name" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."customer_id") = ("customers"."id")) WHERE ((("purchases"."product_id") = ("products"."id"))) LIMIT ('20') :: integer) AS "customers_1") AS "__sr_1") AS "__sj_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/manyToManyReverse
|
=== RUN TestCompileQuery/manyToManyReverse
|
||||||
SELECT jsonb_build_object('customers', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "customers_0"."email" AS "email", "customers_0"."full_name" AS "full_name", "__sj_1"."json" AS "products" FROM (SELECT "customers"."email", "customers"."full_name", "customers"."id" FROM "customers" LIMIT ('20') :: integer) AS "customers_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_1"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "products_1"."name" AS "name" FROM (SELECT "products"."name" FROM "products" LEFT OUTER JOIN "purchases" ON (("purchases"."customer_id") = ("customers_0"."id")) WHERE ((("products"."id") = ("purchases"."product_id")) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_1") AS "__sr_1") AS "__sj_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('customers', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "customers_0"."email" AS "email", "customers_0"."full_name" AS "full_name", "__sj_1"."json" AS "products" FROM (SELECT "customers"."email", "customers"."full_name", "customers"."id" FROM "customers" LIMIT ('20') :: integer) AS "customers_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_1"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "products_1"."name" AS "name" FROM (SELECT "products"."name" FROM "products" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products"."id")) WHERE ((("purchases"."customer_id") = ("customers"."id")) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_1") AS "__sr_1") AS "__sj_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/aggFunction
|
=== RUN TestCompileQuery/aggFunction
|
||||||
SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."name" AS "name", "products_0"."count_price" AS "count_price" FROM (SELECT "products"."name", count("products"."price") AS "count_price" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."name" AS "name", "products_0"."count_price" AS "count_price" FROM (SELECT "products"."name", count("products"."price") AS "count_price" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/aggFunctionBlockedByCol
|
=== RUN TestCompileQuery/aggFunctionBlockedByCol
|
||||||
|
@ -85,15 +85,13 @@ SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT t
|
||||||
=== RUN TestCompileQuery/withWhereOnRelations
|
=== RUN TestCompileQuery/withWhereOnRelations
|
||||||
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT "users"."id", "users"."email" FROM "users" WHERE (NOT EXISTS (SELECT 1 FROM products WHERE (("products"."user_id") = ("users"."id")) AND ((("products"."price") > '3' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT "users"."id", "users"."email" FROM "users" WHERE (NOT EXISTS (SELECT 1 FROM products WHERE (("products"."user_id") = ("users"."id")) AND ((("products"."price") > '3' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/multiRoot
|
=== RUN TestCompileQuery/multiRoot
|
||||||
SELECT jsonb_build_object('customer', "__sj_0"."json", 'user', "__sj_1"."json", 'product', "__sj_2"."json") as "__root" FROM (SELECT to_jsonb("__sr_2".*) AS "json"FROM (SELECT "products_2"."id" AS "id", "products_2"."name" AS "name", "__sj_3"."json" AS "customers", "__sj_4"."json" AS "customer" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_4".*) AS "json"FROM (SELECT "customers_4"."email" AS "email" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_2"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('1') :: integer) AS "customers_4") AS "__sr_4") AS "__sj_4" ON ('true') LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_3"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_3".*) AS "json"FROM (SELECT "customers_3"."email" AS "email" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_2"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('20') :: integer) AS "customers_3") AS "__sr_3") AS "__sj_3") AS "__sj_3" ON ('true')) AS "__sr_2") AS "__sj_2", (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "users_1"."id" AS "id", "users_1"."email" AS "email" FROM (SELECT "users"."id", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_1") AS "__sr_1") AS "__sj_1", (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "customers_0"."id" AS "id" FROM (SELECT "customers"."id" FROM "customers" LIMIT ('1') :: integer) AS "customers_0") AS "__sr_0") AS "__sj_0"
|
SELECT jsonb_build_object('customer', "__sj_0"."json", 'user', "__sj_1"."json", 'product', "__sj_2"."json") as "__root" FROM (SELECT to_jsonb("__sr_2".*) AS "json"FROM (SELECT "products_2"."id" AS "id", "products_2"."name" AS "name", "__sj_3"."json" AS "customers", "__sj_4"."json" AS "customer" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_4".*) AS "json"FROM (SELECT "customers_4"."email" AS "email" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."customer_id") = ("customers"."id")) WHERE ((("purchases"."product_id") = ("products"."id"))) LIMIT ('1') :: integer) AS "customers_4") AS "__sr_4") AS "__sj_4" ON ('true') LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_3"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_3".*) AS "json"FROM (SELECT "customers_3"."email" AS "email" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."customer_id") = ("customers"."id")) WHERE ((("purchases"."product_id") = ("products"."id"))) LIMIT ('20') :: integer) AS "customers_3") AS "__sr_3") AS "__sj_3") AS "__sj_3" ON ('true')) AS "__sr_2") AS "__sj_2", (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "users_1"."id" AS "id", "users_1"."email" AS "email" FROM (SELECT "users"."id", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_1") AS "__sr_1") AS "__sj_1", (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "customers_0"."id" AS "id" FROM (SELECT "customers"."id" FROM "customers" LIMIT ('1') :: integer) AS "customers_0") AS "__sr_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/withFragment1
|
=== RUN TestCompileQuery/withFragment1
|
||||||
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name", "users_0"."created_at" AS "created_at", "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT , "users"."created_at", "users"."id", "users"."email" FROM "users" GROUP BY "users"."created_at", "users"."id", "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name", "users_0"."created_at" AS "created_at", "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT , "users"."created_at", "users"."id", "users"."email" FROM "users" GROUP BY "users"."created_at", "users"."id", "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/withFragment2
|
=== RUN TestCompileQuery/withFragment2
|
||||||
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name", "users_0"."created_at" AS "created_at", "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT , "users"."created_at", "users"."id", "users"."email" FROM "users" GROUP BY "users"."created_at", "users"."id", "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name", "users_0"."created_at" AS "created_at", "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT , "users"."created_at", "users"."id", "users"."email" FROM "users" GROUP BY "users"."created_at", "users"."id", "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/withFragment3
|
=== RUN TestCompileQuery/withFragment3
|
||||||
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name", "users_0"."created_at" AS "created_at", "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT , "users"."created_at", "users"."id", "users"."email" FROM "users" GROUP BY "users"."created_at", "users"."id", "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name", "users_0"."created_at" AS "created_at", "users_0"."id" AS "id", "users_0"."email" AS "email" FROM (SELECT , "users"."created_at", "users"."id", "users"."email" FROM "users" GROUP BY "users"."created_at", "users"."id", "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/withInlineFragment
|
|
||||||
SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."id" AS "id", "users_0"."email" AS "email", "users_0"."created_at" AS "created_at", "users_0"."first_name" AS "first_name", "users_0"."last_name" AS "last_name" FROM (SELECT "users"."id", "users"."email", "users"."created_at" FROM "users" GROUP BY "users"."id", "users"."email", "users"."created_at" LIMIT ('20') :: integer) AS "users_0") AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
|
||||||
=== RUN TestCompileQuery/jsonColumnAsTable
|
=== RUN TestCompileQuery/jsonColumnAsTable
|
||||||
SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "__sj_1"."json" AS "tag_count" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "tag_count_1"."count" AS "count", "__sj_2"."json" AS "tags" FROM (SELECT "tag_count"."count", "tag_count"."tag_id" FROM "products", json_to_recordset("products"."tag_count") AS "tag_count"(tag_id bigint, count int) WHERE ((("products"."id") = ("products_0"."id"))) LIMIT ('1') :: integer) AS "tag_count_1" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_2"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_2".*) AS "json"FROM (SELECT "tags_2"."name" AS "name" FROM (SELECT "tags"."name" FROM "tags" WHERE ((("tags"."id") = ("tag_count_1"."tag_id"))) LIMIT ('20') :: integer) AS "tags_2") AS "__sr_2") AS "__sj_2") AS "__sj_2" ON ('true')) AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
SELECT jsonb_build_object('products', "__sj_0"."json") as "__root" FROM (SELECT coalesce(jsonb_agg("__sj_0"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "__sj_1"."json" AS "tag_count" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "tag_count_1"."count" AS "count", "__sj_2"."json" AS "tags" FROM (SELECT "tag_count"."count", "tag_count"."tag_id" FROM "products", json_to_recordset("products"."tag_count") AS "tag_count"(tag_id bigint, count int) WHERE ((("products"."id") = ("products_0"."id"))) LIMIT ('1') :: integer) AS "tag_count_1" LEFT OUTER JOIN LATERAL (SELECT coalesce(jsonb_agg("__sj_2"."json"), '[]') as "json" FROM (SELECT to_jsonb("__sr_2".*) AS "json"FROM (SELECT "tags_2"."name" AS "name" FROM (SELECT "tags"."name" FROM "tags" WHERE ((("tags"."id") = ("tag_count_1"."tag_id"))) LIMIT ('20') :: integer) AS "tags_2") AS "__sr_2") AS "__sj_2") AS "__sj_2" ON ('true')) AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0") AS "__sj_0"
|
||||||
=== RUN TestCompileQuery/withCursor
|
=== RUN TestCompileQuery/withCursor
|
||||||
|
@ -128,7 +126,6 @@ SELECT jsonb_build_object('users', "__sj_0"."json") as "__root" FROM (SELECT coa
|
||||||
--- PASS: TestCompileQuery/withFragment1 (0.00s)
|
--- PASS: TestCompileQuery/withFragment1 (0.00s)
|
||||||
--- PASS: TestCompileQuery/withFragment2 (0.00s)
|
--- PASS: TestCompileQuery/withFragment2 (0.00s)
|
||||||
--- PASS: TestCompileQuery/withFragment3 (0.00s)
|
--- PASS: TestCompileQuery/withFragment3 (0.00s)
|
||||||
--- PASS: TestCompileQuery/withInlineFragment (0.00s)
|
|
||||||
--- PASS: TestCompileQuery/jsonColumnAsTable (0.00s)
|
--- PASS: TestCompileQuery/jsonColumnAsTable (0.00s)
|
||||||
--- PASS: TestCompileQuery/withCursor (0.00s)
|
--- PASS: TestCompileQuery/withCursor (0.00s)
|
||||||
--- PASS: TestCompileQuery/nullForAuthRequiredInAnon (0.00s)
|
--- PASS: TestCompileQuery/nullForAuthRequiredInAnon (0.00s)
|
||||||
|
@ -149,8 +146,8 @@ WITH "_sg_input" AS (SELECT $1 :: json AS j), "products" AS (UPDATE "products" S
|
||||||
=== RUN TestCompileUpdate/nestedUpdateOneToManyWithConnect
|
=== RUN TestCompileUpdate/nestedUpdateOneToManyWithConnect
|
||||||
WITH "_sg_input" AS (SELECT $1 :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT CAST( i.j ->>'full_name' AS character varying), CAST( i.j ->>'email' AS character varying), CAST( i.j ->>'created_at' AS timestamp without time zone), CAST( i.j ->>'updated_at' AS timestamp without time zone) FROM "_sg_input" i) WHERE (("users"."id") = $2 :: bigint) RETURNING "users".*), "products_c" AS ( UPDATE "products" SET "user_id" = "users"."id" FROM "users" WHERE ("products"."id"= ((i.j->'product'->'connect'->>'id'))::bigint) RETURNING "products".*), "products_d" AS ( UPDATE "products" SET "user_id" = NULL FROM "users" WHERE ("products"."id"= ((i.j->'product'->'disconnect'->>'id'))::bigint) RETURNING "products".*), "products" AS (SELECT * FROM "products_c" UNION ALL SELECT * FROM "products_d") SELECT jsonb_build_object('user', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "__sj_1"."json" AS "product" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0"
|
WITH "_sg_input" AS (SELECT $1 :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT CAST( i.j ->>'full_name' AS character varying), CAST( i.j ->>'email' AS character varying), CAST( i.j ->>'created_at' AS timestamp without time zone), CAST( i.j ->>'updated_at' AS timestamp without time zone) FROM "_sg_input" i) WHERE (("users"."id") = $2 :: bigint) RETURNING "users".*), "products_c" AS ( UPDATE "products" SET "user_id" = "users"."id" FROM "users" WHERE ("products"."id"= ((i.j->'product'->'connect'->>'id'))::bigint) RETURNING "products".*), "products_d" AS ( UPDATE "products" SET "user_id" = NULL FROM "users" WHERE ("products"."id"= ((i.j->'product'->'disconnect'->>'id'))::bigint) RETURNING "products".*), "products" AS (SELECT * FROM "products_c" UNION ALL SELECT * FROM "products_d") SELECT jsonb_build_object('user', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "__sj_1"."json" AS "product" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0"
|
||||||
=== RUN TestCompileUpdate/nestedUpdateOneToOneWithConnect
|
=== RUN TestCompileUpdate/nestedUpdateOneToOneWithConnect
|
||||||
WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint AND "users"."email"= ((i.j->'user'->'connect'->>'email'))::character varying LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT CAST( i.j ->>'name' AS character varying), CAST( i.j ->>'price' AS numeric(7,2)), "_x_users"."id" FROM "_sg_input" i, "_x_users") WHERE (("products"."id") = $2 :: bigint) RETURNING "products".*) SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "__sj_1"."json" AS "user" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0"
|
|
||||||
WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."email"= ((i.j->'user'->'connect'->>'email'))::character varying AND "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT CAST( i.j ->>'name' AS character varying), CAST( i.j ->>'price' AS numeric(7,2)), "_x_users"."id" FROM "_sg_input" i, "_x_users") WHERE (("products"."id") = $2 :: bigint) RETURNING "products".*) SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "__sj_1"."json" AS "user" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0"
|
WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."email"= ((i.j->'user'->'connect'->>'email'))::character varying AND "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT CAST( i.j ->>'name' AS character varying), CAST( i.j ->>'price' AS numeric(7,2)), "_x_users"."id" FROM "_sg_input" i, "_x_users") WHERE (("products"."id") = $2 :: bigint) RETURNING "products".*) SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "__sj_1"."json" AS "user" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0"
|
||||||
|
WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint AND "users"."email"= ((i.j->'user'->'connect'->>'email'))::character varying LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT CAST( i.j ->>'name' AS character varying), CAST( i.j ->>'price' AS numeric(7,2)), "_x_users"."id" FROM "_sg_input" i, "_x_users") WHERE (("products"."id") = $2 :: bigint) RETURNING "products".*) SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "__sj_1"."json" AS "user" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT to_jsonb("__sr_1".*) AS "json"FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1") AS "__sr_1") AS "__sj_1" ON ('true')) AS "__sr_0") AS "__sj_0"
|
||||||
=== RUN TestCompileUpdate/nestedUpdateOneToOneWithDisconnect
|
=== RUN TestCompileUpdate/nestedUpdateOneToOneWithDisconnect
|
||||||
WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT * FROM (VALUES(NULL::bigint)) AS LOOKUP("id")), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT CAST( i.j ->>'name' AS character varying), CAST( i.j ->>'price' AS numeric(7,2)), "_x_users"."id" FROM "_sg_input" i, "_x_users") WHERE (("products"."id") = $2 :: bigint) RETURNING "products".*) SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "products_0"."user_id" AS "user_id" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sr_0") AS "__sj_0"
|
WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT * FROM (VALUES(NULL::bigint)) AS LOOKUP("id")), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT CAST( i.j ->>'name' AS character varying), CAST( i.j ->>'price' AS numeric(7,2)), "_x_users"."id" FROM "_sg_input" i, "_x_users") WHERE (("products"."id") = $2 :: bigint) RETURNING "products".*) SELECT jsonb_build_object('product', "__sj_0"."json") as "__root" FROM (SELECT to_jsonb("__sr_0".*) AS "json"FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "products_0"."user_id" AS "user_id" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sr_0") AS "__sj_0"
|
||||||
--- PASS: TestCompileUpdate (0.02s)
|
--- PASS: TestCompileUpdate (0.02s)
|
||||||
|
@ -163,4 +160,4 @@ WITH "_sg_input" AS (SELECT $1 :: json AS j), "_x_users" AS (SELECT * FROM (VALU
|
||||||
--- PASS: TestCompileUpdate/nestedUpdateOneToOneWithConnect (0.00s)
|
--- PASS: TestCompileUpdate/nestedUpdateOneToOneWithConnect (0.00s)
|
||||||
--- PASS: TestCompileUpdate/nestedUpdateOneToOneWithDisconnect (0.00s)
|
--- PASS: TestCompileUpdate/nestedUpdateOneToOneWithDisconnect (0.00s)
|
||||||
PASS
|
PASS
|
||||||
ok github.com/dosco/super-graph/core/internal/psql 0.371s
|
ok github.com/dosco/super-graph/core/internal/psql 0.323s
|
||||||
|
|
|
@ -71,6 +71,7 @@ type Field struct {
|
||||||
argsA [5]Arg
|
argsA [5]Arg
|
||||||
Children []int32
|
Children []int32
|
||||||
childrenA [5]int32
|
childrenA [5]int32
|
||||||
|
Union bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Arg struct {
|
type Arg struct {
|
||||||
|
@ -155,7 +156,7 @@ func Parse(gql []byte) (*Operation, error) {
|
||||||
|
|
||||||
if p.peek(itemFragment) {
|
if p.peek(itemFragment) {
|
||||||
p.ignore()
|
p.ignore()
|
||||||
if f, err := p.parseFragment(false); err != nil {
|
if f, err := p.parseFragment(); err != nil {
|
||||||
fragPool.Put(f)
|
fragPool.Put(f)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -181,7 +182,7 @@ func Parse(gql []byte) (*Operation, error) {
|
||||||
return op, nil
|
return op, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseFragment(inline bool) (*Fragment, error) {
|
func (p *Parser) parseFragment() (*Fragment, error) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
frag := fragPool.Get().(*Fragment)
|
frag := fragPool.Get().(*Fragment)
|
||||||
|
@ -190,7 +191,7 @@ func (p *Parser) parseFragment(inline bool) (*Fragment, error) {
|
||||||
|
|
||||||
if p.peek(itemName) {
|
if p.peek(itemName) {
|
||||||
frag.Name = p.val(p.next())
|
frag.Name = p.val(p.next())
|
||||||
} else if !inline {
|
} else {
|
||||||
return frag, errors.New("fragment: missing name")
|
return frag, errors.New("fragment: missing name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,18 +218,16 @@ func (p *Parser) parseFragment(inline bool) (*Fragment, error) {
|
||||||
return frag, fmt.Errorf("fragment: %v", err)
|
return frag, fmt.Errorf("fragment: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !inline {
|
if p.frags == nil {
|
||||||
if p.frags == nil {
|
p.frags = make(map[uint64]*Fragment)
|
||||||
p.frags = make(map[uint64]*Fragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = p.h.WriteString(frag.Name)
|
|
||||||
k := p.h.Sum64()
|
|
||||||
p.h.Reset()
|
|
||||||
|
|
||||||
p.frags[k] = frag
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, _ = p.h.WriteString(frag.Name)
|
||||||
|
k := p.h.Sum64()
|
||||||
|
p.h.Reset()
|
||||||
|
|
||||||
|
p.frags[k] = frag
|
||||||
|
|
||||||
return frag, nil
|
return frag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,18 +404,27 @@ func (p *Parser) parseNormalFields(st *Stack, fields []Field) ([]Field, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseFragmentFields(st *Stack, fields []Field) ([]Field, error) {
|
func (p *Parser) parseFragmentFields(st *Stack, fields []Field) ([]Field, error) {
|
||||||
var fr *Fragment
|
|
||||||
var err error
|
var err error
|
||||||
|
pid := st.Peek()
|
||||||
|
|
||||||
if p.peek(itemOn) {
|
if p.peek(itemOn) {
|
||||||
if fr, err = p.parseFragment(true); err != nil {
|
p.ignore()
|
||||||
|
fields[pid].Union = true
|
||||||
|
|
||||||
|
if fields, err = p.parseNormalFields(st, fields); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer fragPool.Put(fr)
|
|
||||||
|
// If parent is a union selector than copy over args from the parent
|
||||||
|
// to the first child which is the root selector for each union type.
|
||||||
|
for i := pid + 1; i < int32(len(fields)); i++ {
|
||||||
|
f := &fields[i]
|
||||||
|
if f.ParentID == pid {
|
||||||
|
f.Args = fields[pid].Args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if !p.peek(itemName) {
|
if !p.peek(itemName) {
|
||||||
return nil, fmt.Errorf("expecting a fragment name, got: %s", p.next())
|
return nil, fmt.Errorf("expecting a fragment name, got: %s", p.next())
|
||||||
}
|
}
|
||||||
|
@ -426,41 +434,47 @@ func (p *Parser) parseFragmentFields(st *Stack, fields []Field) ([]Field, error)
|
||||||
id := p.h.Sum64()
|
id := p.h.Sum64()
|
||||||
p.h.Reset()
|
p.h.Reset()
|
||||||
|
|
||||||
if fr, ok = p.frags[id]; !ok {
|
fr, ok := p.frags[id]
|
||||||
|
if !ok {
|
||||||
return nil, fmt.Errorf("no fragment named '%s' defined", name)
|
return nil, fmt.Errorf("no fragment named '%s' defined", name)
|
||||||
}
|
}
|
||||||
}
|
ff := fr.Fields
|
||||||
|
|
||||||
n := int32(len(fields))
|
parent := &fields[pid]
|
||||||
fields = append(fields, fr.Fields...)
|
|
||||||
|
|
||||||
for i := 0; i < len(fr.Fields); i++ {
|
n := int32(len(fields))
|
||||||
k := (n + int32(i))
|
fields = append(fields, ff...)
|
||||||
f := &fields[k]
|
|
||||||
f.ID = int32(k)
|
|
||||||
|
|
||||||
// If this is the top-level point the parent to the parent of the
|
for i := 0; i < len(ff); i++ {
|
||||||
// previous field.
|
k := (n + int32(i))
|
||||||
if f.ParentID == -1 {
|
f := &fields[k]
|
||||||
pid := st.Peek()
|
f.ID = int32(k)
|
||||||
f.ParentID = pid
|
|
||||||
if f.ParentID != -1 {
|
// If this is the top-level point the parent to the parent of the
|
||||||
fields[pid].Children = append(fields[f.ParentID].Children, f.ID)
|
// previous field.
|
||||||
|
if f.ParentID == -1 {
|
||||||
|
f.ParentID = pid
|
||||||
|
|
||||||
|
if f.ParentID != -1 {
|
||||||
|
parent.Children = append(parent.Children, f.ID)
|
||||||
|
}
|
||||||
|
// Update all the other parents id's by our new place in this new array
|
||||||
|
} else {
|
||||||
|
f.ParentID += n
|
||||||
}
|
}
|
||||||
// Update all the other parents id's by our new place in this new array
|
|
||||||
} else {
|
|
||||||
f.ParentID += n
|
|
||||||
}
|
|
||||||
|
|
||||||
f.Children = make([]int32, len(f.Children))
|
// Copy over children since fields append is not a deep copy
|
||||||
copy(f.Children, fr.Fields[i].Children)
|
f.Children = make([]int32, len(f.Children))
|
||||||
|
copy(f.Children, ff[i].Children)
|
||||||
|
|
||||||
f.Args = make([]Arg, len(f.Args))
|
// Copy over args since args append is not a deep copy
|
||||||
copy(f.Args, fr.Fields[i].Args)
|
f.Args = make([]Arg, len(f.Args))
|
||||||
|
copy(f.Args, ff[i].Args)
|
||||||
|
|
||||||
// Update all the children which is needed.
|
// Update all the children which is needed.
|
||||||
for j := range f.Children {
|
for j := range f.Children {
|
||||||
f.Children[j] += n
|
f.Children[j] += n
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type QType int
|
type QType int
|
||||||
|
type SType int
|
||||||
type Action int
|
type Action int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -19,7 +20,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
QTQuery QType = iota + 1
|
QTUnknown QType = iota
|
||||||
|
QTQuery
|
||||||
QTMutation
|
QTMutation
|
||||||
QTInsert
|
QTInsert
|
||||||
QTUpdate
|
QTUpdate
|
||||||
|
@ -27,6 +29,12 @@ const (
|
||||||
QTUpsert
|
QTUpsert
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
STNone SType = iota
|
||||||
|
STUnion
|
||||||
|
STMember
|
||||||
|
)
|
||||||
|
|
||||||
type QCode struct {
|
type QCode struct {
|
||||||
Type QType
|
Type QType
|
||||||
ActionVar string
|
ActionVar string
|
||||||
|
@ -38,6 +46,8 @@ type QCode struct {
|
||||||
type Select struct {
|
type Select struct {
|
||||||
ID int32
|
ID int32
|
||||||
ParentID int32
|
ParentID int32
|
||||||
|
UParentID int32
|
||||||
|
Type SType
|
||||||
Args map[string]*Node
|
Args map[string]*Node
|
||||||
Name string
|
Name string
|
||||||
FieldName string
|
FieldName string
|
||||||
|
@ -372,6 +382,10 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||||
})
|
})
|
||||||
s := &selects[(len(selects) - 1)]
|
s := &selects[(len(selects) - 1)]
|
||||||
|
|
||||||
|
if field.Union {
|
||||||
|
s.Type = STUnion
|
||||||
|
}
|
||||||
|
|
||||||
if len(field.Alias) != 0 {
|
if len(field.Alias) != 0 {
|
||||||
s.FieldName = field.Alias
|
s.FieldName = field.Alias
|
||||||
} else {
|
} else {
|
||||||
|
@ -383,6 +397,11 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||||
} else {
|
} else {
|
||||||
p := &selects[s.ParentID]
|
p := &selects[s.ParentID]
|
||||||
p.Children = append(p.Children, s.ID)
|
p.Children = append(p.Children, s.ID)
|
||||||
|
|
||||||
|
if p.Type == STUnion {
|
||||||
|
s.Type = STMember
|
||||||
|
s.UParentID = p.ParentID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipRender {
|
if skipRender {
|
||||||
|
|
|
@ -29,6 +29,8 @@ func al(b byte) bool {
|
||||||
|
|
||||||
func (qt QType) String() string {
|
func (qt QType) String() string {
|
||||||
switch qt {
|
switch qt {
|
||||||
|
case QTUnknown:
|
||||||
|
return "unknown"
|
||||||
case QTQuery:
|
case QTQuery:
|
||||||
return "query"
|
return "query"
|
||||||
case QTMutation:
|
case QTMutation:
|
||||||
|
|
|
@ -88,6 +88,10 @@ func cmdNew(cmd *cobra.Command, args []string) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ifNotExists(path.Join(appConfigPath, "allow.list"), func(p string) error {
|
||||||
|
return ioutil.WriteFile(p, []byte{}, 0644)
|
||||||
|
})
|
||||||
|
|
||||||
// Create app migrations folder and add relevant files
|
// Create app migrations folder and add relevant files
|
||||||
|
|
||||||
appMigrationsPath := path.Join(appConfigPath, "migrations")
|
appMigrationsPath := path.Join(appConfigPath, "migrations")
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -134,7 +134,7 @@ database:
|
||||||
type: postgres
|
type: postgres
|
||||||
host: db
|
host: db
|
||||||
port: 5432
|
port: 5432
|
||||||
dbname: {{- .AppNameSlug -}}_development
|
dbname: {{ .AppNameSlug -}}_development
|
||||||
user: postgres
|
user: postgres
|
||||||
password: postgres
|
password: postgres
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ database:
|
||||||
type: postgres
|
type: postgres
|
||||||
host: db
|
host: db
|
||||||
port: 5432
|
port: 5432
|
||||||
dbname: {{- .AppNameSlug -}}_production
|
dbname: {{ .AppNameSlug -}}_production
|
||||||
user: postgres
|
user: postgres
|
||||||
password: postgres
|
password: postgres
|
||||||
#pool_size: 10
|
#pool_size: 10
|
||||||
|
|
Loading…
Reference in New Issue