Add fetch by ID feature

This commit is contained in:
Vikram Rangnekar
2019-04-04 00:53:24 -04:00
parent 813540f257
commit 33c5ef0bf1
12 changed files with 138 additions and 482 deletions

View File

@ -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 {

View File

@ -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)

View File

@ -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"`