Add fetch by ID feature
This commit is contained in:
61
psql/psql.go
61
psql/psql.go
@ -263,7 +263,7 @@ func (v *selectBlock) renderJoinedColumns(w io.Writer, childIDs []int) error {
|
||||
func (v *selectBlock) renderBaseSelect(w io.Writer, schema *DBSchema, childCols []*qcode.Column, childIDs []int) error {
|
||||
var groupBy []int
|
||||
|
||||
isNotRoot := v.parent != nil
|
||||
isRoot := v.parent == nil
|
||||
isFil := v.sel.Where != nil
|
||||
isAgg := false
|
||||
|
||||
@ -305,23 +305,7 @@ func (v *selectBlock) renderBaseSelect(w io.Writer, schema *DBSchema, childCols
|
||||
|
||||
fmt.Fprintf(w, ` FROM "%s"`, v.sel.Table)
|
||||
|
||||
if isNotRoot {
|
||||
v.renderJoinTable(w, schema, childIDs)
|
||||
}
|
||||
|
||||
switch {
|
||||
case isNotRoot:
|
||||
io.WriteString(w, ` WHERE (`)
|
||||
v.renderRelationship(w, schema)
|
||||
if isFil {
|
||||
io.WriteString(w, ` AND `)
|
||||
if err := v.renderWhere(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
io.WriteString(w, `)`)
|
||||
|
||||
case isFil && !isAgg:
|
||||
if isRoot && isFil {
|
||||
io.WriteString(w, ` WHERE (`)
|
||||
if err := v.renderWhere(w); err != nil {
|
||||
return err
|
||||
@ -329,23 +313,32 @@ func (v *selectBlock) renderBaseSelect(w io.Writer, schema *DBSchema, childCols
|
||||
io.WriteString(w, `)`)
|
||||
}
|
||||
|
||||
if isAgg && len(groupBy) != 0 {
|
||||
fmt.Fprintf(w, ` GROUP BY `)
|
||||
if !isRoot {
|
||||
v.renderJoinTable(w, schema, childIDs)
|
||||
|
||||
for i, id := range groupBy {
|
||||
fmt.Fprintf(w, `"%s"."%s"`, v.sel.Table, v.sel.Cols[id].Name)
|
||||
|
||||
if i < len(groupBy)-1 {
|
||||
io.WriteString(w, ", ")
|
||||
}
|
||||
}
|
||||
io.WriteString(w, ` WHERE (`)
|
||||
v.renderRelationship(w, schema)
|
||||
|
||||
if isFil {
|
||||
io.WriteString(w, ` HAVING (`)
|
||||
io.WriteString(w, ` AND `)
|
||||
if err := v.renderWhere(w); err != nil {
|
||||
return err
|
||||
}
|
||||
io.WriteString(w, `)`)
|
||||
}
|
||||
io.WriteString(w, `)`)
|
||||
}
|
||||
|
||||
if isAgg {
|
||||
if len(groupBy) != 0 {
|
||||
fmt.Fprintf(w, ` GROUP BY `)
|
||||
|
||||
for i, id := range groupBy {
|
||||
fmt.Fprintf(w, `"%s"."%s"`, v.sel.Table, v.sel.Cols[id].Name)
|
||||
|
||||
if i < len(groupBy)-1 {
|
||||
io.WriteString(w, ", ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,6 +396,16 @@ func (v *selectBlock) renderRelationship(w io.Writer, schema *DBSchema) {
|
||||
}
|
||||
|
||||
func (v *selectBlock) renderWhere(w io.Writer) error {
|
||||
if v.sel.Where.Op == qcode.OpEqID {
|
||||
col, ok := v.schema.PCols[v.sel.Table]
|
||||
if !ok {
|
||||
return fmt.Errorf("no primary key defined for %s", v.sel.Table)
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, `(("%s") = ('%s'))`, col.Name, v.sel.Where.Val)
|
||||
return nil
|
||||
}
|
||||
|
||||
st := util.NewStack()
|
||||
|
||||
if v.sel.Where != nil {
|
||||
|
@ -317,6 +317,26 @@ func manyToManyReverse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func fetchByID(t *testing.T) {
|
||||
gql := `query {
|
||||
product(id: 4) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}`
|
||||
|
||||
sql := `SELECT json_object_agg('product', products) FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name") AS "sel_0")) AS "products" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE ((("id") = ('4'))) LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "done_1337";`
|
||||
|
||||
resSQL, err := compileGQLToPSQL(gql)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if resSQL != sql {
|
||||
t.Fatal(errNotExpected)
|
||||
}
|
||||
}
|
||||
|
||||
func aggFunction(t *testing.T) {
|
||||
gql := `query {
|
||||
products {
|
||||
@ -345,7 +365,7 @@ func aggFunctionWithFilter(t *testing.T) {
|
||||
}
|
||||
}`
|
||||
|
||||
sql := `SELECT json_object_agg('products', products) FROM (SELECT coalesce(json_agg("products"), '[]') AS "products" FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."max_price" AS "max_price") AS "sel_0")) AS "products" FROM (SELECT "products"."id", max("products"."price") AS max_price FROM "products" GROUP BY "products"."id" HAVING ((("products"."id") > (10))) LIMIT ('20') :: integer) AS "products_0" LIMIT ('20') :: integer) AS "products_0") AS "done_1337";`
|
||||
sql := `SELECT json_object_agg('products', products) FROM (SELECT coalesce(json_agg("products"), '[]') AS "products" FROM (SELECT row_to_json((SELECT "sel_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."max_price" AS "max_price") AS "sel_0")) AS "products" FROM (SELECT "products"."id", max("products"."price") AS max_price FROM "products" WHERE ((("products"."id") > (10))) GROUP BY "products"."id" LIMIT ('20') :: integer) AS "products_0" LIMIT ('20') :: integer) AS "products_0") AS "done_1337";`
|
||||
|
||||
resSQL, err := compileGQLToPSQL(gql)
|
||||
if err != nil {
|
||||
@ -362,6 +382,7 @@ func TestCompileGQL(t *testing.T) {
|
||||
t.Run("withWhereAndList", withWhereAndList)
|
||||
t.Run("withWhereIsNull", withWhereIsNull)
|
||||
t.Run("withWhereMultiOr", withWhereMultiOr)
|
||||
t.Run("fetchByID", fetchByID)
|
||||
t.Run("belongsTo", belongsTo)
|
||||
t.Run("oneToMany", oneToMany)
|
||||
t.Run("manyToMany", manyToMany)
|
||||
|
@ -118,34 +118,42 @@ func updateSchema(schema *DBSchema, t *DBTable, cols []*DBColumn) {
|
||||
// Below one-to-many relations use the current table as the
|
||||
// join table aka through table.
|
||||
if len(jcols) > 1 {
|
||||
col1, col2 := jcols[0], jcols[1]
|
||||
|
||||
t1 := strings.ToLower(col1.FKeyTable)
|
||||
t2 := strings.ToLower(col2.FKeyTable)
|
||||
|
||||
fc1, ok := schema.ColIDMap[col1.FKeyColID[0]]
|
||||
if !ok {
|
||||
return
|
||||
for i := range jcols {
|
||||
for n := range jcols {
|
||||
if n != i {
|
||||
updateSchemaOTMT(schema, ct, jcols[i], jcols[n])
|
||||
}
|
||||
}
|
||||
}
|
||||
fc2, ok := schema.ColIDMap[col2.FKeyColID[0]]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// One-to-many-through relation between 1nd foreign key table and the
|
||||
// 2nd foreign key table
|
||||
//rel1 := &DBRel{RelOneToManyThrough, ct, fc1.Name, col1.Name}
|
||||
rel1 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name, col1.Name}
|
||||
schema.RelMap[TTKey{t1, t2}] = rel1
|
||||
|
||||
// One-to-many-through relation between 2nd foreign key table and the
|
||||
// 1nd foreign key table
|
||||
//rel2 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name}
|
||||
rel2 := &DBRel{RelOneToManyThrough, ct, col1.Name, fc1.Name, col2.Name}
|
||||
schema.RelMap[TTKey{t2, t1}] = rel2
|
||||
}
|
||||
}
|
||||
|
||||
func updateSchemaOTMT(schema *DBSchema, ct string, col1, col2 *DBColumn) {
|
||||
t1 := strings.ToLower(col1.FKeyTable)
|
||||
t2 := strings.ToLower(col2.FKeyTable)
|
||||
|
||||
fc1, ok := schema.ColIDMap[col1.FKeyColID[0]]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
fc2, ok := schema.ColIDMap[col2.FKeyColID[0]]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// One-to-many-through relation between 1nd foreign key table and the
|
||||
// 2nd foreign key table
|
||||
//rel1 := &DBRel{RelOneToManyThrough, ct, fc1.Name, col1.Name}
|
||||
rel1 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name, col1.Name}
|
||||
schema.RelMap[TTKey{t1, t2}] = rel1
|
||||
|
||||
// One-to-many-through relation between 2nd foreign key table and the
|
||||
// 1nd foreign key table
|
||||
//rel2 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name}
|
||||
rel2 := &DBRel{RelOneToManyThrough, ct, col1.Name, fc1.Name, col2.Name}
|
||||
schema.RelMap[TTKey{t2, t1}] = rel2
|
||||
}
|
||||
|
||||
type DBTable struct {
|
||||
Name string `sql:"name"`
|
||||
Type string `sql:"type"`
|
||||
|
Reference in New Issue
Block a user